Timer



Wir wollen einen der Timer benutzen, die uns der Atmega8 zur Verfügung stellt, um genau jede Sekunde eine LED blinken zu lassen. Wenn wir einen Quarz benutzen würden, wäre die Sekunde wirklich ganz exakt. Ohne Quarz, also mit dem internen 1MHz-Takt des Atmega8 ist die Sekunde aber auch schon relativ genau. Man könnte durchaus einen Küchen-Kurzzeitwecker damit bauen.

Wir benutzen LED1 unseres Experimentierboards (oder ein Steckbrett und verbinden die LED mit PD2).




Hier das Programm:

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


// Interrupt Service Routine fuer Timer1
ISR(TIMER1_OVF_vect) {        
    TCNT1 = 49911;         //Zaehlregister mit Vorladewert V vorladen / Berechnung: siehe unten
    PORTD ^= _BV(PD2);    //LED toggeln
}




int main(void){
    DDRD |= _BV(PD2) ; // PD2 ist LED Ausgang
    sei(); //Interrupts ermoeglichen
       
   
    // fuer Timer:     
    TIMSK |= (1<<TOIE1);
    TCCR1B |= (1<<CS11) |  (1<<CS10);     //Prescaler = 64
    TCNT1 = 0xFFFF;         //Zaehlregister vorladen mit FFFF zum Sofortstart
     
    while (1) { /* ich laufe ewig */ }
   
    return(0);
}



Was macht das Programm?

Sicher hast Du schon erkannt, daß wir wieder mit Interrupt arbeiten.

Wenn der Timer einen Interrupt auslöst (also genau jede Sekunde), wird diese Funktion:

ISR(TIMER1_OVF_vect) {        
    TCNT1 = 49911;        
    PORTD ^= _BV(PD2);   
}

angesprungen bzw ausgeführt.

Dort wird die LED an PD2 getoggelt, damit sie blinkt.
Und es wird der sogenannte Vorladewert TCNT1 in den Timer geladen, doch dazu später.

Wie funktioniert nun so ein Timer?
Wie ein Zähler, der es bemerkt, wenn er überläuft.
Wenn wir nichts weiteres unternehmen würden, machte der Timer in etwa folgendes:

  • er fängt bei Null an zu zählen

  • jede Millionstel Sekunde (denn er arbeitet mit 1 MHz) zählt er einen Schritt weiter

  • wenn er seine größte zu zählende Zahl erreicht hat, läuft er über - dabei wird in einem bestimmten Register das sogenannte "Signal-overflow-bit" gesetzt) - und er beginnt wieder von vorn

Um die Geschwindigkeit des Hochzählens zu verlangsamen, können wir die Quarzfrequenz "vor-teilen".
Die Zahl, die wir dazu benutzen nennt sich Prescaler (dt. Vorteiler).
        z.B.:
    TCCR1B = _BV(CS11) |  _BV(CS10);//Prescaler = 64
    TCCR1B = _BV(CS12)              //Prescaler = 256

Für genaue Zeiten muß der Wert (Quarzfrequenz / Prescaler) ein ganzzahliges Ergebnis liefern!

    Ganzzahlige Werte:

    1000000 / 64   = 15625  (1 MHz durch Prescaler 64)
    4000000 / 256 = 15625  (4 MHz durch Prescaler 256)


Die größte zu zählende Zahl, die ein 16-bit-Timer zählen kann, ist: 2 hoch 16 = 65536.

Es ist auch nicht notwendig, daß der Timer bei Null mit Zählen beginnt.
Wir können mit einem Vorladewert selbst bestimmen, bei welcher Zahl er mit Zählen anfangen soll (und somit exakt eine Sekunde realisieren).

Der Vorladewert errechnet sich bei unserem 16-bit-Timer:

    Vorladewert = 2^16 - (Wunschzeit * Quarzfrequenz / Prescaler)
                               2^16 - (        1s           *       1000000 Hz /           64    ) = 49911
 
Wir benutzen allerdings keinen Quarz, sondern die intern erzeugte Frequenz von 1 Mhz, was jedoch nichts an der Formel ändert.

Der Vorladewert muß naturlich jedesmal nach Überlaufen des Timers neu in ihn hineingeschrieben werden:

  TCNT1 = 49911;




Hier gibt es den Code-Ordner zum Programmieren des Atmega8.


Näheres wie immer im Datenblatt.

Dies ist die überabeitete Timer-Seite. Die alte Seite und der alte Code enthielt veraltete Befehle und funktionierte nur mit alten Versionen der avr-libc.