====== Loendurid/Taimerid ======
Loendurid (inglise keeles //counter//), teatud mõttes taimerid (inglise keeles //timer//), on mikrokontrollerite ühed oluliseimad 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 sisemine eraldiseisev taktsignaali generaator, mille sagedust saab sageduskordistiga tõsta. Loendurid erinevad ka rakendusvõimaluste ja töörežiimide poolest.
~~CL~~
Atmeli xmega kontrollerite puhul tuleb jälgida, mis siini taimerit kasutatakse. AVR xmega taimerite registritesse kirjutamine toimib sarnaselt eelnevates peatükkides toodud näidetega. Režiimid on sarnased ning täpsem kirjeldus on toodud andmelehes.
===== Loenduri normaalrežiim =====
Normaalrežiimis ei täida loendur muud funktsiooni, kui pidevat järjestikulist arvude loendamist. Loenduri väärtust saab igal hetkel programmis muidugi ka lugeda ja muuta. Ainuke lisavõimalus normaalrežiimis on katkestuse tekitamine loenduri ületäitumisel. Normaalrežiimi kasutatakse tavaliselt mingi programmilõigu täitmiseks kindlate ajaintervallide järel.
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
ISR(TIMER0_OVF_vect) {
// Loendurile sellise väärtuse omistamine,
// et järgmine ületäitumine tekiks 10 ms pärast
// Valem: 256 - 8 MHz / 1024 / 100 Hz = 177,785 = ~178
TCNT0 = 178;
}
int main(void) {
// Et esimene ületäitumise katkestus tekiks 10 ms pärast,
// tuleb ka siinkohal loendur algväärtustada
TCNT0 = 178;
// Sagedusjaguri teguriks 1024
TCCR0 = 0x07;
// 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 ka 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.
==== Välise taktiga loendur ====
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.
==== Sündmuste mõõtmine ====
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
unsigned long frequency;
// Sündmuse toimumise katkestus
ISR(TIMER1_CAPT_vect) {
// Loenduri nullimine
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(void) {
// 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äärtus. Riistvaraline sündmuste püüdmine on siiski mõeldud eeskätt programmist sõltumatuks töötamiseks ja suhteliselt lühiajaliste (või tihedate) sündmuste mõõtmiseks.
===== Signaali genereerimine =====
Keerukamate loenduritega saab peale signaali pikkuse mõõtmise 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 nende registrite 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. Valida on võrdusmomendil viigu kõrgeks muutumise, madalaks muutumise ja ümbermuutumise vahel. Väljundviigu oleku muutused tekitavadki signaali.
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. Lisaks sellele on võimalik loendurit seadistada režiimi, kus see toimib nii juurde- kui mahalugemisel.
Loendurid ja nende abil genereeritavate signaalide režiimid on ühed keerulisemad perifeeriamoodulid AVR-il. Kõigist neist kirjutamine läheks pikaks ja enamasti pole nende kasutamisel vaja kõike detailselt teada. Seetõttu on järgnevalt kirjeldatud vaid üht levinuimat PWM signaali robootikas. Ülejäänut saab juba AVR dokumentatsioonist järgi uurida.
==== Pulsilaius-modulatsioon ====
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 kiirusreguleeritavat servomootori signaali. Viiguga PB5 (OC1A) tuleb genereerida pulsipikkus 1 ms ja viiguga PB6 (OC1B) pulsipikkus 2 ms.
#include
int main(void) {
// 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;
}