Reklama

Čítač / Časovač 0 nejen na ATtiny – 2. Díl

Úvod

V tom díle budeme pokračovat s ukázkami práce s čítačem / časovačem nejen u mikrokontrolérů ATtiny.

Ukážeme si další tři vzorové realizace a to:

  • CTC režim – 1 sekunda pomocí přerušení (D)
  • CTC režim – generování frekvence (E)
  • CTC režim – generování fázově posunutých signálů (F)
CTC režim – 1 sekunda pomocí přerušení (D)

Časovač může pracovat v různých módech. Jeden z módů je CTC. Ten umožňuje měnit strop časovače. V něm můžete ovládat, do kolika časovač napočítá než “přeteče” – tedy než začne počítat od nuly. Pomocí bitů WGM02,WGM01 a WGM00 v registru TCCR0A volíte režim časovače (Tab. 1). Ve všech předchozích případech jsme používali režim “Normal”. V něm časovač počítá od nuly do 0xFF (strop), pak přeteče (a nastaví stavový bit TOV0) a zase počítá od nuly. V CTC módu je strop časovače určený registrem OCR0A. Časovač počítá jen do hodnoty v registru OCR0A a pak začne počítat zase od nuly. Může tedy počítat do jakékoli hodnoty mezi 0 – 255. Stavové bity TOV0,OCF0B i OCF0A se chovají stejně jako v režimu “Normal”. Jestliže tedy časovač napočítá do svého stropu (OCR0A), tak se nastaví stavový bit OCF0A. Podle něhož také poznáte, že časovač “přetekl”. Přirozeně si můžete také zapnout i přerušení od této události. Předvedeme si, jak s touto funkcí realizovat čas 1 s. Na tuto úlohu by byl sice vhodnější šestnáctibitový čítač / časovač 1, ale pro jednoduchost zůstaneme u čítače / časovače 0. Zvolíme předděličku kmitočtu osmi a časovač necháme počítat do hodnoty 250. Při kmitočtu 1 MHz bude jedno přetečení časovače trvat 8 x 250 tedy 2000 µs. Od přetečení si necháme volat přerušení a v něm budeme inkrementovat proměnnou. Jakmile dosáhne 500 uplyne čas 2000 µs x 500 = 1 s. Zde je na místě připomenout, že časovač počítá od nuly! Pokud mu zvolíte strop 1 bude muset napočítat dva cykly, než přeteče. Pokud má tedy počítat do 250 je mu potřeba zvolit strop na hodnotu 249. Komu to stále není jasné, spočítejte si prsty na ruce a začněte nulou:) Za poznámku stojí deklarace proměnné num. Deklarujeme ji s třídou static. Tím překladači říkáme, že si proměnná má uchovat svoji hodnotu i po skončení bloku (rutiny přerušení). Pokud bychom to neudělali, tak se skončením přerušení by skončila také platnost proměnné num a ztratili bychom její hodnotu. Přirozeně bychom mohli proměnnou definovat jako globální, ale pak bychom si zase zabrali název num i přes to, že tato proměnná nikde jinde potřeba nebude. Pro přehlednost kódu tedy doporučuji raději řešení pomocí static. Další finta, která by mohla čtenáře zmást je přepínání LED v rutině přerušení pomocí zápisu do registru PIND. Ten obecně slouží ke čtení vstupů, ale novější čipy rodiny AVR umožňují zápisem log. 1 na příslušnou pozici přepínat (toggle) hodnotu v registru PORTD. Analogicky lze postup použít i pro další piny.

Tab. 1: Režim čítače / časovače 0

Režim WGM02 WGM01 WGM00 Název režim Strop
0 0 0 0 Normal 0xFF
1 0 0 1 Phase Correct PWM 0xFF
2 0 1 0 CTC OCR0A
3 0 1 1 Fast PWM 0xFF
4 1 0 0 - -
5 1 0 1 Phase Correct PWM OCR0A
6 1 1 0 - -
7 1 1 1 Fast PWM OCR0A

 

 

001: // D) CTC režim – realizace 1s pomocí přerušení
002: #include <avr/io.h>
003: #include <avr/interrupt.h>
004:
005: ISR(TIMER0_COMPA_vect){
006: static int num = 0;
007: num++;// počítáme kolikrát přišlo přerušení
008: if(num >= 500){// pokud 500x, uplynula 1s
009: PIND |= (1<<PIND0);// přepni LED – “finta” nemusí fungovat u všech AVR !
010: num = 0;// a začneme počítat znovu
011: }
012: }
013:
014: int main(void){
015:
016: DDRD |= (1<<DDD0);// výstup (LED)
017: TCCR0A = (1<<WGM01);// CTC mód
018: OCR0A = 249;// strop časovače (počítá 250 pulzů)
019: TCCR0B = (1<<CS01);// clock časovače 1MHz/8
020: TIFR = (1<<OCF0A);// mažeme vlajku (kdo ví v jakém stavu byla)
021: TIMSK = (1<<OCIE0A);// zapínáme přerušení od OCR0A (tedy od přetečení časovače)
022: sei();// povolujeme globálně přerušení
023: while(1){
024: asm("nop");
025: }
026: }
Vzorové zdrojové kódy slouží pouze k jednoduché demonstraci funkce, proto často obsahují globální proměnné, neobsahují většinou ukazatele, dále také neobsahují kontroly proměnných, definování nevyužitých pinů mikrokontroléru a podobné správné programátorské návyky. Proto je nutné tyto kódy brát s patřičnou rezervou.
 
CTC režim – generování frekvence (E)

Čítač / časovač 0 může sám ovládat piny OC0A (PB2) a OC0B (PD5). V CTC režimu jde této vlastnosti využít ke generování frekvence (třeba “tónů”). Generování pak probíhá nezávisle na programu a frekvence je navázaná na hodinový takt mikrokontroléru (takže je signál bez “jitteru”). Čítači může přímo ovládat piny OC0A a OC0B. Ovládání se nasatvuje pomocí bitů COM0A0, COM0A1, COM0B0 a COM0B1 v registru TCCR0A. Každá dvojice bitů ovládá jeden z výstupů. Pokud jsou oba bity nulové, tak je pin OC0A (respektive OC0B) od čítače odpojen. Pokud COM0A0 nastavíte do stavu log. 1, tak bude pin čítače / časovače OC0A (PB2) přepínat s každou “compare” událostí. Analogicky to platí pro výstup OC0B (PD5), který se ovládá pomocí bitu COM0B0. Další možnosti (nastavitelné zbylými bity COM0A1 a COM0B1) si předvedeme v dalších příkladech. Jak už víte z minulého příkladu, tak čítač v režimu CTC počítá do hodnoty OCR0A a jakmile jí dosáhne, tak se vyvolá se “compare” událost a nastaví se bit OCF0A. Pokud je bit COM0A0 nastaven, tak se změní hodnota na pinu OC0A. Spolu s tím čítač přeteče a začíná počítat znovu. Hodnotou v registru OCR0A tedy nastavujete frekvenci přepínání pinu OC0A. Jednoduchým příkladem uplatnění je “pískátko”, které vidíte na Obr. 1. Výstup mikrokontroléru jsme zde posílili tranzistorem. Reproduktor může být v podstatě jakéhokoli typu. Pokud jde o to udělat, co největší rachot, tak je dobré volit frekvenci pískání blízko k rezonanční frekvenci reproduktoru. V našem příkladu použijeme běžný “buzzer”, což není nic jiného než miniaturní reproduktor (viz Obr. 2). Jeho rezonanční frekvence je přibližně 2000 Hz. My ho budeme budit frekvencí 1000 Hz. Perioda tedy bude 1 ms. Protože čítač při přetečení přepíná pin OC0A, potřebujeme, aby to provedl během jedné periody dvakrát. Musíme realizovat čas 500 µs. Zvolíme proto předěličku osmi a časovač necháme počítat do 63 (strop bude tedy 62). Vybereme režim CTC (WGM01 = 1) a bitem COM0A0 přidělíme čítači / časovači pin OC0A na kterém je připojen reproduktor. Pak už jen stačí pustit čítači / časovači hodiny (s předděličkou osmi). Program čeká na stisk tlačítka, poté spouští čítač / časovač a nechává ho generovat tón. Dále čeká na uvolnění tlačítka a pak vypíná reproduktor. Je potřeba si uvědomit, že pokud časovač vypnete (odpojíte mu hodiny), tak stále ještě bude ovládat pin OC0A a vy nemáte záruku, jestli na pinu skončila log. 1 nebo log. 0. Vzhledem k tomu, že má reproduktor v sepnutém stavu celkem velký odběr, je vhodné čítači také odebrat ovládání pinu OC0A (vynulováním bitů COM0A0 a COM0A1) a na PB2 (OC0A) zapsat stav log. 0.

 
Obr. 1: Elektronické scháma

Obr. 1: Elektronické scháma “pískatka”

 

 
Obr. 2: Realizace

Obr. 2: Realizace “pískatka” na kontaktním poli

 

 

001: // E) – generování tónu v režimu CTC s OCR0A
002: #include <avr/io.h>
003: #include <avr/interrupt.h>
004:
005: int main(void){
006: DDRD &= ~(1<<DDD4);// vstup pro tlačítko
007: DDRB |= (1<<DDB2);// bzučák
008: PORTB &= ~(1<<PORTB2);// vypínáme bzučák – nechceme aby do něj tekl
009: // proud dokud nepracuje
010:
011: while(1){
012: while(!(PIND & (1<<PIND4))){}// čekej dokud není stisknuto tlačítko
013: TCNT0 = 0;// vynuluj časovač (kdo ví co tam zbylo z "minule")
014: TCCR0A = (1<<COM0A0) | (1<<WGM01);
015: OCR0A = 62;
016: TCCR0B = (1<<CS01);// spouštíme časovač
017: while(PIND & (1<<PIND4)){}// čekej dokud není uvolněno tlačítko
018: TCCR0B = 0;// vypínáme časovač
019: // odebíráme mu možnost ovládat port
020: TCCR0A &= ~((1<<COM0A0) | (1<<COM0A1));
021: PORTB &= ~(1<<PORTB2);// vypínáme bzučák – ať nemá odběr
022: }
023: }
024:
Vzorové zdrojové kódy slouží pouze k jednoduché demonstraci funkce, proto často obsahují globální proměnné, neobsahují většinou ukazatele, dále také neobsahují kontroly proměnných, definování nevyužitých pinů mikrokontroléru a podobné správné programátorské návyky. Proto je nutné tyto kódy brát s patřičnou rezervou.
 
CTC režim – generování fázově posunutých signálů (F)

Zajímavou aplikací čítače / časovače v CTC režimu je možnost generovat fázově posunuté signály s volitelnou frekvencí. Rozsah je bohužel omezen pouze na fázový rozdíl 0 – 180°. Stačí nastavit oba piny OC0A a OC0B jako výstupy a přidělit je čítači (pomocí nastavení bitů COM0A0 a COM0B0). Dále musíme spustit čítač / časovač v režimu CTC (nastavením bitu WGM01), zvolit vhodnou předděličku a nastavit strop časovače (pomocí registru OCR0A). Registrem OCR0B se pak nastavuje fázový předstih signálu na pinu OC0B. Myšlenka je prostá. Čítač nejprve projde úrovní nastavenou v OCR0B a přepne výstup na pinu OC0B. Poté dopočítá do stropu (registr OCR0A) a přepne výstup OC0A a tak stále dokola. Rozdíl mezi OCR0B a OCR0A je časový posun mezi oběma signály. Hodnota v OCR0B musí být menší nebo rovna OCR0A, protože jinak nebude docházet k přepínání signálu OC0B (čítač do hodnoty OCR0B nikdy nedopočítá). Rozlišení takového generátoru závisí na stropu čítače. Čím je hodnota v registru OCR0A větší, tím více kroků fázového posunu (hodnot v registru OCR0B) máte k dispozici. Opět se pro tento účel může lépe hodit 16bitový čítač / časovač 1. Bohužel fázi nejde jednoduše ladit v rozsahu 360°. Je-li některý z pinů OC0A nebo OC0B přidělen čítači / časovači před startem, tak je příslušný výstup čítačem / časovačem vynulován. Nemůžete tedy bez peripetií nastartovat čítač / časovač s OC0A ve stavu log. 1 a OC0B ve stavu log. 0 nebo naopak. Jak na to uvidíte v příštím díle. A i když tyto problémy překonáte, tak pořád budete moci ladit fázový posun jen o 180° a to buď v intervalu 0 – 180° nebo 180 – 360°. V následujícím příkladu (zdrojový kód F1) necháme signál na pinu OC0B zpožděný o 20 µs za signálem na pinu OC0A. Při periodě 400 µs to tvoří přibližně 18°. V tomto nastavení lze zpozdit signál naOC0B maximálně o 199 µs, tedy o 179°. Fázový posuv tedy můžete měnit v rozsahu 0 – 179°. Zpoždění na pinu OC0B za pinem OC0A můžete spočítat jako rozdíl OCR0AOCR0B. Obecně pak fázový posuv můžete určit vztahem:
 

Posun = 360° / perioda x zpoždění

 

Když do něj dosadíte “registry” a zohledníte že perioda je 2 x OCR0A dostanete:
 

Posun = 180° / OCR0A x (OCR0AOCR0B)

 
Obr. 3: Generování fázově posunutých signálů (zdrojový kód F1)

Obr. 3: Generování fázově posunutých signálů (zdrojový kód F1)

 

 
Obr. 4: Generování fázově posunutých signálů s obrácenou polaritou OC0B (zdrojový kód F2)

Obr. 4: Generování fázově posunutých signálů s obrácenou polaritou OC0B (zdrojový kód F2)

 

Okomentovaný zdrojový kód

 

001: // F1) – generování fázově posunutých signálů v CTC režimu
002: #include <avr/io.h>
003:
004: int main(void){
005: DDRB = (1<<DDB2);// OC0A výstup
006: DDRD = (1<<DDD5);// OC0B výstup
007: // CTC mód + přidělení OC0A a OC0B čítači
008: TCCR0A = (1<<COM0A0) | (1<<COM0B0) | (1<<WGM01);
009: OCR0A = 200;// perioda čítače
010: OCR0B = 180;// fázové posunutí OC0B vůči OC0A
011: TCCR0B = (1<<CS00);// spouštíme čítač s clockem čipu (1MHz)
012:
013: while(1){
014: asm("nop");
015: }
016: }
Vzorové zdrojové kódy slouží pouze k jednoduché demonstraci funkce, proto často obsahují globální proměnné, neobsahují většinou ukazatele, dále také neobsahují kontroly proměnných, definování nevyužitých pinů mikrokontroléru a podobné správné programátorské návyky. Proto je nutné tyto kódy brát s patřičnou rezervou.
 

Potřebujete-li pracovat s fázovým posuvem v intervalu 180° – 360°, tak si musíte pomoci menší fintou a před začátkem generování signál na pinu OC0B invertovat. Detailnější popis uvidíte v příštím díle. Já jen ve zkratce řeknu, že čítač / časovač nejprve přepnete do režimu “Set on compare match”, pak vynutíte “compare” událost. Tím se interní signál pro pin OC0B přepne do stavu log. 1. Pak si vrátíte režim čítače / časovače na CTC a když ho teď spustíte, tak bude pin OC0A startovat ze stavu log. 0 a pin OC0B ze stavu log. 1. Tím docílíte posunutí OC0B signálu o dalších 180°. Změnou hodnoty registru OCR0B pak ladíte fázový posuv v rozsahu 180 – 359°. Fázový posun pak lze spočítat stejně jako v předchozím příkladě, jen stačí přičíst 180°.
 

Posun = 180° + 180° / OCR0A x (OCR0A – OCR0B)

 
 

001: // F2) – generování fázově posunutých signálů v CTC
002: // režimu s opačnou fází na OC0B
003: #include <avr/io.h>
004:
005: int main(void){
005: DDRB = (1<<DDB2);// OC0A výstup
007: DDRD = (1<<DDD5);// OC0B výstup
008: // čítač do režimu “Set on compare match” na kanále B
009: TCCR0A = (1<<COM0B1) | (1<<COM0B0);
009: TCCR0B |= (1<<FOC0B);// vynucení compare události – nastavuje interní
010: // hodnotu OC0B na log.1
011: // CTC mód + přidělení OC0A čítači
012: TCCR0A = (1<<COM0A0) | (1<<COM0B0) | (1<<WGM01);
013: OCR0A = 200;// perioda čítače
014: OCR0B = 180;// fázové posunutí OC0B vůči OC0A
015: TCCR0B = (1<<CS00);// spouštíme čítač
016:
017: while(1){
018: asm("nop");
019: }
020: }
021:
Vzorové zdrojové kódy slouží pouze k jednoduché demonstraci funkce, proto často obsahují globální proměnné, neobsahují většinou ukazatele, dále také neobsahují kontroly proměnných, definování nevyužitých pinů mikrokontroléru a podobné správné programátorské návyky. Proto je nutné tyto kódy brát s patřičnou rezervou.
 

 

 
 
Autor: Michal Dudka
 

[1] ATMEL. 8-bit Microcontroller with 2/4K Bytes In-System Programmable Flash ATtiny2313A ATtiny4313. [online] citováno 12. února 2017. Dostupné na www: http://www.atmel.com/images/doc8246.pdf
[2] ATMEL. AVR130: Setup and Use of AVR Timers. [online] citováno 12. února 2017. Dostupné na www: http://www.atmel.com/Images/Atmel-2505-Setup-and-Use-of-AVR-Timers_ApplicationNote_AVR130.pdf
[3] AVRFREAKS. Newbie’s Guide to AVR Timers. [online] citováno 12. února 2017. Dostupné na www: http://www.avrfreaks.net/forum/tut-c-newbies-guide-avr-timers?page=all

 

Jiné příspěvek v kategorii:

 
Čítač / Časovač 0 nejen na ATtiny – 2. Díl
Čítač / Časovač 0 nejen na ATtiny – 3. Díl

 
Tajned facebook
 

Za případné chyby v textu, ve zdrojovém kódě, nebo ve schématickém zapojení se omlouváme.
AUTOŘI NEBEROU ŽÁDNOU ODPOVĚDNOST ZA PŘÍPADNÉ ÚJMY NA ZDRAVÍ ČI MAJETKU.