Počítadlá a časovače AVR: Rozdiel medzi revíziami
Zo stránky SensorWiki
(33 medziľahlých úprav od rovnakého používateľa nie je zobrazených.) | |||
Riadok 1: | Riadok 1: | ||
[[Súbor:MechanicalCounter.jpg|250px|right]] | |||
'''Pozn:.''' Tu si môžete stiahnuť novú verziu LCD knižnice http://senzor.robotika.sk/mmp/src/ | |||
== 16-bitové počítadlo a časovač T1 s prerušením == | == 16-bitové počítadlo a časovač T1 s prerušením == | ||
Riadok 5: | Riadok 12: | ||
V tejto úlohe sa predpokladá znalosť funkcie časovača T1 z prednášky (datasheet, str.114 -- 141). | V tejto úlohe sa predpokladá znalosť funkcie časovača T1 z prednášky (datasheet, str.114 -- 141). | ||
Procesor je nakonfigurovaný na prácu s externým kryštálovým oscilátorom 16,000 MHz. | Procesor je nakonfigurovaný na prácu s externým kryštálovým oscilátorom 16,000 MHz. | ||
<div style='text-align: center;'> | |||
<HTML> | |||
<IFRAME Src="https://senzor.robotika.sk/mmp/anim/counter1.html" width="750" height="450" style="border:none;"></IFRAME> | |||
<!-- A HREF="http://senzor.robotika.sk/mmp/counter/index.html">Simulátor počítadla</A --> | |||
<BR><A HREF="https://senzor.robotika.sk/mmp/anim/counter1.html">Simulátor počítadla</A> | |||
</HTML> | |||
</div> | |||
Riadok 10: | Riadok 27: | ||
K vývojovej doske | K vývojovej doske pripojíme tlačítko na vstup PD5 (Arduino D5). Schémy zapojenia tu nebudeme opakovať, použijete tie z minulého cvičenia. | ||
Ukážeme si, ako treba nakonfigurovať počítadlo T1, aby registrovalo počet stlačení tohoto tlačítka. Okrem tlačítka (čo nemá veľký praktický význam) môžeme počítať napr. počet impulzov z nejakého snímača za pevnú periódu a tým zistiť frekvenciu, alebo počítať kroky z inkrementálneho snímača a zistiť tak polohu pohonu, alebo počítať napr. počet výrobkov, ktoré prepadli cez optickú závoru. | Ukážeme si, ako treba nakonfigurovať počítadlo T1, aby registrovalo počet stlačení tohoto tlačítka. Okrem tlačítka (čo nemá veľký praktický význam) môžeme počítať napr. počet impulzov z nejakého snímača za pevnú periódu a tým zistiť frekvenciu, alebo počítať kroky z inkrementálneho snímača a zistiť tak polohu pohonu, alebo počítať napr. počet výrobkov, ktoré prepadli cez optickú závoru. | ||
Zároveň budeme tlačítkom ovládať zabudovanú LED diódu aby ste videli, že vstupy sú skutočne multifunkčné. | Zároveň budeme tlačítkom ovládať zabudovanú LED diódu aby ste videli, že vstupy sú skutočne multifunkčné. | ||
Okrem toho si zobrazíme aktuálny stav tlačítka (0/1) a stav počítadla TCNT1 v hexadecimálnom tvare. | |||
Na prvom obrázku je časť vnútornej štruktúry počítadla a časovača T1. Ako vidno, konfigurácia do režimu počítania impulzov z externého vstupu spočíva len v nastavení príslušných bitov v registri TCCR1B. Ostatné bity a konfiguračné registre si zatiaľ nebudeme všímať. | Na prvom obrázku je časť vnútornej štruktúry počítadla a časovača T1. Ako vidno, konfigurácia do režimu počítania impulzov z externého vstupu spočíva len v nastavení príslušných bitov v registri TCCR1B. Ostatné bity a konfiguračné registre si zatiaľ nebudeme všímať. | ||
Riadok 27: | Riadok 44: | ||
<tabs> | <tabs> | ||
<tab name=" | <tab name="Verzia LCD"><source lang="c"> | ||
#define F_CPU 16000000UL | #define F_CPU 16000000UL | ||
Riadok 46: | Riadok 63: | ||
DDRD &= ~(1<<SW1); // PORTD: SW2 (PD5) input | DDRD &= ~(1<<SW1); // PORTD: SW2 (PD5) input | ||
PORTD |= (1<<SW1); // pull-up ON | PORTD |= (1<<SW1); // pull-up ON | ||
TCNT1 = 0x0000; // initialize (CLEAR) counter | |||
TCCR1B = 0b00000111; // T1 clk = external clock source on pin T1, rising edge | TCCR1B = 0b00000111; // T1 clk = external clock source on pin T1, rising edge | ||
Riadok 53: | Riadok 70: | ||
lcd_init(); | lcd_init(); | ||
lcd_puts("-Button counter- | lcd_puts("-Button counter-"); | ||
while(1) | while(1) | ||
Riadok 66: | Riadok 83: | ||
PORTB |= (1<<LED1); } | PORTB |= (1<<LED1); } | ||
lcd_command(0xC0 + 0); // a vrátime kurzor na začiatok 2. riadku (0b1000 0000 + 40 + 0) | |||
sprintf(riadok,"D5: %d TCNT: %04X",value,TCNT1); // vytvoríme kombinovaný text | sprintf(riadok,"D5: %d TCNT: %04X",value,TCNT1); // vytvoríme kombinovaný text | ||
lcd_puts(riadok); // zobrazíme ho na displeji | lcd_puts(riadok); // zobrazíme ho na displeji | ||
} | } | ||
Riadok 76: | Riadok 93: | ||
} | } | ||
</source> | |||
</source></tab> | |||
<tab name="Verzia UART"><source lang="c"> | |||
#define BAUD 9600 | |||
#include <avr/io.h> | |||
#include <stdio.h> | |||
#include "uart.h" | |||
#define LED1 PB5 // internal LED | |||
#define SW1 PD5 // pushbutton on PD5 (Arduino D5) | |||
FILE mystdout = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE); | |||
int main(void) | |||
{ | |||
int value = 0; | |||
DDRB |= (1<<LED1); // PORTB: LED1 na PB5 je output | |||
DDRD &= ~(1<<SW1); // PORTD: SW2 (PD5) input | |||
PORTD |= (1<<SW1); // pull-up ON | |||
TCNT1 = 0x0000; // initialize (CLEAR) counter | |||
TCCR1B = 0b00000111; // T1 clk = external clock source on pin T1, rising edge | |||
uart_init(); // Inicializacia seriovej linky | |||
stdout = &mystdout; // Odteraz funguje printf(); | |||
printf("\n-Button counter-\n\n"); | |||
while(1) | |||
{ | |||
/* test the switch first */ | |||
if ( PIND&(1<<SW1) ) | |||
{ value = 1; | |||
PORTB &= !(1<<LED1); } | |||
else | |||
{ value = 0; | |||
PORTB |= (1<<LED1); } | |||
printf("D5: %d TCNT: %04X\r",value,TCNT1); // vytvoríme kombinovaný text | |||
} | |||
return(0); | |||
} | |||
</source></tab> | |||
</tabs> | |||
'''Úloha:''' pripočíta počítadlo naozaj vždy len jeden impulz pri stlačení tlačítka? Ak áno, prečo? Ak nie, prečo? | |||
== Overflow / pretečenie == | |||
Aby sme nemuseli stlačiť tlačidlo 65 535 krát, trocha si to uľahčíme. Nasledovný výsek programu nastaví počítadlo na 5 impulzov pred pretečení, t.j. (0xFFFF - 5) a potom sleduje v hlavnej slučke, kedy nastane pretečenie. Počítadlo stále počíta impulzy, ktoré mu dávame externým signálom na T1, čiže tlačítkom. | |||
Pokúsime sa program zmeniť tak, aby namiesto zobrazenia stavu pinu <code>D5</code> zobrazoval stav príznaku pretečenia počítadla <code>TOV1</code> (Timer1 Overflow) v registri <code>TIFR1</code>. | |||
<tabs> | |||
<tab name="AVR-GCC"><source lang="c"> | |||
/* tento riadok treba pridať do inicializačnej časti programu */ | |||
TCNT1 = 0xFFFA; // initialize (CLEAR) counter | |||
/* a tento kus programu zaradiť do hlavnej slučky while(1) */ | |||
/* test the overflow bit */ | |||
if ( (TIFR1 & 0x01) == 0x01) // If the overflow flag is set | |||
{ | |||
TCNT1 = 0x????; // Restart T/C1 - reload | |||
TIFR1 = 0x01; // Clear the overflow flag | |||
} | |||
/* a napokon vymeniť riadok s výpisom za nasledovný */ | |||
printf("TO: %d TCNT: %04X\r",TIFR1,TCNT1); // zobrazíme TOF a TCNT | |||
</source></tab> | </source></tab> | ||
</tabs> | </tabs> | ||
'''Úloha:''' Čo sa stane, ak príznak pretečenia nevynulujete? Prečo? | |||
=== Časovač T1 === | === Časovač T1 === | ||
Zdroj hodín a preddelička je nakreslená na nasledovnom obrázku | |||
<div style='text-align: center;'> | |||
[[Súbor:AVR_Timer_ClockSource.png]]<BR> | |||
''Voľba vstupov pre počítadlo.'' | |||
</div> | |||
Ako vidno z obrázku s vnútornou štruktúrou časovača, prechod z režimu počítania do časovania je opäť možný jednoduchou zmenou posledných troch bitov v registri TCCR1B. | |||
Skúste zmeniť nasledovný riadok v predošlom programe tak, aby zdrojom hodín časovača bol oscilátor procesora. Ak chceme sledovať zmeny voľným okom, musíme frekvenciu | |||
oscilátora 16 MHz znížiť preddeličkou na čo najnižšiu hodnotu. | |||
<source lang="c"> | |||
TCCR1B = 0b00000101; // T1 clk = internal clock source, prescaler 1:1024 | |||
</source> | |||
Overte, že tlačidlo aj naďalej bude fungovať, ale už nemá žiaden vplyv na stav počítadla TCNT1. | |||
Teraz už máme pripravené všetko potrebné na to, aby sme vedeli riadiť frekvenciu blikania LED diódy pomocou časovača T1. | |||
V hlavnej programovej slučke budeme testovať príznak pretečenia TOV1 a v prípade, že časovač pretečie, tak zmeníme stav | |||
LED diódy. Aby sa to opakovalo a táto situácia nenastala len raz, znova naplníme register TCNT1 inicializačnou hodnotou a vynulujeme príznak pretečenia (ten sa nevynuluje sám od seba, ale robí sa to pomerne neintuitívne tak, že do registra TIFR1 zapíšeme na pozíciu TOV1 log. 1 - čiže ako by sme ten príznak prepísaním jednotkou vymazali). | |||
<source lang="C"> | |||
if ( (TIFR1 & (1<<TOV1)) == 0x01 ) | |||
{ | |||
PORTB = PORTB ^ (1<<LED1); // toggle LED1 | |||
TCNT1 = 0x8000; // initialize counter napr. 8000 hex | |||
TIFR1 = (1<<TOV1); // zapisom jednotky vynulujem priznak Timer Overflow | |||
} | |||
/* znova si zobrazime aktualne hodnoty */ | |||
printf("TF: %d TCNT: %04X\r",(TIFR1 & (1<<TOV1)),TCNT1); | |||
</source> | |||
Čo sa zmení, ak príznak pretečenia nevynulujete? Ak sa vám nezobrazí správna hodnota príznaku, upravte program tak, aby to fungovalo správne. | |||
'''Úlohy''' | '''Úlohy''' | ||
<!-- | |||
# Program doplňte podľa pokynov asistenta a odsimulujte.<BR>Pozn.: Treba vypnúť optimalizáciu! (Project/Project options [[Médiá:AVRstudioOptimize0.png|Pozri obr.]]) | # Program doplňte podľa pokynov asistenta a odsimulujte.<BR>Pozn.: Treba vypnúť optimalizáciu! (Project/Project options [[Médiá:AVRstudioOptimize0.png|Pozri obr.]]) | ||
# | --> | ||
# Vypočítajte správnu hodnotu TCNT1 tak, aby LED blikala s frekvenciou 1 s. Overte! | |||
<FONT Size="+2">Cvičenie pokračuje [[Generovanie tónov|druhou časťou...]]<BR><BR><BR></FONT> | |||
== Informácie == | |||
Literatúra | '''Literatúra''' | ||
* [http://ww1.microchip.com/downloads/en/AppNotes/Atmel-2505-Setup-and-Use-of-AVR-Timers_ApplicationNote_AVR130.pdf AVR130: Setup and Use of AVR Timers] Atmel Application Note. 2016 | |||
* [http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=50106 Newbie's Guide to AVR Timers] | * [http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=50106 Newbie's Guide to AVR Timers] | ||
* [http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=55347 The traps when using interrupts] | * [http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=55347 The traps when using interrupts] | ||
* [http:// | * [http://senzor.robotika.sk/mmp/AVR-C-Timers.pdf Ako používať časovače v AVR C] | ||
* [http:// | * [http://senzor.robotika.sk/mmp/PreruseniaAVR.pdf Prednáška o prerušeniach] (Ing. Chamraz) | ||
'''Odkazy''' | |||
* Knižnica pre Arduino: http://www.arduino.cc/playground/Code/Timer1 | * Knižnica pre Arduino: http://www.arduino.cc/playground/Code/Timer1 |
Aktuálna revízia z 11:45, 14. marec 2024
Pozn:. Tu si môžete stiahnuť novú verziu LCD knižnice http://senzor.robotika.sk/mmp/src/
16-bitové počítadlo a časovač T1 s prerušením
Máte k dispozícii vývojovú dosku Arduino s procesorom ATmega328P (datasheet).
V tejto úlohe sa predpokladá znalosť funkcie časovača T1 z prednášky (datasheet, str.114 -- 141). Procesor je nakonfigurovaný na prácu s externým kryštálovým oscilátorom 16,000 MHz.
Počítadlo T1
K vývojovej doske pripojíme tlačítko na vstup PD5 (Arduino D5). Schémy zapojenia tu nebudeme opakovať, použijete tie z minulého cvičenia.
Ukážeme si, ako treba nakonfigurovať počítadlo T1, aby registrovalo počet stlačení tohoto tlačítka. Okrem tlačítka (čo nemá veľký praktický význam) môžeme počítať napr. počet impulzov z nejakého snímača za pevnú periódu a tým zistiť frekvenciu, alebo počítať kroky z inkrementálneho snímača a zistiť tak polohu pohonu, alebo počítať napr. počet výrobkov, ktoré prepadli cez optickú závoru. Zároveň budeme tlačítkom ovládať zabudovanú LED diódu aby ste videli, že vstupy sú skutočne multifunkčné.
Okrem toho si zobrazíme aktuálny stav tlačítka (0/1) a stav počítadla TCNT1 v hexadecimálnom tvare.
Na prvom obrázku je časť vnútornej štruktúry počítadla a časovača T1. Ako vidno, konfigurácia do režimu počítania impulzov z externého vstupu spočíva len v nastavení príslušných bitov v registri TCCR1B. Ostatné bity a konfiguračné registre si zatiaľ nebudeme všímať.
Na druhom obrázku vidno, že nastavenie sa vykoná spodnými troma bitmi registra TCCR1B. Žiadne ďalšie nastavenie v tejto chvíli nie je potrebné. Môžeme však pre istotu vynulovať aj register počítadla TCNT1.
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include "lcd_ch.h" // using our LCD library
#define LED1 PB5 // internal LED
#define SW1 PD5 // pushbutton on PD5 (Arduino D5)
int main(void)
{
char riadok[]= {" "};
int value = 0;
DDRB |= (1<<LED1); // PORTB: LED1 na PB5 je output
DDRD &= ~(1<<SW1); // PORTD: SW2 (PD5) input
PORTD |= (1<<SW1); // pull-up ON
TCNT1 = 0x0000; // initialize (CLEAR) counter
TCCR1B = 0b00000111; // T1 clk = external clock source on pin T1, rising edge
/* initialize LCD display */
ini_ports();
lcd_init();
lcd_puts("-Button counter-");
while(1)
{
/* test the switch first */
if ( PIND&(1<<SW1) )
{ value = 1;
PORTB &= !(1<<LED1); }
else
{ value = 0;
PORTB |= (1<<LED1); }
lcd_command(0xC0 + 0); // a vrátime kurzor na začiatok 2. riadku (0b1000 0000 + 40 + 0)
sprintf(riadok,"D5: %d TCNT: %04X",value,TCNT1); // vytvoríme kombinovaný text
lcd_puts(riadok); // zobrazíme ho na displeji
}
return(0);
}
#define BAUD 9600
#include <avr/io.h>
#include <stdio.h>
#include "uart.h"
#define LED1 PB5 // internal LED
#define SW1 PD5 // pushbutton on PD5 (Arduino D5)
FILE mystdout = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE);
int main(void)
{
int value = 0;
DDRB |= (1<<LED1); // PORTB: LED1 na PB5 je output
DDRD &= ~(1<<SW1); // PORTD: SW2 (PD5) input
PORTD |= (1<<SW1); // pull-up ON
TCNT1 = 0x0000; // initialize (CLEAR) counter
TCCR1B = 0b00000111; // T1 clk = external clock source on pin T1, rising edge
uart_init(); // Inicializacia seriovej linky
stdout = &mystdout; // Odteraz funguje printf();
printf("\n-Button counter-\n\n");
while(1)
{
/* test the switch first */
if ( PIND&(1<<SW1) )
{ value = 1;
PORTB &= !(1<<LED1); }
else
{ value = 0;
PORTB |= (1<<LED1); }
printf("D5: %d TCNT: %04X\r",value,TCNT1); // vytvoríme kombinovaný text
}
return(0);
}
Úloha: pripočíta počítadlo naozaj vždy len jeden impulz pri stlačení tlačítka? Ak áno, prečo? Ak nie, prečo?
Overflow / pretečenie
Aby sme nemuseli stlačiť tlačidlo 65 535 krát, trocha si to uľahčíme. Nasledovný výsek programu nastaví počítadlo na 5 impulzov pred pretečení, t.j. (0xFFFF - 5) a potom sleduje v hlavnej slučke, kedy nastane pretečenie. Počítadlo stále počíta impulzy, ktoré mu dávame externým signálom na T1, čiže tlačítkom.
Pokúsime sa program zmeniť tak, aby namiesto zobrazenia stavu pinu D5
zobrazoval stav príznaku pretečenia počítadla TOV1
(Timer1 Overflow) v registri TIFR1
.
/* tento riadok treba pridať do inicializačnej časti programu */
TCNT1 = 0xFFFA; // initialize (CLEAR) counter
/* a tento kus programu zaradiť do hlavnej slučky while(1) */
/* test the overflow bit */
if ( (TIFR1 & 0x01) == 0x01) // If the overflow flag is set
{
TCNT1 = 0x????; // Restart T/C1 - reload
TIFR1 = 0x01; // Clear the overflow flag
}
/* a napokon vymeniť riadok s výpisom za nasledovný */
printf("TO: %d TCNT: %04X\r",TIFR1,TCNT1); // zobrazíme TOF a TCNT
Úloha: Čo sa stane, ak príznak pretečenia nevynulujete? Prečo?
Časovač T1
Zdroj hodín a preddelička je nakreslená na nasledovnom obrázku
Ako vidno z obrázku s vnútornou štruktúrou časovača, prechod z režimu počítania do časovania je opäť možný jednoduchou zmenou posledných troch bitov v registri TCCR1B.
Skúste zmeniť nasledovný riadok v predošlom programe tak, aby zdrojom hodín časovača bol oscilátor procesora. Ak chceme sledovať zmeny voľným okom, musíme frekvenciu
oscilátora 16 MHz znížiť preddeličkou na čo najnižšiu hodnotu.
TCCR1B = 0b00000101; // T1 clk = internal clock source, prescaler 1:1024
Overte, že tlačidlo aj naďalej bude fungovať, ale už nemá žiaden vplyv na stav počítadla TCNT1.
Teraz už máme pripravené všetko potrebné na to, aby sme vedeli riadiť frekvenciu blikania LED diódy pomocou časovača T1.
V hlavnej programovej slučke budeme testovať príznak pretečenia TOV1 a v prípade, že časovač pretečie, tak zmeníme stav
LED diódy. Aby sa to opakovalo a táto situácia nenastala len raz, znova naplníme register TCNT1 inicializačnou hodnotou a vynulujeme príznak pretečenia (ten sa nevynuluje sám od seba, ale robí sa to pomerne neintuitívne tak, že do registra TIFR1 zapíšeme na pozíciu TOV1 log. 1 - čiže ako by sme ten príznak prepísaním jednotkou vymazali).
if ( (TIFR1 & (1<<TOV1)) == 0x01 )
{
PORTB = PORTB ^ (1<<LED1); // toggle LED1
TCNT1 = 0x8000; // initialize counter napr. 8000 hex
TIFR1 = (1<<TOV1); // zapisom jednotky vynulujem priznak Timer Overflow
}
/* znova si zobrazime aktualne hodnoty */
printf("TF: %d TCNT: %04X\r",(TIFR1 & (1<<TOV1)),TCNT1);
Čo sa zmení, ak príznak pretečenia nevynulujete? Ak sa vám nezobrazí správna hodnota príznaku, upravte program tak, aby to fungovalo správne.
Úlohy
- Vypočítajte správnu hodnotu TCNT1 tak, aby LED blikala s frekvenciou 1 s. Overte!
Cvičenie pokračuje druhou časťou...
Informácie
Literatúra
- AVR130: Setup and Use of AVR Timers Atmel Application Note. 2016
- Newbie's Guide to AVR Timers
- The traps when using interrupts
- Ako používať časovače v AVR C
- Prednáška o prerušeniach (Ing. Chamraz)
Odkazy
- Knižnica pre Arduino: http://www.arduino.cc/playground/Code/Timer1