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