0,0 → 1,497 |
// Melodicky zvonek MB01A_1_00 |
// |
// Prohram pro melodicky zvonek s pouzitim knihovny Sound_t1.C |
// |
// (c)miho 2004 |
// |
// Historie: |
// 1.00 Uvodni verze |
|
#include <16F819.h> // Definice procesoru |
#fuses HS,NOPROTECT,NOWDT,PUT,NOLVP,NOBROWNOUT,WRT // Definice konfiguracniho slova |
|
#define POWER_386 PIN_A4 // L zapne napajeni pro zesilovac LM386 |
#define POWER_PIC PIN_A3 // L pripoji GND pro procesor PIC |
|
#define SOUND_HI PIN_B3 // Akusticky vystup |
#define SOUND_LO PIN_B2 // Akusticky vystup |
#define SOUND_CLOCK 20000000 // Frekvence hodin |
#define SOUND_LowOctave 1 // O oktavu vys protoze mame moc rychly krystal |
#include "Sound_T1.c" // Hudebni knihovna |
|
#include "Data.c" // Datovy blok pro predpripravene skladby |
|
#define RXD PIN_B1 // Port pro RS232 |
#use delay(CLOCK=20000000) // Konfigurace pro casovani RS232 |
#use rs232(BAUD=9600,RCV=RXD,INVERT,PARITY=N,BITS=8) // Prenosove parametry RS232 |
|
|
// Sada globalnich promennych |
// -------------------------- |
// Vyuzivame globalni promenne protoze je to vyhodne z hlediska spotreby zdroju. Usetri |
// se vyznmane mnoho pameti programu pri predavani parametru |
|
|
unsigned int16 Adr; // Adresovy ukazatel do pameti skladeb |
unsigned int16 Data; // Prectena data nebo data pro zapis |
unsigned int1 Mode; // 0=rezim testovani, 1=rezim zapisu do FLASH |
|
unsigned int16 Tempo; // Tempo (delka nejkratsi skladby v ms) |
unsigned int16 Pause; // Delka mezery mezi notami v ms |
unsigned int8 Octava; // Posunuti skladby v oktavach |
|
unsigned int8 Beep; // Druh pipnuti pro proceduru SoundSpec |
unsigned int1 Error; // Priznak chyby |
|
unsigned int8 CisloSkladby; // Cislo skladby pro proceduru Find a Play |
|
|
// Zvuky, posloupnost zadaneho poctu tonu |
#define SoundEndOfLine 0x01 // Kratke pipnuti na konci radky |
#define SoundPGM 0x03 // Trilek pri vstupu do rezimu programovani |
#define SoundPostPlay 0x03 // Po ukonceni prehravani |
#define SoundERASE 0x02 // Zvuk pri smazani bloku FLASH pameti |
#define SoundERR 0x05 // Chyba syntaxe |
void SpecBeep() |
// Data - pocet pipnuti, 0 znamena ticho |
{ |
int Oct; |
|
if (Error) Beep=SoundERR; |
|
for(Oct=2;Beep!=0;Beep--) |
{ |
SoundNote(SOUND_A,Oct++,50); |
} |
|
Error=0; |
} |
|
|
// Precti slovo z pameti programu |
int16 ReadData() |
// Adr - adresa ze ktere se cte |
// Data - prectena data |
{ |
int8 a,b; // Pomocne promenne |
|
(int8)*EEADR = Adr; // Adresa, spodni cast |
(int8)*EEADRH = Adr >> 8; // Adresa, horni cast |
bit_set(*EECON1,EECON1_EEPGD); // Pamet programu |
bit_set(*EECON1,EECON1_RD); // Operace cteni |
#ASM |
nop; // Povinne nop |
nop; |
#ENDASM |
a = (int8)*EEDATA; // Prevezmi data |
b = (int8)*EEDATAH; |
Data=make16(b,a); // Sestav vysledek ze 2 bajtu |
} |
|
|
// Smazani cele pameti vyhrazene pro skladby |
void Erase() |
{ |
for(Adr=STARTMEM; Adr<=ENDMEM; Adr++) // Cela oblast |
{ |
ReadData(); |
if (Data!=0x3FFF) // Mazu jen bloky, ktere to potrebuji |
{ |
if (input(POWER_PIC)!=0) return; // Nezapisuj pokud neni jumper povoleni programovani |
(int8)*EEADR = Adr; // Adresa bloku, spodni cast |
(int8)*EEADRH = Adr >> 8; // Adresa bloku, horni cast |
bit_set(*EECON1,EECON1_EEPGD); // Pamet programu |
bit_set(*EECON1,EECON1_WREN); // Povolit zapis |
bit_set(*EECON1,EECON1_FREE); // Operace mazani |
(int8)*EECON2 = 0x55; // Povinna segvence pro zapis |
(int8)*EECON2 = 0xAA; |
bit_set(*EECON1,EECON1_WR); // Zahajeni mazani |
#ASM |
nop; // Povinne prazdne instrukce |
nop; |
#ENDASM |
bit_clear(*EECON1,EECON1_WREN); // Uz ne zapis |
bit_clear(*EECON1,EECON1_FREE); // Uz ne mazani |
bit_clear(*EECON1,EECON1_EEPGD); // Uz ne pamet programu |
Beep=SoundERASE; |
SpecBeep(); |
} |
} |
} |
|
|
// Zapis do pameti programu po jednotlivych slovech. |
// Soucastka vyzaduje zapis vzdy celych osmi slov najednou. Vyuziva se toho, ze pamet FLASH |
// umi zapisovat jen smerem do nuly a tak staci na pozice, ktere zrovna nechceme programovat |
// zapsat same jednicky cimz se stav jejich stav nezmeni. |
void WriteData() |
// Adr - adresa, kam se bude zapisovat |
// Data - data, ktera se budou zapisovat |
{ |
int i; |
|
bit_set(*EECON1,EECON1_EEPGD); // Pamet programu |
bit_set(*EECON1,EECON1_WREN); // Zapis |
(int8)*EEADR = Adr & ~3; // Adresa, spodni cast, zaokrouhleno dolu na nasobek 8 |
(int8)*EEADRH = Adr >> 8; // Adresa, horni cast |
for (i=0; i<4; i++) |
{ |
if ((Adr & 3) == i) // Pokud je adresa slova v bloku totozna s pozadovanou |
{ |
(int8)*EEDATA =Data; // Platne slovo, spodni cast |
(int8)*EEDATAH=Data >> 8; // Platne slovo, horni cast |
} |
else // Ostatni bunky nemenime |
{ |
(int8)*EEDATA =0xFF; // Zbytek same jednicky |
(int8)*EEDATAH=0xFF; |
} |
(int8)*EECON2 = 0x55; // Povinna sekvence pro zapis |
(int8)*EECON2 = 0xAA; |
bit_set(*EECON1,EECON1_WR); // Zahajeni zapisu |
#ASM |
nop; // Povinne dve prazdne instrukce |
nop; |
#ENDASM |
((int8)*EEADR) ++; // Dalsi slovo |
} |
bit_clear(*EECON1,EECON1_WREN); // Konec zapisu |
bit_clear(*EECON1,EECON1_EEPGD); // Uz ne pamet programu (bezpecnost) |
} |
|
|
// Zapise data Data na adresu Adr a provede posun na dalsi adresu, zapisuje se jen do |
// dovolene oblasti pameti a jen v pripade, ze je Mode=1 |
void WriteDataInc() |
// Data - co se zapisuje |
// Adr - kam se zapisuje, po zapisu se adresa posouva |
{ |
if (~Mode) return; // Neni rezim zapisu |
if ( (Adr>=STARTMEM) & (Adr<=ENDMEM) & (input(POWER_PIC)==0) ) |
{ |
WriteData(); |
Adr++; |
} |
else |
{ |
Error=1; |
} |
} |
|
|
// Najdi zacatek zadaneho cisla skladby. Promenna Adr ukazuje na zacatek skladby. |
// Neni-li skladba nalezena ukazuje Adr na koncovou znacku (0x3FFF) posledni skladby |
// nebo na konec pameti. |
int1 Find() |
// CisloSkladby - poradove cislo skladby |
// Adr - adresa zacatku skladby |
{ |
Adr=STARTMEM-1; // Od zacatku oblasti pro skladby |
for(;1;) |
{ |
Adr++; |
ReadData(); // Precti data |
if (Data==ENDOFDATA) return 1; // Priznak konce dat |
if (Adr==ENDMEM+1) return 1; // Uz jsme prosli celou pamet |
if ((Data&MASKBEGIN)==DATABEGIN) // Priznak zacatku skladby |
{ |
CisloSkladby--; // Otestuj pocitadlo skladeb |
if (CisloSkladby==0) return 0; // Je to tato skladba |
} |
} |
} |
|
|
// Zahraj jednu notu |
void PlayData() |
// Data = zakodovana nota |
// Tempo, Octava, Pause = parametry hrane noty |
{ |
SoundNote((int8)Data&0xF,Octava+(((int8)Data>>4)&0x7),Tempo*((Data>>7)&0x3F)); // Zahraj notu |
SoundNote(SOUND_Space,0,Pause); // Zahraj mezeru mezi notami |
} |
|
|
// Zahraj skladbu od zadane adresy v promenne Adr. |
void Play() |
// CisloSkladby - cislo skladby k hrani |
{ |
if (Find()) // Najdi zacatek skladby v pameti skladeb |
{ |
return; // Skladba nenalezena |
} |
|
Tempo=100; // Default delka noty |
Pause=100; // Default mezera mezi notami |
|
Octava=Data&~MASKBEGIN; // Posunuti oktav (povinna soucast zacatku skladby) |
Adr++; |
|
for (;1;) |
{ |
if (Adr==ENDMEM+1) return; // Konec pametove oblasti |
ReadData(); // Vezmi data |
Adr++; // Posun adresu |
if (Data==ENDOFDATA) return; // Konec dat |
if ((Data&MASKBEGIN)==DATABEGIN) return; // Zacatek dalsi skladby |
if ((Data&MASKTEMPO)==DATATEMPO) Tempo=Data&~DATATEMPO; // Paramter TEMPO |
if ((Data&MASKPAUSE)==DATAPAUSE) Pause=Data&~DATAPAUSE; // Parametr PAUSE |
if ((Data&MASKNOTE)==0) // Nota |
{ |
PlayData(); // Zahraj notu |
} |
} |
} |
|
|
// Vycisli cislo z bufferu, posune ukazovatko na prvni nezpracovany znak, preskakuje mezery |
int16 Number(char line[], int *a, len) |
{ |
int16 Data; |
char c; |
|
while((line[*a]==' ')&(*a<len)) // Vynech mezery na zacatku |
(*a)++; // Posouvej ukazovatko |
|
Data=0; |
while (1) |
{ |
if (*a>=len) return Data; // Konec retezce |
c=line[*a]; // Vezmi znak z pole |
if ((c<'0')|(c>'9')) return Data; // Koncime pokud znak neni cislice |
Data = Data * 10 + (c-'0'); // Pouzij cislici |
(*a)++; // Dalsi znak |
} |
} |
|
|
// Vyhledej klicove slovo a vrat jeho zkraceny kod |
// Pokud slovo neexistuje vraci -1 |
// Format definice - retezec ukonceny nulou + zastupny znak, na konci prazdny retezec (nula) |
const char KeyWords[] = |
{ |
'P','L','A','Y',0, 'P', |
'E','R','A','S','E',0, 'E', |
'T','E','M','P','O',0, 't', |
'P','A','U','S','E',0, 'p', |
'B','E','G','I','N',0, 'B', |
'T','E','S','T',0, 'b', |
'E','N','D',0, 'Z', |
'C',0. SOUND_C, |
'C','I','S',0, SOUND_Cis, |
'D',0, SOUND_D, |
'D','I','S',0, SOUND_Dis, |
'E',0, SOUND_E, |
'F',0, SOUND_F, |
'F','I','S',0, SOUND_Fis, |
'G',0, SOUND_G, |
'G','I','S',0, SOUND_Gis, |
'A',0, SOUND_A, |
'A','I','S',0, SOUND_Ais, |
'H',0, SOUND_H, |
'S','P','A','C','E',0, SOUND_Space |
}; |
signed int Word(char line[], unsigned int8 *a, len) |
{ |
unsigned int8 i; // Index do pole klicovych slov |
unsigned int8 j; // index do zpracovavane radky |
|
while((line[*a]==' ')&(*a<len)) // Vynech mezery na zacatku |
(*a)++; // Posouvej ukazovatko |
|
for (i=0;i<sizeof(KeyWords);) // Slova ze slovniku |
{ |
for (j=*a;(j<len)&(KeyWords[i]!=0)&(KeyWords[i]==line[j]);i++,j++) // Znaky ze slova |
{ |
} |
if ((KeyWords[i]==0)&((line[j]==' ')|(j==len))) |
{ |
if (j>=len) j=len-1; // Korekce abychom se nedostali za konec retezce |
*a=j+1; // Posun ukazovatko za zpracovane slovo |
|
return KeyWords[i+1]; // Vrat zastupnou hodnotu z tabulky klicovych slov |
} |
while(KeyWords[i]!=0) i++; // Preskoc zbytek slova v tabulce |
i++; // Preskoc oddelovac |
i++; // Preskoc polozku se zastupnou hodnotou |
} |
return -1; // Prosli jsme cely slovnik a nedoslo ke shode |
} |
|
|
// Programovani pres RS232 |
#define LINELEN 40 // Delka radky pro RS232 |
#define CR 0x0D // Odradkovani |
#define BS 0x08 // Back Space |
void Download() |
{ |
char line[LINELEN]; // Buffer na radku |
unsigned char c; // Znak |
unsigned int8 a; // Ukazovatko do bufferu |
unsigned int8 len; // Delka retezce v radce |
unsigned int8 Oct; // Cislo oktavy u noty |
|
output_low(POWER_386); // Zapni napajeni zesilovace |
SoundNote(SOUND_Space,3,10); // Mezera |
Beep=SoundPGM; |
Error=0; |
SpecBeep(); // Pipni na znameni vstupu do programovani |
|
Tempo=100; // Default hodnoty |
Pause=100; |
Octava=0; |
Mode=0; // Mod hrani |
Oct=0; |
a=0; // Na zacatku je radka prazdna |
|
for(;input(POWER_PIC)==0;) // Opakuj vse dokud je PGM rezim |
{ |
Loop: |
c=Getc(); // Vezmi znak ze seriovky |
if (c>=0x80) goto Loop; // Ignoruj znaky nad ASCII |
if (c>=0x60) c=c-0x20; // Preved velka pismena na velka pismena |
if ((c==CR)|(c=='/')) // Konec radky nebo komentar |
{ |
while (c!=CR) c=Getc(); // Zpracuj znaky komentare |
len=a; // Zapamatuj si delku radky |
a=0; // Postav se na zacatek radky |
Beep=SoundEndOfLine; // Default zuk na konci radky |
// Zpracovani radky |
while(a<len) |
{ |
c=Word(line,&a,len); |
if (c==-1) // Nezname klicove slovo |
{ |
if (a<len) // Nejsme uz na konci radky ? |
{ |
if ((line[a]>='0')&(line[a]<='9')) // Stojime na cislici -> je to cislo |
{ |
// Oct=Number(line,&a,len)&0x7; // tohle nefunguje protoze je chyba v prekladaci |
Oct=Number(line,&a,len); // prekladac prepoklada, z W obsahuje spodni bajt vysledku |
Oct&=0x7; // ale k navratu pouziva RETLW 0 coz smaze W ! |
} |
else // Stojime na pismenu nebo oddelovaci |
{ |
if (line[a]!=' ') Error=1; // Neni to oddelovac - chyba |
a++; // Preskocim 1 znak (a kdyz to nepomuze dostanu se zase sem) |
} |
} |
} |
else if (c=='P') // Play |
{ |
CisloSkladby=Number(line,&a,len); |
Mode=0; |
Play(); |
Beep=SoundPGM; |
} |
else if (c=='E') // Erase |
{ |
Mode=0; |
Erase(); |
Beep=SoundPGM; |
} |
else if (c=='t') // Tempo |
{ |
Tempo=Number(line,&a,len)&~MASKTEMPO; |
if (Tempo==0) Tempo=100; |
Data=Tempo|DATATEMPO; |
WriteDataInc(); // Podmineny zapis do FLASH a posun na dalsi adresu |
} |
else if (c=='p') // Pause |
{ |
Pause=Number(line,&a,len)&~MASKPAUSE; |
if (Pause==0) Pause=100; |
Data=Pause|DATAPAUSE; |
WriteDataInc(); // Podmineny zapis do FLASH a posun na dalsi adresu |
} |
else if (c=='B') // Begin |
{ |
CisloSkladby=~0; // Neplatne cislo skladby |
Find(); // najde konec posledni skladby |
Octava=Number(line,&a,len)&~MASKBEGIN; |
Data=DATABEGIN|Octava; // Zacatecni znacka |
Mode=1; // Mod zapisu do FLASH pameti |
WriteDataInc(); // Podmineny zapis do FLASH a posun na dalsi adresu |
} |
else if (c=='b') // Test |
{ |
Octava=Number(line,&a,len)&~MASKBEGIN; |
Mode=0; |
} |
else if (c=='Z') // End |
{ |
Mode=0; |
} |
else // Nota |
{ |
Data=Number(line,&a,len); // Delka noty (nepovinna) |
Data&=0x3F; // Jen platny rozsah |
if (Data==0) Data++; // Je-li nulova delka - dej jednotkovou |
Data<<=7; // Delka |
Data|=c; // Nota |
Data|=(Oct<<4); // Oktava |
WriteDataInc(); // Podmineny zapis do FLASH a posun na dalsi adresu |
if (~Mode) |
{ |
PlayData(); // Zahraj notu |
Beep=0; // Po zahrani noty uz nepipej na konci radky |
} |
} |
} |
a=0; // Radka zpracovana, nuluj jeji delku |
SpecBeep(); // Pipni |
goto Loop; |
} |
if ((c==BS)&(a>0)) {a--;goto Loop;} // Smaz znak |
if ((c==',')|(c<=' ')) c=' '; // Vsechny ostatni ridici znaky i carka jsou oddelovac |
if (a<LINELEN) line[a++]=c; // Zapis znak do bufferu a posun ukazovatko |
} |
} |
|
|
// Tabulka pro prekodovani tlacitek |
const int8 KeyTranslate[16] = {0, 1, 2, 5, 3, 6, 8, 11, 4, 7, 9, 12, 10, 13, 14, 15}; |
|
void main() |
{ |
Start: |
|
// Inicializace |
port_b_pullups(TRUE); // Zapni pull-up odpory na portu B |
set_tris_a(0b00001000); // Nastav nepouzite vyvody jako vystupy |
set_tris_b(0b11110000); // 1 znamena vstup |
*0x9F = 6; // Vsechny vstupy jsou digitalni |
|
// Test na rezim programovani |
if ((input(POWER_PIC)==0)&(input(RXD)==0)) // Podminka programovani |
Download(); // Pripojen RS232 a propojka na PGM |
|
// Zapnuti napajeni |
output_low(POWER_PIC); // Pripoj GND pro procesor |
SoundNote(SOUND_Space,3,10); // Mezera |
output_low(POWER_386); // Zapni napajeni zesilovace |
SoundNote(SOUND_Space,3,100); // Mezera (jinak se chybne detekuje tlacitko) |
|
// Cteni tlacitek |
#use FAST_IO(B) |
CisloSkladby=(input_b() >> 4) ^ 0xFF & 0xF; // Precti stav tlacitek |
#use STANDARD_IO(B) |
CisloSkladby=KeyTranslate[CisloSkladby]; // Prekoduj je do binarniho kodu |
|
// Prehrani skladby |
Play(); // Zahraj skladbu |
|
// Po odehrani usni a cekej na zmacknuti tlacitka |
#use FAST_IO(B) |
if (input_B()); // Precti stav portu B (vuci tomuto stavu se bude hlidat zmena) |
#use STANDARD_IO(B) |
bit_clear(*0xB,0); // Nuluj priznak preruseni od zmeny stavu portu B |
enable_interrupts(INT_RB); // Povol preruseni od zmeny stavu portu B |
|
output_high(POWER_386); // Vypni napajeni zesilovace |
output_float(POWER_PIC); // Odpoj GND |
Sleep(); // Usni aby byla minimalni klidova spotreba |
|
disable_interrupts(INT_RB); // Zakaz preruseni od portu |
goto Start; // Po probuzeni skoc na zacatek |
} |