Der Timer wird zum Leben erweckt

Eine der wichtigsten Funktionen im Mikrocontroller ist das Zählen. In den bisherigen Anwendungen wurden Wartefunktionen dadurch realisiert, dass eine Variable z.B. in einer for-Schleife nach oben gezählt wurde. Wenn ein bestimmter Wert erreicht wird, geht es weiter.
Nachteil: während dieser Zählaktionen ist der Controller beschäftigt und steht für andere Dinge nicht zur Verfügung.
Abhilfe: Im Controller gibt es mehrere Einheiten (Timer), die ohne den Controller zu belasten zählen können. Sie machen das ruhig in einer Ecke des Controllers und stören dabei niemanden.
Wird ein bestimmter Wert erreicht, melden sie sich kurz, sagen Bescheid und fangen dann wieder von vorne an.
Im laufenden Programm kann man auf diesen Hinweis vom Timer reagieren und Aktionen ausführen. Man kann es aber auch ignorieren.
Der Hinweis des Timers wird als Interrupt (Unterbrechung) bezeichnet. Der Programmierer muss dem laufenden Programm mitteilen, wie es auf einen Interrupt reagieren soll.
Für den Einsatz des Timers sind zwei zwei Dinge zu tun:
- Der Timer muss eingestellt und gestartet werden.
- Der Programmteil für den Interrupt muss geschrieben werden.
Es wird zuerst der Timer0 benutzt, ein 8bit-Timer. Das bedeutet, das Zählen läuft in einem 8bit-Register ab. Er kann als maximal bis 256 zählen.
Es gibt dann noch einen 16bit-Timer (Timer1), der das Zählen in einem 16bit-Register bewerkstelligt.
zu 1. Timer einstellen und starten
Zum Einstellen des Timer0 gibt es zwei Register:
TCCR0 = Timer/Counter Control Register 0
In diesem 8bit-Register werden die Bits 0, 1 und 2 zur Einstellung des Timer0 verwendet. Die restlichen 5 Bits brauchen nicht beachtet zu werden, da sie für eine speziellen Einsatz (PWM Steuerung) vorgesehen sind und nicht bei allen Controllern verwendet werden.
Bit 2 | Bit 1 | Bit 0 | Bedeutung |
---|---|---|---|
CS02 | CS01 | CS00 | |
0 | 0 | 0 | Timer Stopp |
0 | 0 | 1 | Systemtakt |
0 | 1 | 0 | Systemtakt / 8 |
0 | 1 | 1 | Systemtakt / 64 |
1 | 0 | 0 | Systemtakt / 256 |
1 | 0 | 1 | Systemtakt / 1024 |
1 | 1 | 0 | externer Takt, fallende Flanke an T0 |
1 | 1 | 1 | externer Takt, steigende Flanke an T0 |
Sind die drei Bits auf 0 gesetzt, ist der Timer ausgeschaltet.
Die nächsten 5 Einstellungsmöglichkeiten bestimmen, wann der Zähler eins weite zählt. Im ersten Fall wird bei jedem Takt des Systemtaktes gezählt. Die vier anderen Möglichkeiten teilen den Systemtakt um den angegeben Wert. Damit ist es möglich, die teilweise recht hohen Takte des Controllers zu verkleinern.
Beträgt der Systemtakt z.B. 3,6864 MHz (ein typischer Wert), so kann dieser über die Teilung durch 1024 auf 3,6 kHz erniedrigt werden.
Da der Zähler maximal bis 256 zählen kann, wird nach jedem 256. Takt ein Interrupt ausgelöst. Das ergibt eine Interruptfrequenz von 14,0625 Hz oder einen Abstand zwischen zwei Interrups von etwa 71 ms.
Das ist für viele Zwecke immer noch sehr kurz und muss dann in dder Interrupt-Routine über einen weiteren Zähler verändert werden. Dazu später.
Wenn wir also unseren Timer so einstellen wollen, dass der Takt durch 1024 geteilt wird, schreiben wir im Hauptprogramm als erstes:
TCCR0 |= 0b00000101;
TIMSK = Timer Interrupt MaSK
Bit 1 | Bit 0 | Bedeutung |
---|---|---|
OCIE0 | TOIE0 | |
0 | 1 | Der Interrupt wird ausgelöst, wenn der Zähler von 255 auf 0 wechselt (Überlauf) |
1 | 0 | Der Interrupt wird ausgelöst, wenn der Zähler den Wert erreicht, der im Register OCR0 steht (Vergleich) |

Die Vergleichsmethode ist der Überlaufmethode vorzuziehen. Warum?
In der Überlaufmethode wird ein Register bis 255 hochgezählt. Das Register heißt
TCNT0 = Timer/CouNTer0
Dieses Register kann mit einem Wert voreingestellt werden. Wenn man also nicht von 0 bis 255 zählen will, sondern nur 100 Schritte braucht, stellt man das Register auf 155 ein. Mit dem Start wird von da an weitergezählt.
Nachteil: Nach dem Überlauf beginnt der Zähler wieder mit 0. Man muss also in der Interrupt-Routine als erstes sicherstellen, dass das Register TCNT0 wieder mit 155 geladen wird. Dazu sind Befehle notwendig, die den kontinuierlichen Timerbetrieb stören. Es sind nach der Interruptauslösung die Takte zu berücksichtigen, die diese Befehle benötigen, denn der Time rläuft ja weiter. Damit ist diese Methode nicht für genaue Anwendungen geeignet.
Bei der Vergleichsmethode wird das Verleichsregister einmal geladen und steht immer zur Verfügung. Der Zähler beginnt immer bei 0 und löst beim Erreichen des Wertes im register den Interrupt aus. Damit arbeitet der Timer kontinuierlich.
OCR0 = Output Compare Register Timer 0
Ein Programmteil für den Start des Timer 0 kann z.B. so aussehen:
Ganz am Ende steht noch der Befehl
sei()
der die Erlaubnis gibt, Interrupts anzunehmen.
Manchmal kann es notwendig sein, eine Unterbrechung des laufenden Programmes zu verbieten. Dann muss der Befehl
cli()
ausgeführt werden, der alle Interrupts sperrt.
Die folgende while()-Schleife bleibt leer! Der Interrupt läuft und wird bei jedem Überlauf ausgelöst.
zu 2. Interrupt-Routine
Was passiert nun, wenn ein Interrupt ausgelöst wurde?
Es wird eine Funktion aufgerufen, die
SIGNAL()
oder
INTERRUPT()
heißen kann.
Die SIGNAL() - Funktion kann durch andere Interrupts nicht unterbrochen werden, die INTERRUPT() - Funktion sehr wohl.
Den beiden Funktionen muss noch mitgeteilt werden, wer den Interrupt ausgelöst hat. In unserem Fall ist das SIG_OVERFLOW0 für einen Überlauf und SIG_OUTPUT_COMPARE0 für einen Vergleich.
(alle Bezeichner für eine Interrupt-Funktion)
Die Funktion heißt also bei einem Aufruf durch einen Vergleich:
SIGNAL(SIG_OUTPUT_COMPARE0) { }
In der Funktion kann nun das gemacht werden, was für den Interrupt-Fall vorgesehen war. Z.B. kann jetzt die LED-Anzeige im Sekundentakt nach oben gezählt werden. Dazu muss ein globale Variable vereinbart werden, die den aktualle Zählwert enthält.
Die Funktion überwacht nun einfach diese Variable und wechselt die LED-Anzeige.
Hinweis: Mit dieser Methode ist es schlecht möglich, einen genauen Sekundentakt zu erzeugen. Für einen Sekundentakt wird der Timer2 genutzt. Dieser Timer nutzt als Takt einen zweiten Quarz von 32768 Hz (215 Hz), der einen genauen Impul zum Ansteuern einer Uhrenfunktion liefert.