Časovač v CTC režime

Zápisník experimentátora

Hierarchy: Časovač (timer)

Každé Arduino obsahuje niekoľko časovačov (timerov). Tie majú rôzne využitie. My sa v tomto článku zameriame na časovač, ktorý v pravidelných intervaloch vyvoláva prerušenie, ktoré nám umožní v prerušení vykonať požadovanú akciu. Výsledkom bude blikanie led diódou 13 na frekvencii 1 Hz. Dosiahneme tak tú istú funkcie, ako v klasickom príklade Blink. Nebudeme ale zbytočne zaťažovať mikrokontrolér čakaním a uvoľníme mu ruky aj na inú činnosť.

Na pripomenutie

Klasický príklad Blink vyzerá takto. Vďaka funkcii delay bude dobre fungovať, ale už nám neostane čas na nič iné, pretože mikrokontrolér je vyťažený čakaním. Celý tento príklad môžeme napísať aj pomocou časovač a prerušenia.

// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);                       // wait for a second
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);                       // wait for a second
}

CTC mód

Rôzne režimy časovača môžu vzbudzovať nechuť študovať datasheet, ale reálne sa potrebujete naučiť iba jediný z nich. Je to režim CTC, ktorý časuje postupne v intervale 0-OCRA a pri dosiahnutí maxima vyvolá prerušenie. Príklad si ukážeme na 16bitovom časovači Timer1.

  • Pred každou úpravou časovača je vhodné zakázať prerušenia.
  • Potom je vhodné aj vynulovať všetky registre, ktoré sa týkajú časovača. V našom prípade sú to registre TCCR1A, TCCR1B a TCNT1.
  • Frekvencia mikrokontroléra ATmega328P je 16 MHz. To môže byť pre naše časovanie príliš vysoká hodnota. Preto máme k dispozícii deličky frekvencie. Pre každý časovač sú tieto deličky mierne iné, ale dá sa povedať, že obvykle sú k dispozícii deličky 8, 64, 256 a 1024. Napríklad delička 8 nám zníži základnú frekvenciu zo 16 na 2 MHz.
  • Každý časovať podporuje viacero módov práce. Pomocou vhodnej modifikácie registrov si musíme zapnúť mód CTC.
  • V režime CTC časovač časuje od 0 po hodnotu registra OCRA. Čiže nadobudne OCRA+1 hodnôt. Prerušenie môže nastať v okamihu dosiahnutia hodnoty OCRA. Treba to ale nastaviť modifikáciou registrov.
  • Celá mágia nastavenia sa skrýva v registri OCRA. Podľa nastavenej hodnoty dosiahneme požadovanú frekvenciu. Aby sme mohli vykonávať našu činnosť, musíme si povoliť príslušné prerušenie a v ňom napísať náš kód.

Vzorec na výpočet frekvencie je fOCXA = fclk / (1 x N x (1 + OCRXA)), kde N je hodnota deličky. V prípade požadovanej frekvencie 1 Hz to môže byť napríklad 1 = 16000000 / (1 x 1024 x (1 + 15624)).

Pozor na to, že v datasheete sa dočítate o vzorci fOCXA = fclk / (2 x N x (1 + OCRXA)). Ten vzorec ale platí len pre generovanie signálu podľa priloženej tabuľky (CTC Mode, Timing Diagram). Z nej jasne vidno, že prerušenia nastávajú 2x častejšie a preto použijeme prvý vzorec.

Príklad s funkciou digitalWrite

Na ukážku som napísal dva príklady. Tento prvý využíva funkcie pinMode a digitalWrite.

#define ledPin 13

void setup()
{
  pinMode(ledPin, OUTPUT);
  
  // initialize timer1 
  noInterrupts();           // disable all interrupts
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1  = 0;

  OCR1A = 15624;            // compare match register 16MHz/1024/1Hz
  TCCR1B |= (1 << WGM12);   // CTC mode
  TCCR1B |= (1 << CS12) | (0 << CS11) | (1 << CS10); // 1024 prescaler 
  TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt
  interrupts();             // enable all interrupts
}

ISR(TIMER1_COMPA_vect)          // timer compare interrupt service routine
{
  digitalWrite(ledPin, digitalRead(ledPin) ^ 1);   // toggle LED pin
}

void loop()
{
  // your program here...
}

Príklad s priamym prístupom na porty

Tento druhý priamo manipuluje porty. Robí presne to isté, čo predchádzajúci.

#define ledPin 13

void setup()
{
  DDRB |= (1<<PB5);
  
  // initialize timer1 
  noInterrupts();           // disable all interrupts
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1  = 0;

  OCR1A = 15624;            // compare match register 16MHz/1024/1Hz
  TCCR1B |= (1 << WGM12);   // CTC mode
  TCCR1B |= (1 << CS12) | (0 << CS11) | (1 << CS10); // 1024 prescaler 
  TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt
  interrupts();             // enable all interrupts
}

ISR(TIMER1_COMPA_vect)          // timer compare interrupt service routine
{
  PORTB ^= (1 << PB5);
}

void loop()
{
  // your program here...
}

Zdrojové kódy

Zdrojové kódy k článku nájdete na GitHub:


08.01.2017


Menu