Zápisník experimentátora
Hierarchy: Časovač (timer)
Každé Arduino obsahuje několik časovačů (timerů). Ty mají různé využití. My se v tomto článku zaměříme na časovač, který v pravidelných intervalech vyvolává přerušení, které nám umožní v přerušení provést požadovanou akci. Výsledkem bude blikání led diodou 13 na frekvenci 1 Hz. Dosáhneme tak tutéž funkci, jako v klasickém příkladu Blink. Nebudeme ale zbytečně zatěžovat mikrokontrolér čekáním a uvolníme mu ruce i na jinou činnost.
Klasický příklad Blink vypadá takto. Díky funkci delay bude dobře fungovat, ale už nám nezbude čas na nic jiného, protože mikrokontrolér je vytížen čekáním. Celý tento příklad můžeme napsat také pomocí časovač a přerušení.
// 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ůzné režimy časovače mohou vzbuzovat nechuť studovat datasheet, ale reálně se potřebujete naučit pouze jediný z nich. Je to režim CTC, který časuje postupně v intervalu 0-OCRA a při dosažení maxima vyvolá přerušení. Příklad si ukážeme na 16bitovém časovači Timer1.
Vzorec pro výpočet frekvence je fOCXA = fclk / (1 x N x (1 + OCRXA)), kde N je hodnota děličky. V případě požadované frekvence 1 Hz to může být například 1 = 16000000 / (1 x 1024 x (1 + 15624)).
Pozor na to, že v datasheetu se dočtete o vzorci fOCXA = fclk / (2 x N x (1 + OCRXA)). Ten vzorec ale platí jen pro generování signálu podle přiložené tabulky (CTC Mode, Timing Diagram). Z ní jasně vidět, že přerušení nastávají 2x častěji a proto použijeme první vzorec.
Na ukázku jsem napsal dva příklady. Tento první využívá funkce 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ý přímo manipuluje porty. Dělá přesně to samé, co předchozí.
#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 naleznete na GitHub:
29.12.2017