LOW-LEVEL PROGRAMIRANJE ARDUINA: TIMER INTERRUPTI
UVOD
U trećem nastavku serije low-level programiranja AVR mikrokontrolera upoznati ćemo se s nekim novim registrima. Koristiti ćemo timer interrupte kako bi kreirali vlastitu delay funkciju te u konačnosti zablinkali ledicu na pinu13. Ponovno ćemo koristiti C programiranje, a već u idućem tutorijalu prebacujemo se na Assembler.
Do sada smo odradilo ovo:
Lekcija 1. Blinkanje LEDice
Lekcija 2. Bitwise operatori
ŠTO JE TIMER?
Timer (točnije Timer/Counter) je komad hardwarea ugrađen u mikrokontroler (i drugi kontroleri i procesori ga imaju). Timer programiramo preko posebnih registara. Npr. konfiguriramo prescaler, njegov mode, waveform generator no o svemu detaljnije uskoro.
Kako radimo s Dasduinom, odnosno Atmel AVR ATmega328 mikrokontrolerom i ovdje ćemo se koncentrirati na njega. ATmega328 ima tri timera: timer0, timer1 i timer2. Timer0 i timer2 su 8bitni timer, timer1 je 16bitni. Glavna razilika je rezolucija, 8bitni je 256 vrijednosti a 16bitno 65536 za veću rezoluciju.
Svi timeri ovise o satu sustava, on je na Dasduinu 16Mhz. Svi timeri na Arduino firmwareu (bootloaderu) su konfigurirani na 1kHz frekvenciju i svi interrupti su omogućeni.
Zamislite da imate brojač koji kada pritisnete gumb se povećava za jedan. Timer/counter radi na istom principu: broji otkucaje sata. Spomenili smo već da je brzina brojanja 16 miliuna otkucaja u sekundi, otprilike 63ns po otkucaju ili operaciji. 8bitni timer će tako brojati od 0-255, a 16 bitni od 0-65535. Vrijeme potrebno da se counter resetira je 256/16.000.000 = 16us, odnosno 65536/16.000.000 = 4ms za 16bitni registar. Ne baš praktičan način za zablinkati LEDicu svakih 1 sekundu. Umjesto kontroliranja brzine timer/countera koristiti ćemo nešto što se zove prescaler. U nastavku bavimo se registrima zaduženim za timere, a u jednom od njih definirati ćemo i prescaler.
Prescaler
Prescaler definira brzinu određenog timera (timer0, timer1 ili timer2) prema formuli:
(brzina timera [Hz]) = (brzina Arduino clocka (16MHz) [Hz]) / prescaler
Prema datasheetu prescalaru su definirane vrijednosti od 1, 8, 64, 256 i 1024. Vidljivo je kako će prescaler 1 inkrementirati clock od 16Mhz, prescaler 8 2Mhz, prescaler 64 250kHz i td. Jednostavno rečeno, prescaler postavljen na 1024 će svakih 1024 otkucaja 16Mhz timer/countera na svoj counter (timer0, timer1, timer2) dodati 1. Najjednostavnije rečeno dok Mujo pojede 1024 čevapa, Haso će 1.
Vratimo se timerima. Prvi novi registar koji ćemo spomenuti u ovom tutorialu je OCR – output compare register u koji ćemo spremiti compare match vrijednost. Čini se previše odjednom? Nije, već smo sve ovo spomenuli samo smo im dali službena imena. Ubacimo u formulu gore kako bismo lakše vizualizirali:
Objašnjenje:
16Mhz – brzina timer/countera clocka Arduina, uvrštavamo kao 16.000.000 Hz
brzina.timera – s obzirom kako želimo blinkati LEDicu svakih sekundu, uvrštavamo frekvenciju 1Hz
-1 – counter broj od 0 pa uvrštavamo -1 u formulu
Iz tablice vidimo kako je najmanji prescalar koji možemo koristiti 256 i to za 16bitni timer1. Razlog? Vrijednost 62499 možemo spremiti u 16bitni registar timera1.
Za mjerenje countera u frekvenciji 1Hz(1sekunda) možemo koristiti i prescaler 1024, međutim točnije je koristiti manji prescaler. U nijednom od ovih slučajeva ne možemo koristiti 8bitne timere timer0 i timer2.
Sada kada smo odabrali timer i prescaler vrijeme je da upoznamo registre koje trebamo postaviti.
Registri
Za više pogledajte datasheet
TCCR1A Timer count control register – 0b00000000 (normal)(normal)
TCCR1B Timer count control register – 0b00000100 (no icr)(256-prescaler)
TCCR1C Timer count control register – 0b00000000 (no force)
OCR1AH output compare register – high byte 0xF4
OCR1AL output compare register – low byte 0x23
Na ovim registrima postavljamo izračunatu compare match vrijednost, 62499. S obzirom kako postavljamo posebno highi i low byte pretvoramo 62499 iz dekadskog u heksadekadski a to je 0xF423. Pa je tako high byte = 0xF4, a low byte 0x23.
TIMSK1 timer interrupt mask 0b00000010 (output compare u enable – OCIE1A)
DDRB
PORTB
DDRB i PORTB registre smo već spomenuli u prvoj lekciji.
C KOD
void
setup
() {
DDRB = B00100000;
// portB5 (pin13) postavljamo kao output
cli();
//onemogucujemo sve interrupte, kako timer intuerrupt ne bi bio prekinut drugim interruptom s većim prioritetom
TCCR1A = 0b00000000;
TCCR1B = 0b00000100;
TCCR1C = 0b00000000;
OCR1AH = 0xF4;
OCR1AL = 0x23;
TIMSK1 = 0b00000010;
sei();
// ponovno omogucujemo interrupte, ukljucujuci i timer interrupte
}
ISR(TIMER1_COMPA_vect) {
// timer compare interrupt service routine
PORTB ^= 0b00100000;
// XOR operator, ako je upaljena LEDica gasimo je i obratno
}
void
loop
() {
}
TIMER BIBLIOTEKA
Nekoliko je timer biblioteka koje koriste različite funkcije. Odraditi ćemo samo one koje bismo i koristili u primjeru blinkanja LEDice, više pronađite na Arduino referencama.
Timer0
Koristi se za funkcije kao delay(), millis() and micros().
delay() – zaustavlja mikrokontroler za zadani broj milisekundi, postoji i funkcija delayMicroseconds() i sl.
millis() – funkcija u koju je spremljeno vrijeme od pokretanja mikrokontrolera, ili resetrianja, u milisekundama. Do overflowa može brojati 15ak dana.
micros() – funkcija u koju je spremljeno vrijeme od pokretanja mikrokontrolera, ili resetrianja, u mikrosekundama. Do overflowa može brojati 70ak minuta.
Primjer blinkanja LEDice bez delay() fje:
unsigned
long
proslo_vrijeme = 0;
// generalno koristimo unsigned long za varijable koje spremaju vrijeme
void
setup
() {
pinMode
(13,
OUTPUT
);
}
void
loop
() {
unsigned
long
trenutno_vrijeme =
millis
();
// spremamo trenutno vrijeme od pokretanja mikrokontrolera
if
(trenutno_vrijeme - proslo_vrijeme >= 1000) {
proslo_vrijeme = trenutno_vrijeme;
// spremi kada je poslijednji put blinknula LEDica
digitalWrite
(13,
digitalRead
(13) ^ 1);
// mijenjaj stanje LEDice
}
}
ZAKLJUČAK
Osim što nam, funkcije koje smo upravo odradili, omogućuju multitasking daju nam i puno bolji uvid u vrijeme odvijanja operacija. Usporedimo li blinkanje LEDice programirane na ovaj način i one Arduino digitalWrite() i delay() funkcijama primjetiti ćemo kako nakon određenog vremena dolazi do odstupanja u trenutcima blinkanja.
Za što točnije praćenje rada mikrokontrolera i interrupta nužno je supustiti se na niži programski jezik. Nakon ovih kratkih uvodnih lekcija, od sljedeće, krećemo s Assembly programiranjem.
Korišteni proizvodi
No products found