Die Timer/Counter des AVR

Die heutigen Microcontroller und insbesondere die RISC-AVR's sind für viele Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED oder Lampe blinken lassen wollen können wir selbstverständlich nicht die CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.

Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann auch noch einigermassen genau und stabil sein.

Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.

Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über einen I/O-Pin zugeführt werden können.

Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den Datenblättern der entsprechenden Controller raus lesen.

Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer Auflösung von 65536.

Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz, der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht höher sein als die Hälfte des CPU-Taktes.

Der Vorzähler (Prescaler)

Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler versorgt.
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer zugeführt.
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024 einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca. 4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw. Zählregister mit dieser Frequenz inkrementiert.

8-Bit Timer/Counter

Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei 8-Bit Timer auf.

Der 8-Bit Timer wird über folgende Register angesprochen:

TCCR0

Timer/Counter Control Register Timer 0
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.
Das Register ist wie folgt aufgebaut:

Bit 7 6 5 4 3 2 1 0
Name - - - - - CS02 CS01 CS00
R/W R R R R R R/W R/W R/W
Initialwert 0 0 0 0 0 0 0 0
CS02
CS01
CS00
Clock Select Bits
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:
CS02 CS01 CS00 Resultat
0 0 0 Stopp, Der Timer/Counter wird angehalten.
0 0 1 CPU-Takt
0 1 0 CPU-Takt / 8
0 1 1 CPU-Takt / 64
1 0 0 CPU-Takt / 256
1 0 1 CPU-Takt / 1024
1 1 0 Externer Pin TO, fallende Flanke
1 1 1 Externer Pin TO, steigende Flanke

Wenn als Quelle der externe Pin TO verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin TO als Ausgang geschaltet ist.

TCNT0

Timer/Counter Daten Register Timer 0
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim nächsten Zyklus wieder bei 0.
Bit 7 6 5 4 3 2 1 0
Name MSB             LSB
R/W R/W R/W R/W R/W R/W R/W R/W R/W
Initialwert 0 0 0 0 0 0 0 0

Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen schreiben wir die folgende Befehlszeile:

outp ((1<<CS02) | (1<<CS00), TCCR0);

Der Zähler zählt nun von 0 an aufwärts bis 255 um dann wieder bei 0 zu beginnen. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag TOV0 im Timer Interrupt Flag TIFR Register gesetzt und, falls so konfiguriert, ein entsprechender Interrupt ausgelöst.

16-Bit Timer/Counter

Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei (einige ATMega-Modelle) 16-Bit Timer.

Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:

Folgende Register sind dem Timer/Counter 1 zugeordnet:

TCCR1A

Timer/Counter Control Register A Timer 1
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.
Das Register ist wie folgt aufgebaut:

Bit 7 6 5 4 3 2 1 0
Name COM1A1 COM1A0 - - - - PWM11 PWM10
R/W R/W R/W R R R R R/W R/W
Initialwert 0 0 0 0 0 0 0 0
COM1A1
COM1A0
Compare Match Control Bits
Diese 2 Bits bestimmen die Aktion, welche am Output-Pin OC1 ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.
Der Pin OC1 (PB3 beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.
COM1A1 COM1A0 Resultat
0 0 Output-Pin OC1 wird nicht angesteuert.
0 1 Das Signal am Pin OC1 wird invertiert (Toggle).
1 0 Der Output Pin OC1 wird auf 0 gesetzt.
1 1 Der Output Pin OC1 wird auf 1 gesetzt.
In der PWM-Betriebsart haben diese Bits eine andere Funktion.
COM1A1 COM1A0 Resultat
0 0 Output-Pin OC1 wird nicht angesteuert.
0 1 Output-Pin OC1 wird nicht angesteuert.
1 0 Wird beim Hochzählen der Wert im Vergleichsregister erreicht so wird der Pin OC1 auf 0 gesetzt.
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 1 gesetzt.
Man nennt dies nicht invertierende PWM.
1 1 Wird beim Hochzählen der Wert im Vergleichsregister erreicht so wird der Pin OC1 auf 1 gesetzt.
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 0 gesetzt.
Man nennt dies invertierende PWM.
PWM11
PWM10
PWM Mode Select Bits
Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.
PWM11 PWM10 Resultat
0 0 Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.
0 1 8-Bit PWM Betriebsart aktivieren.
1 0 9-Bit PWM Betriebsart aktivieren.
1 1 10-Bit PWM Betriebsart aktivieren.
TCCR1B Timer/Counter Control Register B Timer 1
Bit 7 6 5 4 3 2 1 0
Name ICNC1 ICES1 - - CTC1 CS12 CS11 CS10
R/W R/W R/W R R R/W R/W R/W R/W
Initialwert 0 0 0 0 0 0 0 0
ICNC1 Input Capture Noise Canceler (4 CKs) Timer/Counter 1
oder auf Deutsch Rauschunterdrückung des Eingangssignals.
Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin ICP jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.
ICES1 Input Capture Edge Select Timer/Counter 1
Mit diesem Bit wird bestimmt, ob die steigende (ICES1=1) oder fallende (ICES1=0) Flanke zur Auswertung des Input Capture Signals an Pin ICP heran gezogen wird.
CTC1 Clear Timer/Counter on Compare Match Timer/Counter 1
Wenn dieses Bit gesetzt ist so wird nach einer Übereinstimmung des Datenregisters TCNT1H/TCNT1L mit dem Vergleichswert in OCR1H/OCR1L das Datenregister TCNT1H/TCNT1L auf 0 gesetzt.
Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird ergibt sich je nach eingestelltem Vorzähler ein etwas anderes Zählverhalten:
Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:
... | C-2 | C-1 | C | 0 | 1 |...
Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:
... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1, C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...
In der PWM-Betriebsart hat dieses Bit keine Funktion.
CS12
CS11
CS10
Clock Select Bits
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:
CS12 CS11 CS10 Resultat
0 0 0 Stopp, Der Timer/Counter wird angehalten.
0 0 1 CPU-Takt
0 1 0 CPU-Takt / 8
0 1 1 CPU-Takt / 64
1 0 0 CPU-Takt / 256
1 0 1 CPU-Takt / 1024
1 1 0 Externer Pin TO, fallende Flanke
1 1 1 Externer Pin T0, steigende Flanke

Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet ist.

TCNT1H
TCNT1L

Timer/Counter Daten Register Timer/Counter 1
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat beginnt er beim nächsten Zyklus wieder bei 0.
Bit 7 6 5 4 3 2 1 0  
Name MSB               TCNT1H
Name               LSB TCNT1L
R/W R/W R/W R/W R/W R/W R/W R/W R/W  
Initialwert 0 0 0 0 0 0 0 0  

In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.

Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf OCR1 oder ICR1 zugegriffen wird.
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst TCNT1L und erst danach TCNT1H ausgelesen werden.

Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das TCNT1H-Register und erst danach das TCNT1L-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.

OCR1H
OCR1L

Timer/Counter Output Compare Register Timer/Counter 1
Bit 7 6 5 4 3 2 1 0  
Name MSB               OCR1H
Name               LSB OCR1L
R/W R/W R/W R/W R/W R/W R/W R/W R/W  
Initialwert 0 0 0 0 0 0 0 0  

Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt..

Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf OCR1 oder ICR1 zugegriffen wird.
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst TCNT1L und erst danach TCNT1H ausgelesen werden.

Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das TCNT1H-Register und erst danach das TCNT1L-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.

ICR1H
ICR1L

Timer/Counter Input Capture Register Timer/Counter 1
Bit 7 6 5 4 3 2 1 0  
Name MSB               ICR1H
Name               LSB ICR1L
R/W R R R R R R R R  
Initialwert 0 0 0 0 0 0 0 0  

Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.

Wenn am Input Capture Pin ICP die gemäss Einstellungen im TCCR1B definierte Flanke erkannt wird so wird der aktuelle Inhalt des Datenregisters TCNT1H/TCNT1L sofort in dieses Register kopiert und das Input Capture Flag ICF1 im Timer Interrupt Flag Register TIFR gesetzt.

Wie bereits oben erwähnt müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:

Lesen: ICR1L -> ICR1H
Schreiben: ICR1H -> ICR1L

Die PWM-Betriebsart

Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird so bilden das Datenregister TCNT1H/TCNT1L und das Vergleichsregister OCR1H/OCR1L einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am OC1-Pin (PB3 beim 2313) abgegriffen werden kann. Das Datenregister TCNT1H/TCNT1L wird dabei als Auf-/Ab-Zähler betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.
Die Obergrenze ergibt sich daraus, ob 8- 9- oder 10-Bit PWM verwendet wird und zwar gemäss folgender Tabelle:

Auflösung Obergrenze Frequenz
8 255 fTC1 / 510
9 511 fTC1 / 1022
10 1023 fTC1 / 2046

Wenn nun der Zählerwert im Datenregister den in OCR1H/OCR1L gespeicherten Wert erreicht wird der Ausgabepin OC1 gesetzt bzw. gelöscht, je nach Einstellung von COM1A1 und COM1A0 im TCCR1A-Register.

Ich habe versucht, die entsprechenden Signale in der folgenden Grafik zusammenzufassen

Vergleichswert-Überprüfung

Hier wird in ein spezielles Vergleichswertregister (OCR1H/OCR1L) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.
Erreicht der Zähler den in diesem Register eingetragenen Wert so kann ein Signal (0 oder 1) am Pin OC1 erzeugt und/oder ein Interrupt ausgelöst werden.

 

Einfangen eines Eingangssignals (Input Capturing)

Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.
Wenn die Signalquelle ein starkes Rauschen beinhaltet kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind wird die entsprechende Aktion ausgelöst.

Gemeinsame Register

Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben Register zu finden sind.

TIMSK

Timer/Counter Interrupt Mask Register

Bit 7 6 5 4 3 2 1 0
Name TOIE1 OCIE1A - - TICIE - TOIE0 -
R/W R/W R/W R R R/W R R/W R
Initialwert 0 0 0 0 0 0 0 0
TOIE1 Timer/Counter Overflow Interrupt Enable Timer/Counter 1
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.
OCIE1A Output Compare Match Interrupt Enable Timer/Counter 1
Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein Vergleichswert definiert werden.
Wenn dieses Bit gesetzt ist wird beim Erreichen des Vergleichswertes ein Compare Match Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.
TICIE Timer/Counter Input Capture Interrupt Enable
Wenn dieses Bit gesetzt ist wird ein Capture Event Interrupt ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP) auftritt. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein, wenn auch ein entsprechender Interrupt ausgelöst werden soll.
TOIE0 Timer/Counter Overflow Interrupt Enable Timer/Counter 0
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.
TIFR Timer/Counter Interrupt Flag Register
Bit 7 6 5 4 3 2 1 0
Name TOV1 OCF1A - - ICF1 - TOV0 -
R/W R/W R/W R R R/W R R/W R
Initialwert 0 0 0 0 0 0 0 0
TOV1 Timer/Counter Overflow Flag Timer/Counter 1
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein Überlauf des Datenregisters stattfindet.
In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).
Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.
OCF1A Output Compare Flag Timer/Counter 1
Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters von Timer/Counter 1 mit demjenigen im Vergleichsregister OCR1 übereinstimmt.
Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.
ICF1 Input Capture Flag Timer/Counter 1
Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist, welches anzeigt, dass der Wert des Datenregisters des Timer/Counter 1 in das Input Capture Register ICR1 übertragen wurde.
Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.
TOV0 Timer/Counter Overflow Flag Timer/Counter 0
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein Überlauf des Datenregisters stattfindet.
Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.