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ť.
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
}
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.
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.
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...
}
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 k článku nájdete na GitHub:
08.01.2017