Etter å ha lest om timere og avbrudd («interrupts») en stund nå, begynner endelig ting å falle på plass. Til å begynne med var denne delen av AVR veldig uoversiktlig, fordi det finnes et hav av bits i forskjellige registre som man må kjenne til og forstå.
Men så begynte jeg å innså at man ikke trenger å fullt ut forstå alle mulige konfigurasjoner, bare for å ta i bruk en timer og ha litt nytte av den .. Selv med grunnleggende forståelse kan man benytte en timer til mye forskjellig.
Og for å gjøre meg selv en tjeneste skal jeg fra nå av konsekvent kalle timere for tellere, kanskje dette fordrer mer intuitiv tenking. (Når jeg tenker på timere, så tenker jeg fort på timere i JavaScript – noe som bare forvirrer i denne sammenhengen ..)
Introduksjon
En teller har som oppgave å telle fra 0 og opp til og med en satt maksverdi. Også begynner den vanligvis på nytt igjen. Underveis kan telleren sammenligne tellerverdien med en gitt verdi (som du bestemmer).
Både å få treff ved sammenligning og å nå sluttverdien man skal telle til, gjør at det sendes et avbruddssignal man kan benytte til å kjøre kode man skriver selv.
Riktignok er det ikke bare tellere som sender avbruddssignaler. Det finnes andre kilder også, for eksempel PC6 (på ATmega328P i hvertfall) som ved 0 V skaper reset.
Praksis
Å ta i bruk tellere krever at man setter bits i minst et par registre. Skal man benytte et avbruddssignal til å kjøre kode, krever dette ett register til. Til flere endringer fra det som er standard (altså default), til flere registre og bits blir det ..
Avhengig av situasjonen kan det være nødvendig å deaktivere avbruddssignaler fordi man skal gjøre noe viktig. Også aktiverer man igjen etterpå. Dette gjøres med cli() og sei():
// Disable interrupts globally cli(); ... // Enable interrupts globally sei();
Dette vil da foregå i main() eller en funksjon som kjøres derifra.
Tidtaking er ganske grunnleggende å kunne kontrollere, nesten uansett hva man skal programmere. Derfor ble dette et greit sted å begynne her. Til dette valgte jeg å styre ei lysdiode med en enkel 8-bit teller (Timer2). Og med kode som kjører ved såkalt overflyt («overflow) – dvs. når telleren når maksverdien den skal telle til og begynner helt på nytt igjen:
volatile long int counter = 0; ISR (TIMER2_OVF_vect) { counter++; if (counter != 0 && counter == 250) { counter = 0; PORTB ^= 1<<PB4; } } int main(void) { // 1. Prescaler TCCR2B |= 1<<CS22 | 1<<CS21; // 256 // 2. Fast PWM mode TCCR2A |= 1<<WGM21 | 1<<WGM20; TCCR2B |= 1<<WGM22; // 3. Resolution, value to count to OCR2A = 0xFA; // Decimal value: 250 // 4. Interrupt types TIMSK2 |= 1<<TOIE2; //////////////////// // Digital pin 12 = PB4 DDRB |= 1<<PB4; }
Denne kodebiten kan skru ei lysdiode på/av hvert sekund, takket være if-betingelsen som gjør at man venter lenge nok. Uten if-betingelsen ville dioden blitt tent/slukket flere hundre ganger i sekundet. (Se forklaring under EKSEMPEL lenger ned, for matematikken bak.)
Dioden er tilkoblet pinne 4 (PB4) på port D. Den skrus av/på med «bit shifting». For å sende ut må pinnen også være satt til dette i DDRB.