This is an old revision of the document!
Vajalikud teadmised:
[HW]Kontrollermoodul, [HW]Kasutajaliidese moodul
[AVR]Digitaalsed sisendid-väljundid, [AVR]Loendurid/Taimerid, [AVR]Digitaal-analoogmuundur, [AVR]Katkestused
[LIB]Heligeneraator
Taimerite üheks praktiliseks rakenduseks on heligeneraatori juhtimine. Heligeneraator on tihti kasutusel juhtpaneelides, alarmseadmetes ja mujal nupuvajutuste või signaalhelide kuuldavaks toomiseks. Sellist heligeneraatorit kutsutakse tihti lihtsalt kõlariks, piiksujaks, või siis buzzeriks, mis on üle võetud inglise keelest. Tööpõhimõttelt võib buzzerid jagada kaheks piesoelektrilisteks ja elektromagnetilisteks.
Piesoelektriline buzzer koosneb metallplaadist ja sellele kinnitatud piesokeraamilisest materjalist elemendist. Piesokeraamiline element muudab vastavalt pinge suurusele oma mõõtmeid. Selle tagajärjel metallplaat paindub. Rakendades buzzerile nelikantsignaali või vahelduvvoolu tekib võnkumine ehk heli.
Elektromagnetiline buzzer sarnaneb oma tööpõhimõttelt valjuhääldile. Ka siin on magnetväljas pool, mille kohale on paigutatud metallist membraan. Kui lasta helisageduslik vool läbi pooli, hakkab membraan tekkiva magnetvälja ja püsimagneti välja koosmõjul liikuma. Analoogselt piesoelektrilise buzzeriga tekib heli.
Üldiselt on piesoelektrilised buzzerid võrdluses elektromagnetilistega suurema helirõhu, väiksema voolutarbe ja kõrgema võimaliku helisagedusega. Kõige tugevama heli kõige väiksema voolutugevuse korral saab siis, kui buzzer töötab oma resonantssagedusel. Sellisel juhul on membraani amplituud kõige suurem.
Kodulabori kasutajaliidese plaadil asub koos lihtsa võimendiga kõlar, mis on ühendatud kontrolleri DAC1 kanaliga 1.
Kõlari kasutamiseks on Kodulabori teegis olemas funktsioon buzzer_sound, mis toob kuuldavale valitud pikkuse ja sagedusega helisignaali. Helisadegus andtakse ette hertsides ning kestvus millisekundites. Lisaks on võimalik käsuga buzzer_volume muuta heli valjusust vahemikus 0-4095, mis on realiseeritud DACi amplituudiga.
XMega kontrolleriga kenereeritakse tarkvaraline PWM, millega juhitakse DAC mooduli pinge väljundit 0V ja volüümiga määratud pingenivoo vahel.
// Taimer E0 ületäituvuse katkestus ISR(TCE0_OVF_vect) { static uint8_t CH1_Output = 0; if( CH1_Output == 1) { // Sea väljundpinge vastavalt volüümile 1 DAC_Channel_Write( &DACB, volume, CH1); CH1_Output = 0; } else { // Sea väljundpinge 0V DAC_Channel_Write( &DACB, 0, CH1); CH1_Output = 1; } } // // Genereeri heli sagedusega freq (8Hz - 500kHz) ja pikkusega length (ms) // kui length = 0, siis jäägi heli väljastama // void buzzer_sound (uint16_t freq, unsigned int length) { // DAC seadistamine // AVCC Pingereferentsiks DAC moodulile DACB.CTRLC = ( DACB.CTRLC & ~DAC_REFSEL_gm) | DAC_REFSEL_AVCC_gc; // Vali kanalid DACB.CTRLB = ( DACB.CTRLB & ~DAC_CHSEL_gm ) | DAC_CHSEL_DUAL_gc; // Käivita kanal 1 ning DACB moodul DACB.CTRLA = DAC_CH1EN_bm | DAC_ENABLE_bm; // Taimeri E0 seadistamine // Luba ületäituvuse katkestus keskmise prioriteediga TCE0.INTCTRLA = ( TCE0.INTCTRLA & ~TC0_OVFINTLVL_gm ) | TC_OVFINTLVL_MED_gc; // Sea taimeri E0 sageduseks 32MHz/64 = 500kHz TCE0.CTRLA = ( TCE0.CTRLA & ~TC0_CLKSEL_gm ) | TC_CLKSEL_DIV64_gc; // Luba keskmise taseme katkestused PMIC.CTRL |= PMIC_MEDLVLEN_bm; sei(); // Sea Taimeri tekitatav sagedus TCE0.PER = (uint32_t)250000/freq; // Kontrolli, kas on vaja taimer ka välja lülitada, ning vajadusel oota. if(length) { sw_delay_ms(length); TCE0.CTRLA = ( TCE0.CTRLA & ~TC0_CLKSEL_gm ) | TC_CLKSEL_OFF_gc; } }
Kodulabori kasutajaliidese plaadil asub piesoelektriline buzzer koos võimenduslülitusega. Heligeneraatorit juhitakse viiguga G5.
Heligeneraatori kasutamiseks on Kodulabori teegis olemas funktsioon buzzer_sound, mis toob kuuldavale valitud pikkuse ja sagedusega helisignaali. Heli sagedus antakse ette suhtelistes ühikutes 1-255. Heli kestvus on millisekundites.
// // Valitud sageduse ja pikkusega helisignaali genereerimine. // void buzzer_sound (unsigned char freq, unsigned int length) { // Seadista heligeneraatori viik pin buzzer = PIN(G,5); // Seadista heligeneraatori viik väljundiks pin_setup_output(buzzer); // Seadista Timer 0 kiire PWM režiimi koos OCR0A ja COM0B1 väljundiga TCCR0A |= (1 << COM0B1); TCCR0A |= ((1 << WGM01)|(1 << WGM00)); TCCR0B |= (1 << WGM02); OCR0A = freq; // Käivita Timer 0 koos preskaleriga 1024 TCCR0B |= ((1 << CS02)|(1 << CS00)); // Heli pikkus sw_delay_ms(length); // Peata taimer ehk heli genereerimine timer0_stop(); }
Järgmine näide illustreerib heligeneraatori teegi kasutamist. Kuuldavale tuuakse 500 millisekundi pikkune piiksatus.
#include <homelab/module/buzzer.h> int main (void) { buzzer_sound(50,500); }
Muusika mängimiseks, kasutades noodistikku, on vaja teada iga heli sagedust. Järgmine näide õpetab ilma buzzer.h teeki kasutamata keerulisemate muusikapalade loomist. Kasutusel on taimer 0, mille ületäitumise korral tekib katkestus. Katkestuse funktsioonis on muutuja intrs, mida suurendatakse iga katkestuse täitmise korral. Samal ajal võrreldakse seda muutujat noodi sagedusest arvutatud ületäitumiste arvuga. Kui muutuja intrs ja noodi ületäitumiste arv on saanud võrdseks, siis inverteeritakse heligeneraatori viigu väärtus. Tekib ristkülikukujuline signaal, mille sagedus on määratud noodi sagedusega.
Uute nootide defineerimine on näiteprogrammis lihtne. Analoogselt olemasolevatele nootidele tuleb, kasutades #define direktiivi, kirjeldada uue noodi nimi. Noodi nime järele tuleb lisada noodi sagedus hertsides. Sammuti tuleb lisada definitsioon, mitu taimeri ületäitumist leiab aset noodi kohta. Peale seda on uus noot kasutusvalmis.
Näiteprogramm mängib lühikese muusikapala peale nupu S1 vajutamist.
#include <homelab/pin.h> #include <homelab/delay.h> #include <homelab/timer2561.h> #include <avr/interrupt.h> // Timer0 ületäitumisi sekundis #define INT_PER_SEC 31250 // Nootide sagedused (Hz) #define F_FSH_4 370 #define F_A_4 440 #define F_B_4 494 #define F_E_4 330 #define F_CSH_5 554 #define F_D_5 587 #define F_FSH_5 740 #define F_CSH_4 277 #define F_GSH_4 415 // Timer0 ületäitumisi noodi kohta #define REST -1 // special case #define FSH_4 INT_PER_SEC/F_FSH_4 #define A_4 INT_PER_SEC/F_A_4 #define B_4 INT_PER_SEC/F_B_4 #define E_4 INT_PER_SEC/F_E_4 #define CSH_5 INT_PER_SEC/F_CSH_5 #define D_5 INT_PER_SEC/F_D_5 #define FSH_5 INT_PER_SEC/F_FSH_5 #define CSH_4 INT_PER_SEC/F_CSH_4 #define GSH_4 INT_PER_SEC/F_GSH_4 #define SEMIQUAVER_TIME 60 // ms #define BREATH_TIME 20 // ms volatile uint32_t intrs = 0; volatile int32_t curNote = REST; // Nupu viik pin button1 = PIN(C, 0); // Heligeneraatori viik pin buzzer = PIN(G, 5); // Taimer 0 katkestuse programmilõik ISR(TIMER0_OVF_vect) { if (curNote == REST) { intrs = 0; } else { intrs++; if (intrs >= curNote) { pin_toggle(buzzer); intrs = 0; } } } // Mängi nooti etteantud aeg void play(int32_t note, uint32_t len) { int i; curNote = note; for (i = 0; i< len; i++) { sw_delay_ms(SEMIQUAVER_TIME); } curNote = REST; sw_delay_ms(BREATH_TIME); } int main(void) { unsigned char new_value1, old_value1 = 0; // Seab nupu töökorda pin_setup_input_with_pullup(button1); // Heligeneraatori viigu väljundiks määramine pin_setup_output(buzzer); // Taimer 0 normaalreziimi seadistamine. Selles reziimis loendab // taimer 255-ni (kaasa arvatud) ja ainuke tekkiv sündmus on ületäitumine. // Jagurit ei kasutata. timer0_init_normal(TIMER0_NO_PRESCALE); // Taimer 0 ületäitumise katkestuse lubamine timer0_overflow_interrupt_enable(true); // Taimer 0 väärtuse nullimine timer0_set_value(0); // Globaalne katkestuste lubamine sei(); while (1) { // Loeb nuppude väärtused. new_value1 = pin_get_debounced_value(button1); // Nupp S1 alla vajutatud. Registreeritakse ainult üks vajutus. if((!new_value1) && (old_value1)) { // Mängi järgnev lugu play(FSH_4, 2); play(REST, 2); play(A_4, 3); play(FSH_4, 2); play(FSH_4, 1); play(B_4, 2); play(FSH_4, 2); play(E_4, 2); play(FSH_4, 2); play(REST, 2); play(CSH_5, 3); play(FSH_4, 2); play(FSH_4, 1); play(D_5, 2); play(CSH_5, 2); play(A_4, 2); play(FSH_4, 2); play(CSH_5, 2); play(FSH_5, 2); play(FSH_4, 1); play(E_4, 2); play(E_4, 1); play(CSH_4, 2); play(GSH_4, 2); play(FSH_4, 6); play(REST, 12); // Viide sw_delay_ms(2); } // Jätab eelmise nupu väärtuse meelde old_value1 = new_value1; } }