// 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 parametruunsigned int16 Adr; // Adresovy ukazatel do pameti skladebunsigned int16 Data; // Prectena data nebo data pro zapisunsigned int1 Mode; // 0=rezim testovani, 1=rezim zapisu do FLASHunsigned int16 Tempo; // Tempo (delka nejkratsi skladby v ms)unsigned int16 Pause; // Delka mezery mezi notami v msunsigned int8 Octava; // Posunuti skladby v oktavachunsigned int8 Beep; // Druh pipnuti pro proceduru SoundSpecunsigned int1 Error; // Priznak chybyunsigned 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 syntaxevoid 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 programuint16 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 castbit_set(*EECON1,EECON1_EEPGD); // Pamet programubit_set(*EECON1,EECON1_RD); // Operace cteni#ASMnop; // Povinne nopnop;#ENDASMa = (int8)*EEDATA; // Prevezmi datab = (int8)*EEDATAH;Data=make16(b,a); // Sestav vysledek ze 2 bajtu}// Smazani cele pameti vyhrazene pro skladbyvoid 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 castbit_set(*EECON1,EECON1_EEPGD); // Pamet programubit_set(*EECON1,EECON1_WREN); // Povolit zapisbit_set(*EECON1,EECON1_FREE); // Operace mazani(int8)*EECON2 = 0x55; // Povinna segvence pro zapis(int8)*EECON2 = 0xAA;bit_set(*EECON1,EECON1_WR); // Zahajeni mazani#ASMnop; // Povinne prazdne instrukcenop;#ENDASMbit_clear(*EECON1,EECON1_WREN); // Uz ne zapisbit_clear(*EECON1,EECON1_FREE); // Uz ne mazanibit_clear(*EECON1,EECON1_EEPGD); // Uz ne pamet programuBeep=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 programubit_set(*EECON1,EECON1_WREN); // Zapis(int8)*EEADR = Adr & ~3; // Adresa, spodni cast, zaokrouhleno dolu na nasobek 8(int8)*EEADRH = Adr >> 8; // Adresa, horni castfor (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#ASMnop; // Povinne dve prazdne instrukcenop;#ENDASM((int8)*EEADR) ++; // Dalsi slovo}bit_clear(*EECON1,EECON1_WREN); // Konec zapisubit_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=1void WriteDataInc()// Data - co se zapisuje// Adr - kam se zapisuje, po zapisu se adresa posouva{if (~Mode) return; // Neni rezim zapisuif ( (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 skladbyfor(;1;){Adr++;ReadData(); // Precti dataif (Data==ENDOFDATA) return 1; // Priznak konce datif (Adr==ENDMEM+1) return 1; // Uz jsme prosli celou pametif ((Data&MASKBEGIN)==DATABEGIN) // Priznak zacatku skladby{CisloSkladby--; // Otestuj pocitadlo skladebif (CisloSkladby==0) return 0; // Je to tato skladba}}}// Zahraj jednu notuvoid PlayData()// Data = zakodovana nota// Tempo, Octava, Pause = parametry hrane noty{SoundNote((int8)Data&0xF,Octava+(((int8)Data>>4)&0x7),Tempo*((Data>>7)&0x3F)); // Zahraj notuSoundNote(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 notyPause=100; // Default mezera mezi notamiOctava=Data&~MASKBEGIN; // Posunuti oktav (povinna soucast zacatku skladby)Adr++;for (;1;){if (Adr==ENDMEM+1) return; // Konec pametove oblastiReadData(); // Vezmi dataAdr++; // Posun adresuif (Data==ENDOFDATA) return; // Konec datif ((Data&MASKBEGIN)==DATABEGIN) return; // Zacatek dalsi skladbyif ((Data&MASKTEMPO)==DATATEMPO) Tempo=Data&~DATATEMPO; // Paramter TEMPOif ((Data&MASKPAUSE)==DATAPAUSE) Pause=Data&~DATAPAUSE; // Parametr PAUSEif ((Data&MASKNOTE)==0) // Nota{PlayData(); // Zahraj notu}}}// Vycisli cislo z bufferu, posune ukazovatko na prvni nezpracovany znak, preskakuje mezeryint16 Number(char line[], int *a, len){int16 Data;char c;while((line[*a]==' ')&(*a<len)) // Vynech mezery na zacatku(*a)++; // Posouvej ukazovatkoData=0;while (1){if (*a>=len) return Data; // Konec retezcec=line[*a]; // Vezmi znak z poleif ((c<'0')|(c>'9')) return Data; // Koncime pokud znak neni cisliceData = 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 slovunsigned int8 j; // index do zpracovavane radkywhile((line[*a]==' ')&(*a<len)) // Vynech mezery na zacatku(*a)++; // Posouvej ukazovatkofor (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 slovoreturn KeyWords[i+1]; // Vrat zastupnou hodnotu z tabulky klicovych slov}while(KeyWords[i]!=0) i++; // Preskoc zbytek slova v tabulcei++; // Preskoc oddelovaci++; // 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 Spacevoid Download(){char line[LINELEN]; // Buffer na radkuunsigned char c; // Znakunsigned int8 a; // Ukazovatko do bufferuunsigned int8 len; // Delka retezce v radceunsigned int8 Oct; // Cislo oktavy u notyoutput_low(POWER_386); // Zapni napajeni zesilovaceSoundNote(SOUND_Space,3,10); // MezeraBeep=SoundPGM;Error=0;SpecBeep(); // Pipni na znameni vstupu do programovaniTempo=100; // Default hodnotyPause=100;Octava=0;Mode=0; // Mod hraniOct=0;a=0; // Na zacatku je radka prazdnafor(;input(POWER_PIC)==0;) // Opakuj vse dokud je PGM rezim{Loop:c=Getc(); // Vezmi znak ze seriovkyif (c>=0x80) goto Loop; // Ignoruj znaky nad ASCIIif (c>=0x60) c=c-0x20; // Preved velka pismena na velka pismenaif ((c==CR)|(c=='/')) // Konec radky nebo komentar{while (c!=CR) c=Getc(); // Zpracuj znaky komentarelen=a; // Zapamatuj si delku radkya=0; // Postav se na zacatek radkyBeep=SoundEndOfLine; // Default zuk na konci radky// Zpracovani radkywhile(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 prekladaciOct=Number(line,&a,len); // prekladac prepoklada, z W obsahuje spodni bajt vysledkuOct&=0x7; // ale k navratu pouziva RETLW 0 coz smaze W !}else // Stojime na pismenu nebo oddelovaci{if (line[a]!=' ') Error=1; // Neni to oddelovac - chybaa++; // 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 skladbyFind(); // najde konec posledni skladbyOctava=Number(line,&a,len)&~MASKBEGIN;Data=DATABEGIN|Octava; // Zacatecni znackaMode=1; // Mod zapisu do FLASH pametiWriteDataInc(); // 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 rozsahif (Data==0) Data++; // Je-li nulova delka - dej jednotkovouData<<=7; // DelkaData|=c; // NotaData|=(Oct<<4); // OktavaWriteDataInc(); // Podmineny zapis do FLASH a posun na dalsi adresuif (~Mode){PlayData(); // Zahraj notuBeep=0; // Po zahrani noty uz nepipej na konci radky}}}a=0; // Radka zpracovana, nuluj jeji delkuSpecBeep(); // Pipnigoto Loop;}if ((c==BS)&(a>0)) {a--;goto Loop;} // Smaz znakif ((c==',')|(c<=' ')) c=' '; // Vsechny ostatni ridici znaky i carka jsou oddelovacif (a<LINELEN) line[a++]=c; // Zapis znak do bufferu a posun ukazovatko}}// Tabulka pro prekodovani tlacitekconst int8 KeyTranslate[16] = {0, 1, 2, 5, 3, 6, 8, 11, 4, 7, 9, 12, 10, 13, 14, 15};void main(){Start:// Inicializaceport_b_pullups(TRUE); // Zapni pull-up odpory na portu Bset_tris_a(0b00001000); // Nastav nepouzite vyvody jako vystupyset_tris_b(0b11110000); // 1 znamena vstup*0x9F = 6; // Vsechny vstupy jsou digitalni// Test na rezim programovaniif ((input(POWER_PIC)==0)&(input(RXD)==0)) // Podminka programovaniDownload(); // Pripojen RS232 a propojka na PGM// Zapnuti napajenioutput_low(POWER_PIC); // Pripoj GND pro procesorSoundNote(SOUND_Space,3,10); // Mezeraoutput_low(POWER_386); // Zapni napajeni zesilovaceSoundNote(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 skladbyPlay(); // 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 Benable_interrupts(INT_RB); // Povol preruseni od zmeny stavu portu Boutput_high(POWER_386); // Vypni napajeni zesilovaceoutput_float(POWER_PIC); // Odpoj GNDSleep(); // Usni aby byla minimalni klidova spotrebadisable_interrupts(INT_RB); // Zakaz preruseni od portugoto Start; // Po probuzeni skoc na zacatek}