diyundso.de

16-bit Timer AVR-GCC

Der wesentliche Unterschied zwischen einem 16- und 8-Bit-Timer am AVR ist der, dass man selbst festlegen kann, bei welchem Zählerstand ein Interrupt ausgelöst wird (beim erreichen des Vergleichswerts). Beim 8-Bit-Timer geschieht dies immer nur bei einem Overflow (alle 256 Zählertakte).

Das ist von Vorteil, wenn man z.B. einen Zeitgeber benötigt. Durch die wählbarkeit des Vergleichswertes lässt sich dadurch z.B. ein 4 MHz-Quarz verwenden. Zur Berechnung des benötigten Vergleichswertes: siehe unten

Die 16-Bit-Timer verfügen über zwei "Kanäle", nämlich A und B. Beim Auslösen des Interrupts kann man den Zähler zurücksetzen von Kanal A zurücksetzen lassen. Kanal B kann dies nicht.

Somit wird Interrupt B immer im selben Takt ausgelöst wie Kanal A. Der Vergleichswert von Kanal B muss folglich kleiner als der von Kanal A sein.

Der Vergleichswert von Kanal B legt also quasi die Verzögerung zwischen dem ersten auslösen von Interrupt B und Interrupt A fest. Interrupt B wird immer zuerst ausgelöst.


Der Zeitliche Ablauf sieht wie folt aus (Vergleichswert x steht für eine Verzögerung von x Zählertakten):
Vergleichswert B
-> Interrupt B
Vergleichswert (A-B)
-> Interrupt A

...usw.

Benutzung des 16-Bit-Timers x

Festlegen des Zählertaktes:
TCCRxB = 0x01; //CPU-Takt
TCCRxB = 0x02; //CPU-Takt/8
TCCRxB = 0x03; //CPU-Takt/64
TCCRxB = 0x04; //CPU-Takt/256
TCCRxB = 0x05; //CPU-Takt/1024

Zurücksetzen des Registers TCCRxA:
TCCRxA = 0x00; //Register zuruecksetzen

Beim erreichen des Vergleichswerts lassen sich zusätzliche Aktionen mit den Pins OCxA bzw. OCxB festlegen:
//Zusaetzliche Moeglichkeiten Kanal A:

TCCRxA |= (1<<COMxA0); //Pin OC1A toggeln
TCCRxA |= (1<<COMxA1); //Pin OC1A auf LOW
TCCRxA |= ((1<<COMxA1)|(1<<COMxA0)); //OC1A auf HIGH


//zusaetzliche Moeglichkeiten Kanal B:

TCCRxA |= (1<<COMxB0); //Pin OC1B toggeln
TCCRxA |= (1<<COMxB1); //Pin OC1B auf LOW
TCCRxA |= ((1<<COMxB1)|(1<<COMxB0)); //OC1B auf HIGH setzen

Zählregister sollen beim Erreichen des Vergleichswertes von Kanal A wieder auf 0 gesetzt werden:
TCCRxB |= (1<<WGM12); //Zuruecksetzen des Counters aktivieren

Vergleichswerte für Kanal A und B setzen:
OCRxA = 9999;
OCRxB = 4999;


//oder schreiben der Werte in 8-Bit Register:

uint16_t vglA = 62000;
OCRxAH = (vglA>>8);
OCRxAL = (vglA & 0x00FF);

uint16_t vglB = 31000;
OCRxBH = (vglB>>8);
OCRxBL = (vglB & 0x00FF);

Zuletzt müssen noch die Interrupts aktiviert werden:
TIMSK |= (1<<OCIExA); //Interrupt an Kanal A aktivieren
TIMSK |= (1<<OCIExB); //Interrupt an Kanal B aktivieren

sei(); //Interrupts global aktivieren

Der Code für die Interrupts kann dann in folgenden Interrupt-Routinen ausgeführt werden:
ISR(TIMERx_COMPA_vect){

//Code Kanal A Timer x

}

ISR(TIMERx_COMPB_vect){

//Code Kanal B Timer x

}

Beispielcode für Timer 1

#include <avr/io.h>
#include <avr/interrupt.h>


void start_timer(void){

//TCCR1B = 0x01; //CPU-Takt
//TCCR1B = 0x02; //CPU-Takt/8
TCCR1B = 0x03; //CPU-Takt/64
//TCCR1B = 0x04; //CPU-Takt/256
//TCCR1B = 0x05; //CPU-Takt/1024

TCCR1A = 0x00; //Register zuruecksetzen

TCCR1B |= (1<<WGM12); //Zuruecksetzen des Counters aktivieren

uint16_t vglA = 62499; //Vergleichswert A
OCR1AH = (vglA>>8);
OCR1AL = (vglA & 0x00FF);

uint16_t vglB = 31000; //Vergleichswert B
OCR1BH = (vglB>>8);
OCR1BL = (vglB & 0x00FF);

TIMSK |= (1<<OCIE1A); //Interrupt an Kanal A aktivieren
TIMSK |= (1<<OCIE1B); //Interrupt an Kanal B aktivieren

sei(); //Interrupts global aktivieren

}

ISR(TIMER1_COMPA_vect){

//Code Kanal A Timer 1

}

ISR(TIMER1_COMPB_vect){

//Code Kanal B Timer 1

}

Vergleichswert auswählen

Für die Dauer zwischen zwei Interrupts gilt: (Vorteiler*(Vergleichswert+1))/CPU-Takt

Das heißt, für eine Periodendauer x gilt:
Vergleichswert = ((CPU-Takt * x) / Vorteiler) -1
Der Vergleichswert muss kleiner gleich 65535 sein, um in ein 16-Bit-Unsigned-Integer zu passen.
Impressum

Valid HTML 4.01 Transitional