Armins AVR-Buch - Timer

Zuletzt geändert am 09.12.2023, 17:23:38

Timer


(armin77, 28.01.2013, 17:20:39)

Der ATMega 8 hat drei Timer. Zwei 8 Bit und einen 16 Bit Timer.
Timer0 und Timer2 können von 0 bis 255 zählen.
Timer1 kann von 0 bis 65535 zählen
Die Timer zählen die Impulse der Taktfrequenz oder als Counter/Zähler
die externen Schaltimpulse von Schalter/Tastern.
Damit lassen sich Zeitmessungen machen oder PWM Signale erzeugen e.t.c.

Timer0 alsTimer


Dieser zählt als Timer den Systemtakt.
Man kann hinter den Systemtakt einen Vorteiler (PRESCALE) von 1 , 8, 64, 256 oder 1024 setzen
Damit kann man längere/langsamere Zeiten erreichen.
Das gute an diesen Timern ist, daß sie unabhängig vom Hauptprogramm laufen.
Als 8 Bit Timer zählt er von 0-255.
Man kann den Timer so konfigurieren, daß er beim Überlauf von 255 -> 0
einen Interrupt auslöst.
Dann wird in ein Unterprogramm (genannt ISR) gesprungen und die Befehle dort ausgeführt.
Dieses Unterprogramm wird mit dem Befehl

On Timer Unterprogramm


konfiguriert. Danach geht es mit dem Hauptprogramm nahtlos weiter.


Bild Timer1.JPG


Hier ein Programmbeispiel:

$regfile = "m8def.dat"
$Crystal=1000000
$hwstack=40
$swstack=16
$framesize = 32

Config Portb.1 = Output

Config Timer0 = Timer , Prescale = 1024 'Timer0 als Timer mit Vorteiler 1024
On Timer0 Unterprogramm 'Wenn Timer überläuft 255->0 nach Unterprogramm springen
Enable Timer0 'Timer0 einschalten
Enable Interrupts ' Interrupts einschalten

Do


Loop
End

Unterprogramm:

Toggle Portb.1

Return


Die maximale Timer-Zeit berechnet sich:
1 / Taktfrequenz AVR * Prescaler * Timerauflösung (8 oder 16bit)
Hier 1 / 1000000 * 1024 * 255 = 0,26s

Wenn man nicht die maximale Zeit des Timer nutzen will lädt man ihn vor.
Dieser läuft dann nicht von 0 bis 255 sondern z.b. von 125 bis 255 und löst dann
den Interrupt aus.
Dies erreicht man indem man als erste Anweisung in der Interrupt -Routine (ISR)
Timer0=125 schreibt.

Das Programm sieht dann so aus:

$regfile = "m8def.dat"
$crystal = 1000000
$hwstack = 40
$swstack = 16
$framesize = 32

Config Portb.1 = Output

Config Timer0 = Timer , Prescale = 1024 'Timer0 als Timer mit Vorteiler 1024
On Timer0 Unterprogramm 'Wenn Timer überläuft 255->0 nach Unterprogramm springen
Enable Timer0 'Timer0 einschalten
Enable Interrupts ' Interrupts einschalten

Do


Loop
End

Unterprogramm:
Timer0 = 125
Toggle Portb.1

Return


Man kann auch in der Interrupt Routine (ISR) eine Variable hochzählen um längere Zeiten zu erreichen.

$regfile = "m8def.dat"
$crystal = 1000000
$hwstack = 40
$swstack = 16
$framesize = 32

Dim Zahl As Word
Config Portb.0 = Output
Config Timer0 = Timer , Prescale = 1024 'Timer0 als Timer mit Vorteiler 1024
On Timer0 Unterprogramm 'Wenn Timer überläuft 255->0 nach Unterprogramm springen
Enable Timer0 'Timer0 einschalten
Enable Interrupts ' Interrupts einschalten

Do
If Zahl >= 9000 Then
Toggle Portb.0
Zahl = 0
End If


Loop
End

Unterprogramm:
Incr Zahl


Return



Für Timer1 gilt das gleiche, wie für Timer0. Nur das Timer1 von 0 bis 65535 zählen kann.

PWM - Pulsweitenmodulation mit Timer


Mit den Timern des AVR kann man auch einfach ein PWM Signal erzeugen.
Was ist PWM? PWM ist ein Signal bei dem die Frequenz gleich bleibt, aber die An und Aus-Zeiten verändert werden.

Der Atmega8 hat gleich 3 davon. An PortB.1 und PortB.2 befinden sich die PWM-Ausgänge 1a und 1b (OC1A und OC1B), welche durch
den Timer 1 gesteuert werden können. An PortB.3 ist ein PWM-Ausgang, welcher durch Timer 2 gesteuert werden kann (OC2).
Der als PWM konfigurierte Timer zählt ständig von 0 bis zum Maximalwert und dann wieder zurück auf null.
Dies geschieht unabhängig vom Hauptprogramm.

Bei Timer1 kann man diesen Maximalwert mit PWM=8, PWM=9 oder PWM=10 angeben..
Bei Timer2 entfällt dies da er nur ein 8Bit Timer ist und sein Maximalwert 255 ist.
Durch festlegen eines Compare/Vergleich - Wertes kann festgelegt wann der Timer eine Aktion tun soll.

Bild pwm.JPG

Hier die Config Anweisung mit Timer1:

Config Timer1 = Pwm , Pwm = 8 , Compare A Pwm = Clear Up , Prescale = 1

Als erstes teilen wir dem AVR mit welchen Timer wir benutzen wollen.
Dann sagen wir ihm mit PWM=8, daß wir eine maximale Auflösung von 8 Bit = 255 haben wollen.
Mit PWM=10 hätte wir eine feinere Auflösung von 0 bis 1023.

Compare A Pwm = Clear Up bedeutet, daß der Timer bei erreichen des Vergleichswertes den Ausgang einschalten soll.
Compare A Pwm = Clear Down würde bedeuten, daß der Timer bei erreichen des Vergleichswertes den Ausgang ausschalten soll.

Compare A PWM heißt, daß Portb.1 (OC1a) ausgewählt ist.
Compare B PWM heißt, daß Portb.2 (OC2b) ausgewählt ist.

Bei Timer2 fällt der Buchstabe A oder B weg, da dieser Timer nur einen PWM Ausgang steuert.

Und zum Schluß kann man noch einen Vorteiler für die Taktfrequenz wählen.

Im Programm setzen wir den Compare / Vergleichswert mit:

Für Timer1
PWM1a= Variable oder Zahl oder Compare1a = Variable oder Zahl
PWM1b= Variable oder Zahl oder Compare1b = Variable oder Zahl

Für Timer2
PWM2= Variable oder Zahl oder Compare2 = Variable oder Zahl




Hier das Beispielprogramm "Leuchtturmfeuer"

$regfile = "m8def.dat"
$crystal = 1000000
$hwstack = 40
$swstack = 16
$framesize = 32

Config Portb.1 = Output
Dim A as Byte


Config Timer1 = Pwm , Pwm = 8 , Compare A Pwm = Clear Down , Prescale = 1

Do

For A = 1 To 150 Step 2 'Aufdimmen
Pwm1a = A
Waitms 30
Next

Pwm1a = 255 'Volle Helligkeit
Waitms 100

For A = 150 To 1 Step -2 'Abdimmen
Pwm1a = A
Waitms 30
Next

Loop
End


In diesem Programm wird eine LED mit Vorwiderstand an Portb.1 angeschlossen.
Miit Timer1 wird die Led aufgedimmt.
Dann blitzt sie kurz auf und dimmt langsam wieder ab.


Timer als Zähler/Counter



Mit den Timern lassen sich auch externe Takte zählen.
Diese Signale müssen entprellt sein
Zum Beispiel mit einem 100nF Kondensator parallel zum Taster.

Der Eingang für Timer0 ist beim Atmega8 Portd.4 (genannnt T0) und für den
Timer1 Portd.5. (genannt T1)

Der Eingang des Timer wird ganz normal deklariert

Beispiel für Tiimer0:

Config Portd.4=Input
Portd.4=1 ‘Pullup Widerstand einschalten
Config Timer0=Counter


Jetzt bestimmt man noch, ob bei steigender oder fallender
Flanke gezählt werden soll.

Edge = Falling (fallende Flanke)
Edge = Rising (steigende Flanke)


Hier ein kommplettes Beispiel zum Zählen eines Impulses
mit Timer0 an PortD.4 bei fallender Singnalflanke.
Der Wert von Timer0 wird über die serielle Schnittstelle ausgegeben.

$regfile = "m8def.dat"
$Crystal=1000000
$hwstack=40
$swstack=16
$framesize=32
$baud = 2400

Config Portd.4 = Input
Portd.4 = 1 'Pullup-Widerstand

Config Timer0 = Counter , Edge = Falling

Do
Print Timer0
Loop
End


Jetzt zählt dieser Zähler aber nur bis 255 und läuft dann über auf 0.
Deshalb muß man ihn softwaremäßig erweitern.
Genauso wie bei der Timerfunktion richten wir eine Interruptroutine
ein und zählen dort die Überlaüfe des Timer.

Beispiel:

Der Timer ist drei mal übergelaufen und hat einen Wert von 120.
Dann hat an dem Eingang
3 mal 255 = 765 plus den Timerwert von 120 = 885 Impulse angelegen.

Hier ein Progammbeispiel:

$regfile = "m8def.dat"
$Crystal=1000000
$hwstack=40
$swstack=16
$framesize=32
$baud = 2400
Dim Wertueberlauf As Byte
Dim Wert As Long
Config Portd.4 = Input
Portd.4 = 1 'Pullup-Widerstand

Config Timer0 = Counter , Edge = Falling 'Timer0 als Zähler mit fallender Flanke
On Timer0 Ueberlauf
Enable Timer0 'Timer0 einschalten
Enable Interrupts 'Interrupts global einschalten

Do
Wert = Wertueberlauf * 255
Wert = Wert + Timer0
Print Wert
Loop
End

Ueberlauf:

Incr Wertueberlauf

Return