Auf den folgenden Seiten findet Ihr ein kleines Xmega Tutorial – Timer/ Counter zu den Microcontrollern der Familie AVR Xmega in der Programmiersprache “C”. Bei den Xmega Controllern sind jedem I/O-Port mehrere Timer/Counter-Module zugeordnet. Diese können verwendet werden um einen Interrupt auszulösen oder um die Ausgänge der I/O-Pins anzusteuern. Der Name des jeweiligen TC-Moduls setzt sich aus dem I/O-Port, an dem er angeschlossen ist und der Zahl der verfügbaren Timer/Counter Module zusammen (TCxn). TC ist die Abkürzung für Timer/Counter, das X zeigt den Port an den angeschlossenen Port und n ist die T/C-Nummer innerhalb des PORTx (zB. ist TCD0 Timer/Counter 0 an PORTD). Timer sind jeweils an PORTC, PORTD, PORTE und PORTF verfügbar dieser Portx an dem Controller verfügbar ist.
Jeweils der Timer0 an jedem Port verfügt über 4 compare or capture channels. Der Timer1 verfügt über 2 compare or capture channels. Die Abbildung (rechts) zeigt wie die einzelnen Output Compare-Channels der verschiedenen Timer an dem jeweiligem I/O-Port angeschlossen sind. Generell sind die Ausgänge von Timer0 mit den Pins 0-3 innerhalb eines Portx verbunden, während die Ausgänge von Timer1 mit den Pins 4 und 5 verbunden sind.
Features
- True 16 bit operation
- Double buffered timer period setting
- 4 (2) compare or capture channels per timer
– 4 channels on each Timer 0
– 2 channels on each Timer 1
– Double buffered - 2 bit operation with 32 bit input capture by timer cascading
- Event counter
- Timer overflow and error interrupts (and events)
- Input capture interrupts (and events)
Das folgende Beispiel zeigt eine Möglichkeit wie man einen Timer verwendet, um ein Event in bestimmten Zeitintervallen auszulösen. Dafür wird der Timer so konfiguriert, dass er im Takt von 1Khz, also jede Milisekunde einen Interrupt auslöst. In dem Beispiel wird der Timer C0 verwendet. Um den am Timereingang anliegenden Systemtakt von 32Mhz in einen Takt von 1Khz zu skalieren wird dieser mit Hilfe des Prescalers durch 1024 geteilt. Dieser Teiler muss mit der entsprechenden Codierung in das “Control A Register” des verwendeten Timers geschrieben werden. Die Codierung kann man dem Datenblatt oder der folgenden Tabelle entnehmen.
CLKSEL[3:0] 0000 0001 0010 0011 0100 0101 0110 0111 1nnn | Group Configuration OFF DIV1 DIV2 DIV4 DIV8 DIV64 DIV256 DIV1024 EVCHn | Description None (i.e, timer/counter in OFF state) Prescaler: Clk Prescaler: Clk/2 Prescaler: Clk/4 Prescaler: Clk/8 Prescaler: Clk/64 Prescaler: Clk/256 Prescaler: Clk/1024 Event channel n, n= [0,…,7] |
Mit der Wahl des entsprechenden Prescalers wurde der Timer gleichzeitig aktiviert. Jetzt muss nur noch der Topwert für den Timer berechnet und in das Periodenregister geschrieben werden. Bei einem CPU-Takt von 32Mhz, einem Prescaler von 1024 und einem gewünschten Eventintervall von 1Khz kann man den Wert wie folgt berechnen: Topwert = 65535 – (CPU_Takt/ Eventintervall/ Prescaler). Danach wird der Betriebsmodi des Timers durch das “Control B Register” ausgewählt. Für einen normalen Event-Timer wählt man den Modus “Normal Mode”. Momentan zählt der Timer schon im Hintergrund und würde bei jedem Überlauf, beim Erreichen des Periodenregister ein Event auslösen. Diesem Event muss aber erst noch eine Priorität zugeordnet werden, damit es verarbeitet wird. Dazu wird im “Interrupt Enable Register A” die entsprechende Codierung eingetragen. Für das Beispiel wird die höchste Priorität verwendet.
Der Timer ist jetzt komplett konfiguriert, so dass nur noch die Eventroutine “ISR(TCC0_OVF_vect)” für den Timer geschrieben werden muss. Diese Routine wird jede Millisekunde aufgerufen und dekrementiert unsere Timervariable. Wenn die Variable Null wird, wird die LED in der Main-Schleife getoggelt und die Variable wieder auf 500ms gesetzt. Fertig!Tutorial Xmega – Timer/ Counter
//################# Variablen volatile int timer_blink = 500; // timer für echtzeit ausführung //######################################################## initialisierung void initialisierung(void) { cli(); // Interrupts deaktivieren //################# Timer0 - 1Khz TCC0.CTRLA = TC_CLKSEL_DIV1024_gc; // Presacler 1024 TCC0.CTRLB = 0x00; // select Modus: Normal TCC0.PER = 32; // Zähler Top-Wert 1000 Hz TCC0.CNT = 0x00; // Zähler zurücksetzen TCC0.INTCTRLA = 0b00000011; // Interrupt Highlevel //################# Interrupts High-,Medium- und Lowlevel freigeben PMIC.CTRL |= PMIC_HILVLEN_bm |PMIC_MEDLVLEN_bm|PMIC_LOLVLEN_bm; sei(); // Interrupts aktivieren } //######################################################## Int TimerC0 1Khz ISR(TCC0_OVF_vect) { timer_blink--; if (timer_blink <0) {timer_blink= 0;} // Echtzeitablauf } //######################################################## Main int main (void) { initialisierung(); while(1) { //#################### while start if (timer_blink <= 0) { /* zB. toggle Pin --> LED*/ timer_blink=500; } //#################### while end } }
Hinweis:
Das
Tutorial wird nach und nach von mir ergänzt. Wenn ihr Fehler in dem
Tutorial findet, würde ich mich freuen wenn ihr mir diese mitteilt,
damit ich diese korrigieren kann. Wenn jemand eigene Tips, Informationen
oder Code-Schnipsel hat kann er mir diese gern zusenden, damit ich sie
anschließend in das Tutorial einbinden kann.
Links | |
Atmel Appnote Xmega Timer | AVR1306: Using the XMEGA Timer/Counter (pdf) AVR1306: Using the XMEGA Timer/Counter (Software) |