Čítač/časovač a jeho použitie. Časovanie udalostí, čítač externých impulzov, meranie periódy atď…
Mikrokontroléry rady AVR obsahujú niekoľko jednotiek čítača/časovača a označujú sa číslom (0, 1, 2 …). Konkrétne ATmega8 obsahuje takéto jednotky tri. Každá z nich dokáže vykonávať základné činnosti (čítať/časovať) avšak niektoré z nich obsahujú aj ďalšie funkcie ako generovanie PWM signálu, externý vstup čítača atď…
Register ktorý ukladá aktuálnu hodnotu čítača sa označuje TCNTn, kde n predstavuje číslo čítača/časovača. Pri časovaní udalostí často využívame pretečenia TCNTn, pri ktorom vykonáme prerušenie. Pretečenie nastane vtedy, keď máme v čítači maximálnu hodnotu (napr. pri 8 bitovom je to hodnota 255) a túto hodnotu inkementujeme. Vtedy sa čítač vynuluje a nastane prerušenie.
Pri používaní interného RC oscilátora sa môže náš nastavený čas od skutočného mierne líšiť. Je to spôsobené odchýlkou oscilátora. Pri výrobe mikrokontroléra bol vnútorný RC oscilátor nakalibrovaný a výsledkom je kalibračná konštanta. Túto konštantu sa dozvieme v programovacom okne v záložke „Advanced“. Po stlačení tlačidla Read sa dozvieme našu kalibračnú konštantu, ktorá je pre každý mikrokontrolér jedinečná. V mojom prípade je hodnota 0xA5. Keď chceme aplikovať kalibračnú konštantu v našom programe zapíšeme túto hodnotu do registra OSCCAL.
Zápis v mojom prípade bude vyzerať takto :
OSCCAL = 0xA5;
Rozoberieme si postupne tri čítače/časovače ktoré obsahujú mikrokontrolér ATmega8 a zoznámime sa s možnosťami ich použitia.
Vlastnosti:
– 8 bitový čítač/časovač
– Čítač externých impulzov privádzaných na T0
– 10 bitová preddelička
– Prerušenie na akciu pretečenia TCNT0
Popis registrov:
TCCR0 (Timer/Counter 0 Control Register) – slúži na riadenie funkcie čítača/časovača 0.
V prípade čítača/časovača 0 slúži len na výber zdroja hodin a preddeličky pomocou bitov CS02, CS01, CS00. Z tabuľky vidíme, že obsah registra TCNT0 môžeme inkrementovať buď vnútorne (priamo alebo cez preddeličku), alebo externe cez vstupný pin T0. Ak máme frekvenciu hodín 8MHz, tak bez preddeličky sa bude obsah registra TCNT0 inkrementovať za čas T = 1/8MHz = 125ns. Pri preddeličke 64 bude perióda inkrementácie TCNT0 T= 1/(8MHz/64) = 8ms.
TIMSK (Timer/counter Interrupt Mask Register)
– pomocou bitu TOIE0 (Timer/counter 0 Overflow Interrupt Enable) v tomto registri môžeme povoliť prerušenie na akciu pretečenia čítača/časovača 0.
Príklad č.1:
S využitím čítača/časovača 0 a frekvencií hodín 8 MHz realizujte blikanie LED s približnou dĺžkou zopnutia Ton=0,5s a dĺžkou rozopnutia Toff = 0,5s.
Zvolíme preddeličku 1024:
T = 1 / (8MHz / 1024) = 128us – perióda inkrementácie TCNT0.
Kedže čítač/časovač 0 je 8-bitový ,tak pretečenie nastane vždy po 256 impulzoch, teda nastane každých 256*128us = 32,768ms
Vypočítame teda koľko krát musí prerušenie nastať ,aby sme dosiahli čas 0,5s.
N = 500ms / 32,768ms = 15,25x čo je zaokrúhlene 15x ,teda 491,52ms čo predstavuje odchýlku -1,7%
Zdrojový kód:
volatile unsigned char i;
// pretečenie počítadla TCNT0 – nastane každých 128us*256 = 32,768ms
ISR (TIMER0_OVF_vect){
// 15*32.768 = 491,52ms
if(i == 15){
PORTD ^= (1 << PD7); // finta s log. operáciou XOR - neguje len pin PD7
i=0;
}
i++;
}
int main(){
DDRD |= (1 << PD7); // PD7 ako výstupný
TCCR0 |= (1 << CS02) | (1 << CS00); // preddelicka /1024 (128us)
TIMSK |= (1 << TOIE0); // prerušenie pri pretečení TCNT0
sei(); //povol globálne prerušenia
while(1); //nekonenčná slučka
return 0;
}
Youtube video:
Príklad č.2:
Z predchádzajúceho príkladu vidíme, že pri preddeličke 1024 a frekvencii hodín 8MHz nie je možné nastaviť čítač/časovač 0 na presne zadaný čas. Použijeme teda preddeličku 8 pri frekvencií hodín 8MHz, pri ktorom bude perióda inkrementácie čítača 1us. S týmto časom už vieme pohodlnejšie pracovať. Ukážeme si aj „fintu“ s ručným nastavením registra TCNT0,ktorým nastavíme, aby nám pretečenie nastalo vždy po 100 impulzoch teda po 100us.
Zdrojový kód:
volatile unsigned int i;
void delay_ms(unsigned int time);
// pretečenie počítadla TCNT0 – nastane každých 100us
ISR (TIMER0_OVF_vect){
// nastavenie počiatočnej hodnoty počítadla
TCNT0 = 156;
i++;
}
//vlastná čakacia funkcia s využitím čítača/časovača 0
void delay_ms(unsigned int time){
TCNT0 = 156;
i=0;
// cakaj pokial prebehne i krat pretecenie
while(i != time*10);
}
int main(){
DDRD |= (1 << PD7); // PD7 ako výstupný TCCR0 |= (1 << CS01); // preddelicka /1024 (1us) TIMSK |= (1 << TOIE0); // prerušenie pri pretečení TCNT0 sei(); //povol globálne prerušenia while(1){ PORTD ^= (1 << PD7); //neguj PD7 // volanie nasej cakacej funkcie delay_ms(200); } return 0; }
Youtube video:
Príklad č.3:
Pomocou tlačidla privádzajte impulzy do externého vstupu T0 s reakciou na dobežnú hranu a napočítanú hodnotu (TCNT0) ,zobrazte pomocou 4 LED v binárnom tvare.
Zdrojový kód:
int main(){
DDRC |= 0xFF; // PORTC ako výstupný
DDRD &= ~(1 << PD4); // PD4 (T0) ako vstupný
PORTD |= (1 << PD4);// PD4 (T0) do log 1.
TCCR0 |= (1 << CS02) | (1 << CS01); //dobezna hrana na T0
while(1){
PORTC = TCNT0; // zobraz binárne napočítanú hodnotu
if(TCNT0 > 15) TCNT0 = 0; // iba spodne 4 bity
}
return 0;
}
Youtube video:
Čítač/časovač 1 obsahuje register TCNT1,do ktorého sa ukladá aktuálna hodnota čítača. Tento register je oproti TCNT0 16 bitový, čo znamená že počítadlo môže nadobúdať 65536 hodnôt. Znamená to teda ,že bude pretekať pomalšie ako 8 bitový čítač/časovač, čo je pohodlnejšie z hľadiska generovania dlhších časov.
Obsah registra TCNT1 môžeme inkrementovať buď vnútorne (priamo alebo cez preddeličku), alebo externe cez vstupný pin T1.
Ovládací register počítadla je TCCR1 je 16-bitový a preto je rozdelený na dva 8-bitové registre TCCR1A a TCCR1B.
Obsah počítadla možno porovnávať s dvoma registrami OCR1A a OCR1B ,ktoré sú tiež 16-bitové, čo sa využíva hlavne pri generovaní PWM signálu.
Čitač/časovač 1 má často využívanú funkciu uloženia stavu počítadla TCNT1 do registra ICR1 pri udalosti Input Capture. Táto udalosť môže byť vyvolaná príchodom impulzu na pin ICP1 (Input Capture Pin) alebo výstupom z interného komparátora. Táto funkcia sa využíva hlavne na meranie dĺžky periódy externého signálu, frekvencie alebo striedy signálu.
Popis registrov:
TCCR1A (Timer/Counter 1 control register B) – riadiaci register čítača/časovača 1
Slúži na nastavenie režimu PWM, funkcia tohto registra bude vysvetlená v ďalšej časti seriálu.
TCCR1B (Timer/Counter 1 control register B) – riadiaci register čítača/časovača 1
ICNC1 (Input capture noise canceller) – pri povolení (log. 1) zapne obmedzovač šumu na pine ICP1
ICES1 (Input Capture Edge Select) – nastaví na akú hranu vstupného signálu na pine ICP1 sa má vykonať zachytenie
aktuálneho obsahu TCNT1 do registra ICR1. Pri zápise log. 1 je to reakcia na nábežnú hranu pri log. 0 na dobežnú hranu
WGM13, WGM12 – slúži na nastavenie PWM módu
CS12,CS11,CS10 – výber zdroja hodin a preddeličky (viď tabuľka)
TIMSK (Timer/counter Interrupt Mask Register) – slúži na povolenie jednotlivých prerušení od čítača/časovača 1
TICIE1(Timer/counter 1 Input Capture Interrupt Enable) – povolenie prerušenia po príchode impulzu na pin ICP1
OCIE1A(Output compare A Interrupt Enable) – povolenie prerušenia pri zhode registra TCNT1 a OCR1A
OCIE1B (Output compare B Interrupt Enable) – povolenie prerušenia pri zhode registra TCNT1 a OCR1B
TOIE1 (Timer/counter 1 Overflow Interrupt Enable) – povolenie prerušenia pri pretečení alebo podtečený registra TCNT1
Príklad č.1:
Blikanie LED pomocou v intervale Ton=1s a Toff=1s ručným nastavením nastavením TCNT1 alebo využitím porovnávacieho registra OCR1A.
V programe sme zvolili preddeličku čítača/časovača 1 na hodnotu 256. To znamená ,že register TCNT1 sa bude inkrementovať za čas 32us. Keď chceme aby prerušenie na udalosť pretečenia TCNT1 nastalo za 1sec ,tak musí prísť 1s/32us = 31250 impulzov. Pretečenie nastane pri hodnote 65536 teda,keď maximálnu hodnotu čítača/časovača 1 (65535) inkrementujeme. V prerušení teda nastavíme TCNT1 na hodnotu 65536 – 31250 = 34286 a pretečenie nastane vždy za čas 1s.
Zdrojový kód:
// pretečenie registra TCNT1
ISR (TIMER1_OVF_vect){
// nastavenie počiatočnej hodnoty počítadla
TCNT1 = 34286;
//31250
PORTD ^= (1 << PD7); //neguj PD7
}
int main(){
DDRD |= (1 << PD7); // PD7 ako výstupný
TCCR1B |= (1 << CS12); //preddelička 256 (32us)
TIMSK |= (1 << TOIE1); // prerušenie pri pretečení TCNT1
OSCCAL = 0xA5; // nastavenie kalibracneho bajtu interneho RC oscilatora
sei(); // povol globalne prerušenia
while(1); // nekonečná slučka
return 0;
}
Youtube video:
Podobný efekt môžeme dosiahnuť aj nastavením porovnávacieho registra OCR1A, ktorý nastavíme na hodnotu na 31250 – 1 = 31249. Pri zhode TCNT1 a OCR1A nastane prerušenie. V našom prípade tiež v 1s intervale.
Zdrojový kód 2:
// pretečenie pri zhode registrov OCR1A a TCNT1
ISR (TIMER1_COMPA_vect){
TCNT1 = 0; // nuluj TCNT0
PORTD ^= (1 << PD7); //neguj PD7
}
int main(){
DDRD |= (1 << PD7); // PD7 ako výstupný
TCCR1B |= (1 << CS12); //preddelička 256 (32us)
TIMSK |= (1 << OCIE1A);// prerušenie pri zhode OCR1A a TCNT1
OCR1A = 31249; // TOP hodnota, pretečenie nastane pri hodnote 31250
OSCCAL = 0xA5; // nastavenie kalibracneho bajtu interneho RC oscilatora
sei(); // povol globalne prerušenia
while(1); // nekonečná slučka
return 0;
}
Príklad č.2
Realizujte meranie času pomocou ICP1 pinu. Dobu v sekundách medzi dvoma stlačeniami, tlačidla budu reprezentovať počet bliknutí LED.
volatile unsigned int namerane;
// akcia Input Capture
ISR (TIMER1_CAPT_vect){
namerane = TCNT1;
TCNT1 = 0;
}
int main(){
unsigned char blik;
DDRD |= (1 << PD7); // PD7 ako výstupný
DDRB &= ~(1 << PB0); // PB0 (ICP1) ako vstupny
PORTB |= (1 << PB0); //PB0 (ICP1) do log.1
TCCR1B |= (1 << CS12) | (1 << CS10); //preddelička 1024 (128us)
TIMSK |= (1 << TICIE1); // prerušenie na udalost Input Capture
OSCCAL = 0xA5; // nastavenie kalibracneho bajtu interneho RC oscilatora
sei(); // povol globalne prerušenia
while(1){ // nekonečná slučka
// ak sa stlačilo tlačidlo zablikaj LED
if(namerane) {
// 7812 je počet impulzov TCNT1 za čas 1s
for(blik=0;blik < (namerane/7812);blik++){
PORTD |= (1 << PD7);
_delay_ms(200);
PORTD &= ~(1 << PD7);
_delay_ms(200);
}
namerane = 0;
}
}
return 0;
}
Youtube video:
Príklad č.3:
Využitie čítača/časovača 0 a 1. Pomocou čítača/časovača 1 nastavte blikanie LED v intervale približne 0,5s. Čítač/časovač 0 využite na moduláciu signálu s periódou 64ms.
Zdrojový kód:
volatile unsigned char blikaj;
// pretečenie TCNT0 za čas 32ms
ISR (TIMER0_OVF_vect){
// ak blikaj tak blikaj :)
if(blikaj) PORTD ^= (1 << PD7);
}
// pretečenie TCNT0 za čas 0,524288s
ISR (TIMER1_OVF_vect){
blikaj ^= 1; // neguj premennu blikaj
}
int main(){
DDRD |= (1 << PD7); // PD7 ako výstupný
TCCR0 |= (1 << CS02) | (1 << CS00); //preddelička č/č 0 1024 (128us)
TCCR1B |= (1 << CS11) | (1 << CS10); //preddelička č/č 1 64 (8us)
TIMSK |= (1 << TOIE0) |(1 << TOIE1); // prerušenie na udalost Input Capture
OSCCAL = 0xA5; // nastavenie kalibracneho bajtu interneho RC oscilatora
sei(); // povol globalne prerušenia
while(1);
return 0;
}
Youtube video:
Aktuálny stav čítača/časovača 2 sa ukladá do 8 bitového registra TCNT2. Tento čítač/časovač 2 sa hlavne využíva ako RTC – zdroj reálneho času. Keď potrebujeme prerušenie na udalosť pretečenia TCNT2 každú sekundu tak preddeličku nastavíme na 128 a prerušenie nastane každú sekundu pretože 32,768kHz / (128 * 256) = 1s
Popis registrov :
TCCR2 (Timer/Counter 2 control register):
Bity CS22,CS21,CS20 slúžia na výber výber preddeličky. Ostatné bity slúžia na nastavenie PWM.
ASSR2 (Asynchronous Status Register):
AS2 (Asynchronous Timer/Counter 2) – pri zápise log. 0 na tento bit je čítač/časovač taktovaný z frekvencie hodín. Pri zápise log. 1 je taktovaný z externého kryštálu 32,768 kHz pripojeného na piny TOSC1 a TOSC2.
TIMSK (Timer/counter Interrupt Mask Register):
OCIE2 (Timer/counter 2 Output compare Interrupt) – povolenie prerušenia pri zhode registrov TCNT2 a OCR2
TOIE2 (Timer/counter 2 Overflow Interrupt ) – povolenie prerušenia pri pretečení registra TCNT2
Na záver sa chcem poďakovať všetkým, čo sa podielali na korektúre!
V ďalšej časti seriálu sa bližšie pozrieme na generovanie PWM a frekvencie pomocou čítača/časovača.
Prepáčte, ale pred zanechaním komentára sa musíte prihlásiť.
BIG LIKE Zawin
Mám problém zo zistením hodnoty kalibračnej konštanty. Používam programátor USB ASP ( http://www.fischl.de/usbasp/ ) a softvér eXtreme Burner – AVR 1.2 . Po načítaní parametrov z mikrokontroléra mi vypíše časovú konštantu 0x5b5b5b; aká hodnota to je? Mám ju celú zapísať do registra OSCCAL? Ďakujem.
Kalibračnú konštantu zistíš len pomocou ISP programátora.
Mam otazocku, predelička funguje aj pri externych signaloch alebo iba internych z oscilatora ?
tu som našiel chybu
TCCR1A (Timer/Counter 1 control register B)
Ahoj, předem chci poděkovat za super návod, na naučení plně dostačuje.
Jen jsem se nedočetl jak použít jeden časovač pro 2 PWM kanály.
Můžu poprosit o nějakou radu?
Předem díky.
Omlouvám se spletl jsem si článek. Ale článek o PWM jsem četl.
Omlouvám se za způsobené zmatky, problém byl mezi klávesnicí a židlí. Už jsem vše nalezl v textu …
Dobrý den, moc děkuji za článek.
Mám jeden dotaz, co kdybych chtěl aby přišlo přerušení každou hodinu nebo půl hodinu.. jak by se to řešilo?
a druhá otázka mimo .. (v jakých minimálních teplotách pracuje Atmega 8 ?)