Rev 3235 Rev 3255
Line -... Line 1...
-   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 }