Rev Author Line No. Line
193 miho 1 // Melodicky zvonek MB01A_1_00
2 //
3 // Prohram pro melodicky zvonek s pouzitim knihovny Sound_t1.C
4 //
5 // (c)miho 2004
6 //
7 // Historie:
8 // 1.00 Uvodni verze
9  
10 #include <16F819.h> // Definice procesoru
11 #fuses HS,NOPROTECT,NOWDT,PUT,NOLVP,NOBROWNOUT,WRT // Definice konfiguracniho slova
12  
13 #define POWER_386 PIN_A4 // L zapne napajeni pro zesilovac LM386
14 #define POWER_PIC PIN_A3 // L pripoji GND pro procesor PIC
15  
16 #define SOUND_HI PIN_B3 // Akusticky vystup
17 #define SOUND_LO PIN_B2 // Akusticky vystup
18 #define SOUND_CLOCK 20000000 // Frekvence hodin
19 #define SOUND_LowOctave 1 // O oktavu vys protoze mame moc rychly krystal
20 #include "Sound_T1.c" // Hudebni knihovna
21  
22 #include "Data.c" // Datovy blok pro predpripravene skladby
23  
24 #define RXD PIN_B1 // Port pro RS232
25 #use delay(CLOCK=20000000) // Konfigurace pro casovani RS232
26 #use rs232(BAUD=9600,RCV=RXD,INVERT,PARITY=N,BITS=8) // Prenosove parametry RS232
27  
28  
29 // Sada globalnich promennych
30 // --------------------------
31 // Vyuzivame globalni promenne protoze je to vyhodne z hlediska spotreby zdroju. Usetri
32 // se vyznmane mnoho pameti programu pri predavani parametru
33  
34  
35 unsigned int16 Adr; // Adresovy ukazatel do pameti skladeb
36 unsigned int16 Data; // Prectena data nebo data pro zapis
37 unsigned int1 Mode; // 0=rezim testovani, 1=rezim zapisu do FLASH
38  
39 unsigned int16 Tempo; // Tempo (delka nejkratsi skladby v ms)
40 unsigned int16 Pause; // Delka mezery mezi notami v ms
41 unsigned int8 Octava; // Posunuti skladby v oktavach
42  
43 unsigned int8 Beep; // Druh pipnuti pro proceduru SoundSpec
44 unsigned int1 Error; // Priznak chyby
45  
46 unsigned int8 CisloSkladby; // Cislo skladby pro proceduru Find a Play
47  
48  
49 // Zvuky, posloupnost zadaneho poctu tonu
50 #define SoundEndOfLine 0x01 // Kratke pipnuti na konci radky
51 #define SoundPGM 0x03 // Trilek pri vstupu do rezimu programovani
52 #define SoundPostPlay 0x03 // Po ukonceni prehravani
53 #define SoundERASE 0x02 // Zvuk pri smazani bloku FLASH pameti
54 #define SoundERR 0x05 // Chyba syntaxe
55 void SpecBeep()
56 // Data - pocet pipnuti, 0 znamena ticho
57 {
58 int Oct;
59  
60 if (Error) Beep=SoundERR;
61  
62 for(Oct=2;Beep!=0;Beep--)
63 {
64 SoundNote(SOUND_A,Oct++,50);
65 }
66  
67 Error=0;
68 }
69  
70  
71 // Precti slovo z pameti programu
72 int16 ReadData()
73 // Adr - adresa ze ktere se cte
74 // Data - prectena data
75 {
76 int8 a,b; // Pomocne promenne
77  
78 (int8)*EEADR = Adr; // Adresa, spodni cast
79 (int8)*EEADRH = Adr >> 8; // Adresa, horni cast
80 bit_set(*EECON1,EECON1_EEPGD); // Pamet programu
81 bit_set(*EECON1,EECON1_RD); // Operace cteni
82 #ASM
83 nop; // Povinne nop
84 nop;
85 #ENDASM
86 a = (int8)*EEDATA; // Prevezmi data
87 b = (int8)*EEDATAH;
88 Data=make16(b,a); // Sestav vysledek ze 2 bajtu
89 }
90  
91  
92 // Smazani cele pameti vyhrazene pro skladby
93 void Erase()
94 {
95 for(Adr=STARTMEM; Adr<=ENDMEM; Adr++) // Cela oblast
96 {
97 ReadData();
98 if (Data!=0x3FFF) // Mazu jen bloky, ktere to potrebuji
99 {
100 if (input(POWER_PIC)!=0) return; // Nezapisuj pokud neni jumper povoleni programovani
101 (int8)*EEADR = Adr; // Adresa bloku, spodni cast
102 (int8)*EEADRH = Adr >> 8; // Adresa bloku, horni cast
103 bit_set(*EECON1,EECON1_EEPGD); // Pamet programu
104 bit_set(*EECON1,EECON1_WREN); // Povolit zapis
105 bit_set(*EECON1,EECON1_FREE); // Operace mazani
106 (int8)*EECON2 = 0x55; // Povinna segvence pro zapis
107 (int8)*EECON2 = 0xAA;
108 bit_set(*EECON1,EECON1_WR); // Zahajeni mazani
109 #ASM
110 nop; // Povinne prazdne instrukce
111 nop;
112 #ENDASM
113 bit_clear(*EECON1,EECON1_WREN); // Uz ne zapis
114 bit_clear(*EECON1,EECON1_FREE); // Uz ne mazani
115 bit_clear(*EECON1,EECON1_EEPGD); // Uz ne pamet programu
116 Beep=SoundERASE;
117 SpecBeep();
118 }
119 }
120 }
121  
122  
123 // Zapis do pameti programu po jednotlivych slovech.
124 // Soucastka vyzaduje zapis vzdy celych osmi slov najednou. Vyuziva se toho, ze pamet FLASH
125 // umi zapisovat jen smerem do nuly a tak staci na pozice, ktere zrovna nechceme programovat
126 // zapsat same jednicky cimz se stav jejich stav nezmeni.
127 void WriteData()
128 // Adr - adresa, kam se bude zapisovat
129 // Data - data, ktera se budou zapisovat
130 {
131 int i;
132  
133 bit_set(*EECON1,EECON1_EEPGD); // Pamet programu
134 bit_set(*EECON1,EECON1_WREN); // Zapis
135 (int8)*EEADR = Adr & ~3; // Adresa, spodni cast, zaokrouhleno dolu na nasobek 8
136 (int8)*EEADRH = Adr >> 8; // Adresa, horni cast
137 for (i=0; i<4; i++)
138 {
139 if ((Adr & 3) == i) // Pokud je adresa slova v bloku totozna s pozadovanou
140 {
141 (int8)*EEDATA =Data; // Platne slovo, spodni cast
142 (int8)*EEDATAH=Data >> 8; // Platne slovo, horni cast
143 }
144 else // Ostatni bunky nemenime
145 {
146 (int8)*EEDATA =0xFF; // Zbytek same jednicky
147 (int8)*EEDATAH=0xFF;
148 }
149 (int8)*EECON2 = 0x55; // Povinna sekvence pro zapis
150 (int8)*EECON2 = 0xAA;
151 bit_set(*EECON1,EECON1_WR); // Zahajeni zapisu
152 #ASM
153 nop; // Povinne dve prazdne instrukce
154 nop;
155 #ENDASM
156 ((int8)*EEADR) ++; // Dalsi slovo
157 }
158 bit_clear(*EECON1,EECON1_WREN); // Konec zapisu
159 bit_clear(*EECON1,EECON1_EEPGD); // Uz ne pamet programu (bezpecnost)
160 }
161  
162  
163 // Zapise data Data na adresu Adr a provede posun na dalsi adresu, zapisuje se jen do
164 // dovolene oblasti pameti a jen v pripade, ze je Mode=1
165 void WriteDataInc()
166 // Data - co se zapisuje
167 // Adr - kam se zapisuje, po zapisu se adresa posouva
168 {
169 if (~Mode) return; // Neni rezim zapisu
170 if ( (Adr>=STARTMEM) & (Adr<=ENDMEM) & (input(POWER_PIC)==0) )
171 {
172 WriteData();
173 Adr++;
174 }
175 else
176 {
177 Error=1;
178 }
179 }
180  
181  
182 // Najdi zacatek zadaneho cisla skladby. Promenna Adr ukazuje na zacatek skladby.
183 // Neni-li skladba nalezena ukazuje Adr na koncovou znacku (0x3FFF) posledni skladby
184 // nebo na konec pameti.
185 int1 Find()
186 // CisloSkladby - poradove cislo skladby
187 // Adr - adresa zacatku skladby
188 {
189 Adr=STARTMEM-1; // Od zacatku oblasti pro skladby
190 for(;1;)
191 {
192 Adr++;
193 ReadData(); // Precti data
194 if (Data==ENDOFDATA) return 1; // Priznak konce dat
195 if (Adr==ENDMEM+1) return 1; // Uz jsme prosli celou pamet
196 if ((Data&MASKBEGIN)==DATABEGIN) // Priznak zacatku skladby
197 {
198 CisloSkladby--; // Otestuj pocitadlo skladeb
199 if (CisloSkladby==0) return 0; // Je to tato skladba
200 }
201 }
202 }
203  
204  
205 // Zahraj jednu notu
206 void PlayData()
207 // Data = zakodovana nota
208 // Tempo, Octava, Pause = parametry hrane noty
209 {
210 SoundNote((int8)Data&0xF,Octava+(((int8)Data>>4)&0x7),Tempo*((Data>>7)&0x3F)); // Zahraj notu
211 SoundNote(SOUND_Space,0,Pause); // Zahraj mezeru mezi notami
212 }
213  
214  
215 // Zahraj skladbu od zadane adresy v promenne Adr.
216 void Play()
217 // CisloSkladby - cislo skladby k hrani
218 {
219 if (Find()) // Najdi zacatek skladby v pameti skladeb
220 {
221 return; // Skladba nenalezena
222 }
223  
224 Tempo=100; // Default delka noty
225 Pause=100; // Default mezera mezi notami
226  
227 Octava=Data&~MASKBEGIN; // Posunuti oktav (povinna soucast zacatku skladby)
228 Adr++;
229  
230 for (;1;)
231 {
232 if (Adr==ENDMEM+1) return; // Konec pametove oblasti
233 ReadData(); // Vezmi data
234 Adr++; // Posun adresu
235 if (Data==ENDOFDATA) return; // Konec dat
236 if ((Data&MASKBEGIN)==DATABEGIN) return; // Zacatek dalsi skladby
237 if ((Data&MASKTEMPO)==DATATEMPO) Tempo=Data&~DATATEMPO; // Paramter TEMPO
238 if ((Data&MASKPAUSE)==DATAPAUSE) Pause=Data&~DATAPAUSE; // Parametr PAUSE
239 if ((Data&MASKNOTE)==0) // Nota
240 {
241 PlayData(); // Zahraj notu
242 }
243 }
244 }
245  
246  
247 // Vycisli cislo z bufferu, posune ukazovatko na prvni nezpracovany znak, preskakuje mezery
248 int16 Number(char line[], int *a, len)
249 {
250 int16 Data;
251 char c;
252  
253 while((line[*a]==' ')&(*a<len)) // Vynech mezery na zacatku
254 (*a)++; // Posouvej ukazovatko
255  
256 Data=0;
257 while (1)
258 {
259 if (*a>=len) return Data; // Konec retezce
260 c=line[*a]; // Vezmi znak z pole
261 if ((c<'0')|(c>'9')) return Data; // Koncime pokud znak neni cislice
262 Data = Data * 10 + (c-'0'); // Pouzij cislici
263 (*a)++; // Dalsi znak
264 }
265 }
266  
267  
268 // Vyhledej klicove slovo a vrat jeho zkraceny kod
269 // Pokud slovo neexistuje vraci -1
270 // Format definice - retezec ukonceny nulou + zastupny znak, na konci prazdny retezec (nula)
271 const char KeyWords[] =
272 {
273 'P','L','A','Y',0, 'P',
274 'E','R','A','S','E',0, 'E',
275 'T','E','M','P','O',0, 't',
276 'P','A','U','S','E',0, 'p',
277 'B','E','G','I','N',0, 'B',
278 'T','E','S','T',0, 'b',
279 'E','N','D',0, 'Z',
280 'C',0. SOUND_C,
281 'C','I','S',0, SOUND_Cis,
282 'D',0, SOUND_D,
283 'D','I','S',0, SOUND_Dis,
284 'E',0, SOUND_E,
285 'F',0, SOUND_F,
286 'F','I','S',0, SOUND_Fis,
287 'G',0, SOUND_G,
288 'G','I','S',0, SOUND_Gis,
289 'A',0, SOUND_A,
290 'A','I','S',0, SOUND_Ais,
291 'H',0, SOUND_H,
292 'S','P','A','C','E',0, SOUND_Space
293 };
294 signed int Word(char line[], unsigned int8 *a, len)
295 {
296 unsigned int8 i; // Index do pole klicovych slov
297 unsigned int8 j; // index do zpracovavane radky
298  
299 while((line[*a]==' ')&(*a<len)) // Vynech mezery na zacatku
300 (*a)++; // Posouvej ukazovatko
301  
302 for (i=0;i<sizeof(KeyWords);) // Slova ze slovniku
303 {
304 for (j=*a;(j<len)&(KeyWords[i]!=0)&(KeyWords[i]==line[j]);i++,j++) // Znaky ze slova
305 {
306 }
307 if ((KeyWords[i]==0)&((line[j]==' ')|(j==len)))
308 {
309 if (j>=len) j=len-1; // Korekce abychom se nedostali za konec retezce
310 *a=j+1; // Posun ukazovatko za zpracovane slovo
311  
312 return KeyWords[i+1]; // Vrat zastupnou hodnotu z tabulky klicovych slov
313 }
314 while(KeyWords[i]!=0) i++; // Preskoc zbytek slova v tabulce
315 i++; // Preskoc oddelovac
316 i++; // Preskoc polozku se zastupnou hodnotou
317 }
318 return -1; // Prosli jsme cely slovnik a nedoslo ke shode
319 }
320  
321  
322 // Programovani pres RS232
323 #define LINELEN 40 // Delka radky pro RS232
324 #define CR 0x0D // Odradkovani
325 #define BS 0x08 // Back Space
326 void Download()
327 {
328 char line[LINELEN]; // Buffer na radku
329 unsigned char c; // Znak
330 unsigned int8 a; // Ukazovatko do bufferu
331 unsigned int8 len; // Delka retezce v radce
332 unsigned int8 Oct; // Cislo oktavy u noty
333  
334 output_low(POWER_386); // Zapni napajeni zesilovace
335 SoundNote(SOUND_Space,3,10); // Mezera
336 Beep=SoundPGM;
337 Error=0;
338 SpecBeep(); // Pipni na znameni vstupu do programovani
339  
340 Tempo=100; // Default hodnoty
341 Pause=100;
342 Octava=0;
343 Mode=0; // Mod hrani
344 Oct=0;
345 a=0; // Na zacatku je radka prazdna
346  
347 for(;input(POWER_PIC)==0;) // Opakuj vse dokud je PGM rezim
348 {
349 Loop:
350 c=Getc(); // Vezmi znak ze seriovky
351 if (c>=0x80) goto Loop; // Ignoruj znaky nad ASCII
352 if (c>=0x60) c=c-0x20; // Preved velka pismena na velka pismena
353 if ((c==CR)|(c=='/')) // Konec radky nebo komentar
354 {
355 while (c!=CR) c=Getc(); // Zpracuj znaky komentare
356 len=a; // Zapamatuj si delku radky
357 a=0; // Postav se na zacatek radky
358 Beep=SoundEndOfLine; // Default zuk na konci radky
359 // Zpracovani radky
360 while(a<len)
361 {
362 c=Word(line,&a,len);
363 if (c==-1) // Nezname klicove slovo
364 {
365 if (a<len) // Nejsme uz na konci radky ?
366 {
367 if ((line[a]>='0')&(line[a]<='9')) // Stojime na cislici -> je to cislo
368 {
369 // Oct=Number(line,&a,len)&0x7; // tohle nefunguje protoze je chyba v prekladaci
370 Oct=Number(line,&a,len); // prekladac prepoklada, z W obsahuje spodni bajt vysledku
371 Oct&=0x7; // ale k navratu pouziva RETLW 0 coz smaze W !
372 }
373 else // Stojime na pismenu nebo oddelovaci
374 {
375 if (line[a]!=' ') Error=1; // Neni to oddelovac - chyba
376 a++; // Preskocim 1 znak (a kdyz to nepomuze dostanu se zase sem)
377 }
378 }
379 }
380 else if (c=='P') // Play
381 {
382 CisloSkladby=Number(line,&a,len);
383 Mode=0;
384 Play();
385 Beep=SoundPGM;
386 }
387 else if (c=='E') // Erase
388 {
389 Mode=0;
390 Erase();
391 Beep=SoundPGM;
392 }
393 else if (c=='t') // Tempo
394 {
395 Tempo=Number(line,&a,len)&~MASKTEMPO;
396 if (Tempo==0) Tempo=100;
397 Data=Tempo|DATATEMPO;
398 WriteDataInc(); // Podmineny zapis do FLASH a posun na dalsi adresu
399 }
400 else if (c=='p') // Pause
401 {
402 Pause=Number(line,&a,len)&~MASKPAUSE;
403 if (Pause==0) Pause=100;
404 Data=Pause|DATAPAUSE;
405 WriteDataInc(); // Podmineny zapis do FLASH a posun na dalsi adresu
406 }
407 else if (c=='B') // Begin
408 {
409 CisloSkladby=~0; // Neplatne cislo skladby
410 Find(); // najde konec posledni skladby
411 Octava=Number(line,&a,len)&~MASKBEGIN;
412 Data=DATABEGIN|Octava; // Zacatecni znacka
413 Mode=1; // Mod zapisu do FLASH pameti
414 WriteDataInc(); // Podmineny zapis do FLASH a posun na dalsi adresu
415 }
416 else if (c=='b') // Test
417 {
418 Octava=Number(line,&a,len)&~MASKBEGIN;
419 Mode=0;
420 }
421 else if (c=='Z') // End
422 {
423 Mode=0;
424 }
425 else // Nota
426 {
427 Data=Number(line,&a,len); // Delka noty (nepovinna)
428 Data&=0x3F; // Jen platny rozsah
429 if (Data==0) Data++; // Je-li nulova delka - dej jednotkovou
430 Data<<=7; // Delka
431 Data|=c; // Nota
432 Data|=(Oct<<4); // Oktava
433 WriteDataInc(); // Podmineny zapis do FLASH a posun na dalsi adresu
434 if (~Mode)
435 {
436 PlayData(); // Zahraj notu
437 Beep=0; // Po zahrani noty uz nepipej na konci radky
438 }
439 }
440 }
441 a=0; // Radka zpracovana, nuluj jeji delku
442 SpecBeep(); // Pipni
443 goto Loop;
444 }
445 if ((c==BS)&(a>0)) {a--;goto Loop;} // Smaz znak
446 if ((c==',')|(c<=' ')) c=' '; // Vsechny ostatni ridici znaky i carka jsou oddelovac
447 if (a<LINELEN) line[a++]=c; // Zapis znak do bufferu a posun ukazovatko
448 }
449 }
450  
451  
452 // Tabulka pro prekodovani tlacitek
453 const int8 KeyTranslate[16] = {0, 1, 2, 5, 3, 6, 8, 11, 4, 7, 9, 12, 10, 13, 14, 15};
454  
455 void main()
456 {
457 Start:
458  
459 // Inicializace
460 port_b_pullups(TRUE); // Zapni pull-up odpory na portu B
461 set_tris_a(0b00001000); // Nastav nepouzite vyvody jako vystupy
462 set_tris_b(0b11110000); // 1 znamena vstup
463 *0x9F = 6; // Vsechny vstupy jsou digitalni
464  
465 // Test na rezim programovani
466 if ((input(POWER_PIC)==0)&(input(RXD)==0)) // Podminka programovani
467 Download(); // Pripojen RS232 a propojka na PGM
468  
469 // Zapnuti napajeni
470 output_low(POWER_PIC); // Pripoj GND pro procesor
471 SoundNote(SOUND_Space,3,10); // Mezera
472 output_low(POWER_386); // Zapni napajeni zesilovace
473 SoundNote(SOUND_Space,3,100); // Mezera (jinak se chybne detekuje tlacitko)
474  
475 // Cteni tlacitek
476 #use FAST_IO(B)
477 CisloSkladby=(input_b() >> 4) ^ 0xFF & 0xF; // Precti stav tlacitek
478 #use STANDARD_IO(B)
479 CisloSkladby=KeyTranslate[CisloSkladby]; // Prekoduj je do binarniho kodu
480  
481 // Prehrani skladby
482 Play(); // Zahraj skladbu
483  
484 // Po odehrani usni a cekej na zmacknuti tlacitka
485 #use FAST_IO(B)
486 if (input_B()); // Precti stav portu B (vuci tomuto stavu se bude hlidat zmena)
487 #use STANDARD_IO(B)
488 bit_clear(*0xB,0); // Nuluj priznak preruseni od zmeny stavu portu B
489 enable_interrupts(INT_RB); // Povol preruseni od zmeny stavu portu B
490  
491 output_high(POWER_386); // Vypni napajeni zesilovace
492 output_float(POWER_PIC); // Odpoj GND
493 Sleep(); // Usni aby byla minimalni klidova spotreba
494  
495 disable_interrupts(INT_RB); // Zakaz preruseni od portu
496 goto Start; // Po probuzeni skoc na zacatek
497 }