www.mikrocontroller.net

AVR-Tutorial: Timer

Timer sind eines der Hauptarbeitspferde in unserem Mikrocontroller. Mit ihrer Hilfe ist es möglich in regelmäßigen Zeitabständen Aktionen zu veranlassen. Aber Timer können noch mehr!

  • Timer können mit einem externen Pin hochgezählt werden
  • es gibt Möglichkeiten bei bestimmten Zählerständen einen Interrupt auslösen zu lassen
  • Timer können aber auch völlig selbstständig Signale an einem Ausgabepin erzeugen
  • ...

Aber der Reihe nach.

Inhaltsverzeichnis

[bearbeiten] Was ist ein Timer?

Ein Timer ist im Grunde nichts anderes als ein bestimmtes Register im Mikrocontroller, das hardwaregesteuert fortlaufend um 1 erhöht (oder erniedrigt) wird (statt um 1 erhöhen sagt man auch inkrementieren, und das Gegenstück, dekrementieren, bedeutet um 1 erniedrigen). Anstatt also Befehle im Programm vorzusehen, die regelmäßig ausgeführt werden und ein Register inkrementieren, erledigt dies der Mikrocontroller ganz von alleine. Dazu ist es möglich, den Timer mit dem Systemtakt zu verbinden und so die Genauigkeit des Quarzes auszunutzen, um ein Register regelmässig und vor allen Dingen unabhängig vom restlichen Programmfluss (!) hochzählen zu lassen.

Davon alleine hätte man aber noch keinen großen Gewinn. Nützlich wird das Ganze erst dann, wenn man bei bestimmten Zählerständen eine Aktion ausführen lassen kann. Einer der 'bestimmten Zählerstände' ist zum Beispiel der Overflow. Das Zählregister eines Timers kann natürlich nicht beliebig lange inkrementiert werden – z. B. ist der höchste Zählerstand, den ein 8-Bit-Timer erreichen kann, 28 – 1 = 255. Beim nächsten Inkrementierschritt tritt ein Überlauf (engl. Overflow) auf, der den Timerstand wieder zu 0 werden lässt. Und hier liegt der springende Punkt. Wir können uns nämlich an diesen Overflow "anhängen" und den Controller so konfigurieren, daß beim Auftreten des Timer-Overflows ein Interrupt ausgelöst wird.

[bearbeiten] Der Vorteiler (Prescaler)

Wenn also der Quarzoszillator mit 4 MHz schwingt, dann würde auch der Timer 4 Millionen mal in der Sekunde erhöht werden. Da der Timer jedes Mal von 0 bis 255 zählt, bevor ein Overflow auftritt, heist das auch, dass in einer Sekunde 4000000 / 256 = 15625 Overflows vorkommen. Ganz schön schnell! Nur: Oft ist das nicht sinnvoll. Um diese Raten zu verzögern, gibt es den Vorteiler, oder auf Englisch, Prescaler. Er kann auf die Werte 1, 8, 64, 256 oder 1024 eingestellt werden. Seine Aufgabe ist es, den Systemtakt um den angegebenen Faktor zu teilen. Steht der Vorteiler also auf 1024, so wird nur bei jedem 1024-ten Impuls vom Systemtakt das Timerregister um 1 erhöht. Entsprechend weniger häufig kommen dann natürlich die Overflows. Der Systemtakt sei wieder 4000000. Dann wird der Timer in 1 Sekunde 4000000 / 1024 = 3906.25 mal erhöht. Da der Timer wieder jedesmal bis 255 zählen muss bis ein Overflow auftritt, bedeutet dies, dass in 1 Sekunde 3906.25 / 256 = 15.25 Overflows auftreten.

Systemtakt: 4Mhz

 Vorteiler    Overflows/Sekunde      Zeit zwischen
                                     2 Overflows [s]
     1            15625                0.000064
     8             1953.125            0.000512
    64              244.1406           0.004096
   256               61.0351           0.016384
  1024               15.2587           0.065536

[bearbeiten] Erste Tests

Ein Programm das einen Timer Overflow in Aktion zeigt könnte z.B. so aussehen:

.include "m8def.inc"
 
.def temp = r16
.def leds = r17
 
.org 0x0000
        rjmp    main                  ; Reset Handler
.org OVF0addr
        rjmp    timer0_overflow       ; Timer Overflow Handler
 
main:
        ldi     temp, LOW(RAMEND)     ; Stackpointer initialisieren
        out     SPL, temp
        ldi     temp, HIGH(RAMEND)
        out     SPH, temp
	
        ldi     temp, 0xFF            ; Port B auf Ausgang
        out     DDRB, temp
 
        ldi     leds, 0xFF
 
        ldi     temp, 0b00000001      ; CS00 setzen: Teiler 1
        out     TCCR0, temp
 
        ldi     temp, 0b00000001      ; TOIE0: Interrupt bei Timer Overflow
        out     TIMSK, temp
 
        sei
 
loop:   rjmp    loop
 
timer0_overflow:                      ; Timer 0 Overflow Handler
        out     PORTB, leds
        com     leds
        reti

Das Programm beginnt mit der Interrupt-Vektoren-Tabelle. Dort ist an der Adresse OVF0Addr ein Sprung zur Marke timer0_overflow eingetragen. Wenn also ein Overflow Interrupt vom Timer 0 auftritt, so wird dieser Interrupt durch den rjmp weitergeleitet an die Stelle timer0_overflow.

Das Hauptprogramm beginnt ganz normal mit der Belegung des Stackpointers. Danach wird der Port B auf Ausgang geschaltet, wir wollen hier wieder die LED anschliessen.

Durch Beschreiben von TCCR0 mit dem Bitmuster 0b00000001 wird der Vorteiler auf 1 gesetzt. Für die ersten Versuche empfiehlt es sich, das Programm mit dem AVR-Studio zunächst zu simulieren. Würden wir einen größeren Vorteiler benutzen, so müsste man ziemlich oft mittels F11 einen simulierten Schritt ausführen um eine Änderung im Timerregister zu erreichen.

Die nächsten Anweisungen setzen im TIMSK Register das TOIE0 Bit. Sinn der Sache ist es, dem Timer zu erlauben bei Erreichen eines Overflow einen Interrupt auszulösen.

Zum Schluss noch die Interrupts generell mittels sei freigeben. Dieser Schritt ist obligatorisch. Im Mikrocontroller können viele Quellen einen Interrupt auslösen. Daraus folgt: Für jede mögliche Quelle muss festgelegt werden, ob sie einen Interrupt erzeugen darf oder nicht. Die Oberhoheit hat aber das globale Interrupt Flag. Mit ihm können alle Interrupts, egal von welcher Quelle sie kommen, unterdrückt werden.

Damit ist die Initialisierung beendet und das Hauptprogramm kann sich schlafen legen. Die loop: rjmp loop Schleife macht genau dieses.

Tritt nun ein Overflow am Timer auf, so wird über den Umweg über die Interrupt Vektor Tabelle der Programmteil timer0_overflow angesprungen. Dieser gibt einfach nur den Inhalt des Registers leds am Port B aus. Danach wird das leds Register mit einer com Operation negiert, so dass aus allen 0 Bits eine 1 wird und umgekehrt. Die Overflow Behandlung ist damit beendet und mittels reti wird der Interrupt Handler wieder verlassen.

[bearbeiten] Simulation im AVR-Studio

Es lohnt sich den ganzen Vorgang im AVR-Studio simulieren zu lassen. Dazu am besten in der linken I/O View Ansicht die Einträge für PORTB und TIMER_COUNTER_0 öffnen. Wird mittels F11 durch das Programm gegangen, so sieht man, dass ab dem Moment, ab dem der Vorteiler auf 1 gesetzt wird, der Timer 0 im TCNT0 Register zu zählen anfängt. Mit jedem Druck auf F11 erhöht sich der Zählerstand. Irgendwann ist dann die Endlosschleife loop erreicht. Drücken Sie weiterhin F11 und beobachten sie, wie TCNT0 immer höher zählt, bis der Overflow erreicht wird. In dem Moment, in dem der Overflow erreicht wird, wird der Interrupt ausgelöst. Mit dem nächsten F11 landen sie in der Interrupt Vektor Tabelle und von dort geht es weiter zu timer_0_overflow. Weitere Tastendrücke von F11 erledigen dann die Ausgabe auf den Port B, das invertieren des Registers r17 und der Interrupt ist damit behandelt. Nach dem reti macht der Microcontroller genau an der Stelle weiter, an der er vom Interrupt unterbrochen wurde. Und der Timer 0 hat in der Zwischenzeit weitergezählt! Nach exakt weiteren 256 Schritten, vom Auftreten des ersten Overflows an gerechnet, wird der nächste Overflow ausgelöst.

[bearbeiten] Wie schnell schaltet denn jetzt der Port?

Eine berechtigte Frage. Dazu müssen wir etwas rechnen. Keine Angst, es ist nicht schwer, und wer das Prinzip bisher verstanden hat, der sollte keine Schwierigkeiten haben die Berechnung nachzuvollziehen.

Der Quarzoszillator schwingt mit 4 MHz. Das heißt in 1 Sekunde werden 4000000 Taktzyklen generiert. Durch die Wahl des Vorteilers von 1 bedeutet das auch, dass der Timer 4000000 mal in der Sekunde erhöht wird. Von einem Overflow zum nächsten muss der Timer 256 Zählvorgänge ausführen. Also werden in 1 Sekunde 4000000 / 256 = 15625 Overflows generiert. Bei jedem Overflow schalten wir die LEDs jeweils in den anderen Zustand. D.h die LEDs blinken mit einer Frequenz von 7812.5 Hz. Das ist zuviel als dass wir es noch sehen könnten.

Was können wir also tun um diese Blinkfrequenz zu verringern? Im Moment ist unsere einzige Einflussgröße der Vorteiler. Wie sieht die Rechnung aus, wenn wir einen Vorteiler von 1024 wählen?

Wiederrum: Der Systemtakt sei 4 Mhz. Durch den Vorteiler von 1024 werden daraus 4000000 / 1024 = 3906.25 Pulse pro Sekunde für den Timer. Der zählt wiederum 256 Zustände von einem Overflow zum nächsten. 3906.25 / 256 = 15.2587. Und wiederum: Im Overflow werden die LEDs ja abwechselnd ein und ausgeschaltet, also dividieren wir noch durch 2: 15.2587 / 2 = 7.629. Also knapp 7 Hz. Diese Frequenz müsste man schon mit freiem Auge sehen. Die LEDs werden ziemlich schnell vor sich hin blinken.

Reicht diese Verzögerung noch immer nicht, dann haben wir 2 Möglichkeiten:

  • Entweder wir benutzen einen anderen Timer. Timer 1 beispielsweise ist ein 16 Bit Timer. Der Timer zählt also nicht bis 256 sondern bis 65536. Bei entsprechender Umarbeitung des Programms und einem Vorteiler von 1024 bedeutet das, dass die LEDs einen Ein/Aus Zyklus in 33 Sekunden absolvieren.
  • Oder wir schalten die LEDs nicht bei jedem Timer Overflow um. Man könnte zum Beispiel in einem Register bis 7 zählen und nur dann, wenn dieses Register 7 erreicht hat, wird
    • das Register wieder auf 0 gesetzt und
    • die LEDs umgeschaltet.

[bearbeiten] Timer 0

Timer 0 ist ein 8 Bit Timer.

  • Overflow Interrupt

[bearbeiten] TCCR0

 +------+------+------+------+------+------+------+------+
 |      |      |      |      |      | CS02 | CS01 | CS00 |
 +------+------+------+------+------+------+------+------+
 CS02 - CS00   Clock Select
CS02CS01CS00Bedeutung
000keine (Der Timer ist angehalten)
001Vorteiler: 1
010Vorteiler: 8
011Vorteiler: 64
100Vorteiler: 256
101Vorteiler: 1024
110Externer Takt vom Pin T0, fallende Flanke
111Externer Takt vom Pin T0, steigende Flanke

[bearbeiten] TIMSK

 +------+------+------+------+------+------+------+------+
 |      |      |      |      |      |      |      | TOIE0|
 +------+------+------+------+------+------+------+------+
 TOIE0         Timer 0 Overflow Interrupt Enable
   Ist dieses Bit gesetzt, so wird beim Auftreten eines
   Overflows am Timer ein Interrupt ausgelöst.

Anstatt der Schreibweise

        ldi     temp, 0b00000001      ; TOIE0: Interrupt bei Timer Overflow
        out     TIMSK, temp

ist es besser, die Schreibweise

        ldi     temp, 1 << TOIE0
        out     TIMSK, temp

zu wählen, da hier unmittelbar aus dem Ladekommando hervorgeht, welche Bedeutung das gesetzte Bit hat. Die vorher inkludierte m8def.inc definiert dazu alles Notwendige.

[bearbeiten] Timer 1

Timer 1 ist ein 16 Bit Timer

  • Overflow Interrupt
  • Clear Timer on Compare Match
  • Input Capture
  • 2 Compare Einheiten
  • div. PWM Modi

[bearbeiten] TCCR1B

 +------+------+------+------+------+------+------+------+
 | ICNC1| ICES1|      | WGM13| WGM12| CS12 | CS11 | CS10 |
 +------+------+------+------+------+------+------+------+
 CS12 - CS10   Clock Select
CS12CS11CS10Bedeutung
000keine (Der Timer ist angehalten)
001Vorteiler: 1
010Vorteiler: 8
011Vorteiler: 64
100Vorteiler: 256
101Vorteiler: 1024
110Externer Takt vom Pin T1, fallende Flanke
111Externer Takt vom Pin T1, steigende Flanke
 ICES1   Input Capture Edge Select
 ICNC1   Input Capture Noise Canceler

[bearbeiten] TCCR1A

 +------+------+------+------+------+------+------+------+
 |COM1A1|COM1A0|COM1B1|COM1B0|FOC1A |FOC1B |WGM11 |WGM10 |
 +------+------+------+------+------+------+------+------+

[bearbeiten] OCR1A

 +------+------+------+------+------+------+------+------+
 |      |      |      |      |      |      |      |      |
 +------+------+------+------+------+------+------+------+
 +------+------+------+------+------+------+------+------+
 |      |      |      |      |      |      |      |      |
 +------+------+------+------+------+------+------+------+

[bearbeiten] OCR1B

 +------+------+------+------+------+------+------+------+
 |      |      |      |      |      |      |      |      |
 +------+------+------+------+------+------+------+------+
 +------+------+------+------+------+------+------+------+
 |      |      |      |      |      |      |      |      |
 +------+------+------+------+------+------+------+------+

[bearbeiten] ICR1

 +------+------+------+------+------+------+------+------+
 |      |      |      |      |      |      |      |      |
 +------+------+------+------+------+------+------+------+
 +------+------+------+------+------+------+------+------+
 |      |      |      |      |      |      |      |      |
 +------+------+------+------+------+------+------+------+

[bearbeiten] TIMSK

 +------+------+------+------+------+------+------+------+
 |      |      |TICIE1|OCIE1A|OCIE1B| TOIE1|      |      |
 +------+------+------+------+------+------+------+------+
 TICIE1    Timer 1 Input Capture Interrupt Enable
 OCIE1A    Timer 1 Output Compare A Match Interrupt Enable
 OCIE1B    Timer 1 Output Compare B Match Interrupt Enable
 TOIE1     Timer 1 Overflow Interrupt Enable

[bearbeiten] Timer 2

Timer 2 ist ein 8 Bit Timer.

  • Overflow Interrupt
  • Compare Match Interrupt
  • Clear Timer on Compare Match
  • Phasen korrekte PWM

[bearbeiten] TCCR2

 +------+------+------+------+------+------+------+------+
 | FOC2 | WGM20| COM21| COM20| WGM21| CS22 | CS21 | CS20 |
 +------+------+------+------+------+------+------+------+
 CS22 - CS20   Clock Select
CS22CS21CS20Bedeutung
000keine (Der Timer ist angehalten)
001Vorteiler: 1
010Vorteiler: 8
011Vorteiler: 32
100Vorteiler: 64
101Vorteiler: 128
110Vorteiler: 256
111Vorteiler: 1024

[bearbeiten] OCR2

 +------+------+------+------+------+------+------+------+
 |      |      |      |      |      |      |      |      |
 +------+------+------+------+------+------+------+------+

[bearbeiten] TIMSK

 +------+------+------+------+------+------+------+------+
 | OCIE2| TOIE2|      |      |      |      |      |      |
 +------+------+------+------+------+------+------+------+
 OCIE2   Timer 2 Output Compare Interrupt Enable
 TOIE2   Timer 2 Overflow Interrupt Enable

[bearbeiten] Was geht noch mit einem Timer?

Timer sind sehr universelle Microcontroller Bestandteile. Für weitergehende Studien ist es daher unerlässlich das entsprechende Datenblatt des Microcontrollers zu studieren. Oft ist es zb. möglich, dass der Timer bei erreichen von bestimmten Zählerständen einen Ausgabepin von sich aus ein-/aus-/umschaltet. Er erledigt dann das was wir oben noch mit einem Interrupt gemacht haben eigenständig komplett in Hardware. Bei einigen Timern ist es möglich damit eine PWM (Pulsweiten-Modulation) aufzubauen.

Ein paar der Timermodule lassen sich auch als Counter verwenden. Damit kann man z.B. die Anzahl externer Ereignisse wie Schaltvorgänge eines Inkrementalgebers bestimmen.

Andere bieten die Möglichkeit über einen externen Uhrenquarz getaktet zu werden. (Anwendung z.B. eine "Echtzeituhr" oder als "Weckfunktion" aus einem Standby/Powerdownmodus)

[bearbeiten] Weblinks

webmaster@mikrocontroller.netImpressumWerbung auf Mikrocontroller.net