*/ ?>

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 Croduinom, 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 Croduinu 16Mhz. Svi timeri na Arduino firmwareu (bootloaderu) su konfigurirani na 1kHz frekvenciju i svi interrupti su omogućeni. Ukoliko ste ovdje zbog Arduino Timer biblioteke, preskočite na odlomak kako ga koristiti. 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: formula 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 compare match registar 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.

Drugi dio tutoriala low-level programiranja u kojem obrađujemo bitwise opratore na primjeru kativiranja LEDice pritiskom na tipkalo.

Prvi tutorijal u seriji low-level programiranja Arduina. Blinkati ćemo internom LEDicom na pin13 i objasniti koji registri su odgovorni za to.