Im Grunde ist das ziemlich einfach.
Ein Timer ist einfach nur ein Stück Hardware. Ein Zähler
der vor sich hinzählt.
Wie schnell dieser Zähler zählt wird mit dem Vorteiler
eingestellt. Je nach eingestelltem Vorteiler wird der
Systemtakt (also die Quarzfrequenz) durch genau diesen
Faktor dividiert und mit dieser runtergesetzten Frequenz
zählt dann der Timer. In dem Moment wo du einen Vorteiler
setzt, zählt der Timer auch schon. Setzt du den Vorteiler
auf 0 hört der Timer wieder auf zu zählen.
Was kann man nun mit dem Timer machen?
(Ohne Anspruch auf Vollständigkeit)
Zuallererst kann man natürlich auf das Zählregister zugreifen.
Super. Aber meist nicht sehr sinnvoll. Timer werden ja
eingesetzt um eben nicht in einer Schleife Wartezeit zu
verbrutzeln. Wenn ich aber in einer Schleife ständig das
Zählregister überprüfe und warte bis das einen gewissen Wert
erreicht hat, mach ich doch genau dasselbe.
Das kanns also nicht sein.
Aber Timer haben noch ein paar andere nette Features.
Einer davon ist der Overflow Interrupt. Was'n das?
Overflow: Jeder Timer hat nur eine bestimmte Bitgröße.
Zb. Ist der Timer0 ein 8 Bit Timer. Das heist er zählt von
0 bis 255 (255 ist die größte Zahl, die mit 8 Bit gerade noch
darstellbar ist). Und dann? Dann passiert ein Überlauf (engl.
overflow) und der Timer fängt wieder bei 0 an. Das Interessante
an der ganzen Sache ist nun, dass dieser Overflow einen Interrupt
auslösen kann.
Was'n ein Interrupt?
Ein Interrupt ist eine 'Unterbrechung'. Das heist der µC lässt
alles stehen und liegen womit er gerade beschäftigt ist und
führt eine spezielle Funktion aus. Ist er mit der Abarbeitung
dieser Funktion fertig, dann nimmt er seine Arbeit wieder dort
auf, wo er unterbrochen wurde.
Die Kombination aus beidem bewirkt jetzt, dass man bei einem
Overflow des Timers eine bestimmte Funktion ausführen lassen
kann. Das passiert immer, egal womit der µC gerade beschäftigt
ist. Mit 2 Einschränkungen:
* Die Interrupts müssen generell freigegeben sein
* Eine Interrupt Funktion kann selbst nicht durch einen
Interrupt unterbrochen werden. Die Funktion wird zuende gebracht
und erst danach wird der neue Interrupt bearbeitet. (Das stimmt
nicht ganz, aber fürs Erste belassen wir es mal dabei).
Soweit die Einführung.
Wie sieht das jetzt konkret in C, am Beispiel WinAvr avr-gcc aus?
Zb. So
#include <avr\io.h>
#include <avr\interrupt.h>
ISR( TIMER0_OVF_vect )
{
PORTB ^= 0xFF;
}
int main()
{
DDRB = 0xFF;
PORTB = 0x00;
TCCR0 = ( 1 << CS01 ) | ( 1 << CS00 );
TIMSK = ( 1 << TOIE0 );
sei();
while( 1 ) {
}
}
Die Programmausführung beginnt, wie immer bei main().
Dort richte ich zunächst mal einen Port her um dort
eine Ausgabe machen zu können. Ich nehm einfach mal
den kompletten Port als Ausgabe (DDRB = 0xFF;) und
geh davon aus, dass zumindest an einigen davon eine
LED angeschlossen ist.
TCCR0 = ( 1 << CS01 ) | ( 1 << CS00 );
Hier wird der Vorteiler des Timers gesetzt. Aus dem
Datenblatt ist ersichtlich, dass ein Setzen der Bits
CS01 und CS00 den Systemtakt durch 64 teilt. Durch
setzen von anderen Bits könnte man auch andere Teilfaktoren
erreichen. Nachdem der Teiler gesetzt ist, beginnt der
Timer auch schon fröhlich vor sich hin zu tuckern.
TIMSK = ( 1 << TOIE0 );
Das hier setzt das Bit, dass für den Overflow Interrupt
zuständig ist. Sobald also am Timer ein Overflow auftritt,
wird ein Interrupt ausgelöst. Der macht aber noch nichts,
da die Interrupts generell noch nicht freigegeben sind.
sei();
behebt dieses (sei = Set Enable Iterrupt). Ab jetzt ist
alles scharf und ein Overflow würde einen Interrupt auslösen
den der µC auch mit einer speziellen Funktion, dem sog.
Interrupt Handler, abarbeiten wird.
In main() bleibt nichts mehr zu tun, also schicken wir den
µC in eine Schleife, in der er bis zum St. Nimmerleinstag
hängt:
while( 1 ) {
}
Soweit so gut.
Tritt jetzt am Timer ein Overflow ein, so wird eine speziell
Funktion, der Interrupt Handler aufgerufen.
Hier ist er vereinbart:
ISR( TIMER0_OVF_vect )
{
Das ISR ist obligatorisch, und sagt dem Compiler, dass das hier
keine normale Funktion, sondern ein Interrupt Handler ist.
Das TIMER0_OVF_vect sagt ihm auch noch, welcher Interrupt Handler
gemeint ist. Ein AVR kann viele verschiedene Interrupts auslösen,
wir wollen aber den Overflow des Timers 0 haben.
Und in dem Handler?
{
PORTB ^= 0xFF;
}
Da schalte ich einfach den Ausgang vom PORTB um. Wenn an einem
Pin bisher eine 0 war, dann wird eine 1 daraus und umgekehrt.
Eine angeschlossene LED wird also entweder ein oder aus
geschaltet, bei jedem Aufruf des Interrupt Handlers immer
anders rum.
Und das wars dann auch schon.
Wie schnell blinkt nun die LED.
Lass uns mal rechnen.
Sagen wir mal dein µC arbeitet mit 1 Mhz. Wie schnell
zählt dann der Timer? Der Vorteiler ist momentan auf 64,
dh. der Timer zählt mit einer Frequenz von
1000000 / 64 = 15652 Hz
Um einen Ocerflow auszulösen, muss der Timer einmal von 0
bis 255 zählen, also 256 Zählschritte ausführen. Ergo
15652 / 256 = 61,irgendwas Hz
Mit dieser Frequenz wird die Led ein und ausgeschaltet.
DIe Flackerfrequenz beträgt also 61 / 2, also knapp 30 Hz
Deine Led flackert also bei 1 Mhz Taktfrequenz mit knapp 30 Hz
bei einem Vorteiler von 64. Das wirst du nicht sehen können.
Aber mann kann ja auch einen größeren Vorteiler nehmen :-)