This is an old revision of the document!
Loendurid (inglise keeles counter), teatud mõttes taimerid (inglise keeles timer), on mikrokontrollerite ühed vajalikuimad lisafunktsioonid. Nende abil saab protsesse täpselt ajastada, signaale genereerida ja sündmusi loendada. Loenduri tööpõhimõte seisneb sisendtaktide arvu trigerite ahela abil binaarväärtuseks teisendamises. Ahela pikkusest oleneb maksimaalne loendatavate taktide arv, mida tähistatakse kahendkoodi pikkusega. AVR mikrokontrolleril on loendurid 8- ja 16-bitised. Kui loendur omab maksimaalset väärtust (8-bitiste puhul 255, 16-bitiste puhul 65535), tekib järgmise taktiga ületäitumine (inglise keeles overflow) ja loendur alustab uuesti nullist. Loenduri taktsignaal saab tulla mikrokontrolleri töötaktist ja sel juhul on võimalik selle sagedust sagedusjaguriga (inglise keeles prescaler) ka vähendada. Mõnel AVR-il on ka sisemine eraldiseisev taktsignaali generaator, mille sagedust saab sageduskordistiga tõsta. Sageduskordistiga on näiteks USB liidesega AVR-id. Loendurid erinevad ka rakendusvõimaluste ja töörežiimide poolest.
Loendurit, mille taktsignaal tuleb töötaktist ja mis ei ole väliselt millegagi liidestatud, võib nimetada tüüpiliseks loenduriks. Sel loenduril on lisaks hetkeväärtuse lugemisele vaid üks lisavõimalus - katkestuse tekitamine loenduri ületäitumisel. Tüüpilist loendurit või loendurit tüüpilises režiimis kasutatakse tavaliselt kindlate ajaintervallide järel mingi programmilõigu täitmiseks.
Vaja on 8 MHz taktsagedusel töötav ATmega128 10 ms (sagedus 100 Hz) ajavahemiku järel katkestust tekitama panna. Ülesandeks sobib 8-bitine loendur 0.
#include <avr/interrupt.h> // Loenduri 0 ületäitumine ISR(TIMER0_OVF_vect) { // Loendurile sellise väärtuse omistamine, // et järgmine ületäitumine saabuks 10 ms pärast. // Valem: 256 - 8 MHz / 1024 / 100 Hz = 177,785 = ~178 TCNT0 = 178; // Tee midagi, aga alles pärast loendurile uue väärtuse omistamist } int main() { // Kui esimest ületäitumise katkestust soovitakse ka 10 ms // pärast, tuleb loendur enne käivitamist algväärtustada. TCNT0 = 178; // Sagedusjaguri teguriks 1024 TCCR0 = 0x05; // Loenduri täitumise katkestuse lubamine TIMSK |= (1 << TOIE0); // Globaalne katkestuste lubamine sei(); // Lõputu programmitsükkel while (1) continue; }
Näites toodud loendurile omistatava väärtusega siiski täpselt 10 ms järel katkestust ei tekitata, sest vaja oleks loendurile omistada komakohaga väärtus, kuid see pole võimalik. Et täpset katkestuse intervalli saada, tuleb nii sagedusjaguri tegur kui loendurile täitumisel omistatav väärtus valida nii, et taktsagedus jaguks täpselt. Paraku pole see alati võimalik ja eriti just 8-bitise loenduri puhul, sest selle väärtuste skaala on üsna väike. Täpsema ja suurema intervalli tekitamiseks saab kasutada 16-bitist loendurit.
Kõik muud loendurid täiendavad tüüpilist loendurit. Seega kõigil loenduritel on tüüpilise loenduri tunnusjoon - ületäitumise katkestus.
Loenduri taktsignaalina saab kasutada ka mikrokontrollerivälist signaali (inglise keeles external clock source). Selleks on AVR mikrokontrolleril Tn viik, kus n tähistab loenduri numbrit. Välist taktsignaali ja polaarsust saab valida sagedusjaguri registriga. Välise taktika loenduril on muus osas samasugused omadused kui tüüpilisel loenduril.
Kuna loendurid võimaldavad mõõta aega, on keerukamatel AVR mikrokontrolleritel võimalus riistvaraliselt mõõta ka aega, mil toimus mingi sündmus. Seda loenduri osa nimetatakse sündmuse püüdjaks (inglise keeles input capture unit). AVR-is on valida kahe sündmuse vahel: spetsiaalse sisendviigu või analoogkomparaatori võrdlustulemuse loogilise väärtuse muutus. Kui toimub valitud sündmus, kirjutatakse loenduri väärtus spetsiaalsesse registrisse, kust selle võib soovitud ajal välja lugeda. Kui sündmuse toimumise aeg on pikem kui loenduri ületäitumise aeg, tuleb tarkvaraliselt lugeda ka loenduri ületäitumisi (näiteks ületäitumise katkestusega) ja need lõpptulemusse arvestada.
Vaja on 8 MHz taktsagedusel töötava ATmega128-ga mõõta välise 122 Hz - 100 kHz loogilise nelinurksignaali sagedust 1 Hz täpsusega. Programm on tehtud 16-bitise loendur 1 sündmuste püüdjaga.
#include <avr/interrupt.h> unsigned long frequency; // Sündmuse toimumise katkestus ISR(TIMER1_CAPT_vect) { // Loenduri nullimine esimese asjana TCNT1 = 0; // Tulemus on ainult siis arvestatav, kui // loendur pole vahepeal üle täitunud if (!(TIFR & (1 << TOV1))) { // Sageduse arvutamine perioodi pöördväärtusest. frequency = (unsigned long)8000000 / (unsigned long)ICR1; } else { // Sagedus on vähem kui 122 Hz frequency = 0; // Loenduri ületäitumise lipukese nullimine TIFR &= ~(1 << TOV1); } } int main() { // Tõusva frondi registreerimine, sagedusjaguri tegur 1 TCCR1B = (1 << ICES1) | (1 << CS10); // Sündmuse toimumise katkestuse lubamine TIMSK = (1 << TICIE1); // Globaalne katkestuste lubamine sei(); // Lõputu programmitsükkel while (1) continue; }
Programmis tekib välise signaali tõusva frondi ajal sündmuse katkestus. Katkestuse jooksul kontrollitakse, ega loenduri ületäitumine pole toimunud - see saab juhtuda kui signaali sagedus on alla 122 Hz (8 MHz / 216) ja sel juhul ei kajasta loenduri väärtus reaalset perioodi. Sagedus arvutatakse 32-bitiste arvudega pöördväärtusena perioodist. Esimese asjana aga nullitakse loendur, sest taimer töötab samal taktil mis protsessor ja iga instruktsiooni täitmine, mis toimub pärast välist sündmust, lühendab mõõdetavat perioodi ning sellest tulenevalt rikub ka mõõtetulemust. Maksimaalsele mõõdetavale sagedusele seab piiri katkestuse programmiosa tööaeg.
Sündmuste püüdmist ning nende aja registreerimist saab teha ka tarkvaraliselt. Saab kasutada väliseid või muid katkestusi ja nende tekkimise ajal lugeda loenduri väärtuse. Kuid riistvaraline sündmuste püüdmine on mõeldud eeskätt siiski programmist sõltumatuks töötamiseks ja suhteliselt lühiajaliste (või tihedate) sündmuste mõõtmiseks.
Peale signaali pikkuse mõõtmise saab keerukamate loenduritega ka signaali tekitada. Selleks on loenduril väärtuse võrdlemise üksus (inglise keeles output compare unit) ja võrdlustulemuse väljastusüksus (inglise keeles compare match output unit). Võrdlusüksusesse kuuluvad registrid sama bitilaiusega kui loendur ise ja mille väärtusi võrreldakse loenduri väärtusega selle töö ajal. Hetkel, mil loenduri väärtus saab võrdseks võrdlusüksuse registri väärtusega, saab tekitada katkestuse ja spetsiaalsete väljundviikude oleku muutuse. Väljundviigu oleku muutused tekitavadki signaali.
Väljastusüksuses on võimalik seadistada väljundviikude käitumist võrdusmomendil. Valida on viigu kõrgeks muutmise, madalaks muutmise ja ümbermuutumise vahel. Nende täpsem toime aga sõltub sellest, millist signaali genereerimise režiimi kasutada. Kui kasutusel on PWM signaali tekitamise režiim (järgmine peatükk), siis viik muutub loenduri täitumisel kas madalaks või kõrgeks, vastupidiselt sellele, mida viik teeb võrdusmomendil.
Mõnedel signaali genereerimise režiimidel on määratav ka loenduri suurim väärtus - loenduri füüsiline suurus jääb küll samaks, kuid mängus on võrdlusregister, mille väärtust ületades loendur nullitakse. Seda võimalust kasutades saab eespool toodud ülesandeid täpse ajalise katkestuse tekitamise kohta lahendada, kuid mõeldud on see pigem signaali perioodi muutmiseks. Vähe sellest - mõnes režiimis toimib loendur juurde ja maha lugedes.
Loendurid ja eriti just nende signaali genereerimise režiimid on ühed keerulisemad perifeeriamoodulid AVR-il. Kõigist neist kirjutamine läheks pikaks ja enamasti pole nende juures vaja ka kõike teada. Seetõttu on järgnevalt kirjeldatud vaid üht levinuimat PWM signaali robootikas. Ülejäänut saab juba AVR dokumentatsioonist järgi uurida.
Pulsilaius-modulatsioon (inglise keeles pulse width modulation, lühend PWM) on signaali tüüp, mille sagedus ja ühtlasi ka perioodid on konstantne (enamasti), kuid mõlema poolperioodi pikkus on muutuv. PWM signaale kasutatakse elektromehaaniliste, optiliste jms. seadmete juhtimiseks. Näiteks mudelismist tuntud servomootorite PWM signaal on 50 Hz sagedusega ja 1 ms kuni 2 ms pikkuse kõrge poolperioodiga.
Vaja on 8 MHz taktsagedusel töötava ATmega128-ga genereerida kaks kiirusreguleeritavate servomootorite signaali. Viiguga PB5 (OC1A) tuleb genereerida pulsipikkus 1 ms ja viiguga PB6 (OC1B) pulsipikkus 2 ms.
#include <avr/io.h> int main() { // Viigud väljundiks DDRB |= (1 << PIN5) | (1 << PIN6); // Väljundid A ja B võrdusmomendil madalaks, // "Fast PWM" režiim, sagedusjagur 8 TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11); TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11); // Suurim loenduri väärtus. Valem: // TOP = 8 MHz / 8 / 50 Hz ICR1 = 20000; // Esimese mootori poolperiood 1 ms, teisel 2 ms OCR1A = 1000; OCR1B = 2000; // Lõputu programmitsükkel while (1) continue; }