Forum: Compiler & IDEs Timer in C/ATmega8


von Jan B. (Gast)


Lesenswert?

Hallo,
ich hoffe, ich bin im richtigen Unterforum.
Ich möchte gerne einen Timer auf einem ATmega8 realisieren. Dieser soll 
vom Programmstart aus hochzählen, z.B. in ms. Das hat den Vorteil, dass 
ich mir daraus beliebig viele Timer (natürlich nicht so genaue, nur für 
ungefähre Aufgaben, damit ich mit _delay_ms(); mein Programm nicht 
aufhalten muss) generieren lassen kann. Z.B. läuft mein Programm jetzt 
seit 100sek. Jetzt möchte ich z.B alle 10sek eine Sache machen, 
natürlich will ich den Rest meines Programms nicht aufhalten. Dann kann 
ich einfach sagen


zeitStart = momentaneZeit; //zeitStart=100

while(true)
{
  if((momentaneZeit-zeitStart)>10) /*if(100-100>10), nach 10sek 
if(110-100>10), also nach 10sek*/
  {
    Aufgabe erledigen

    zeitStart=momentaneZeit;
  }

  Sonstige Aufgaben
}


Ich hoffe, ihr habt das Prinzip verstanden. Die Idee habe ich vom LEGO 
MINDSTORMS. Jetzt die theoretische Umsetzung:
Ich habe einen 8MHz Quarz. Ich stelle meinen Vorteiler vom Timer 0 auf 
1024 ein. 8000000/1024=7812,5, also 7,8125kHz, also 7,8125 Mal in der 
Sekunde wird das Interrupt (das oder der Interrupt?) ausgelöst. Der 8Bit 
Timer läuft also in 0,128sek durch und löst dann ein Interrupt aus. Wenn 
das Interrupt ausgelöst wird, muss ich also zu meiner Variable 0,128 
dazu addieren. Jetzt habe ich einen Timer in 128ms Schritten.
Ich habe aber auch noch den eigentlichen 8Bit Timer, der in diesen 128ms 
von 0-255 hoch zählt. Immer, wenn sich diese Zahl ändert (also von 0 auf 
1, von 1 auf 2 [...], von 255 auf 0 usw.) können noch 0,128/256, also 
0,0005, also 500us, dazu addiert werden. Ich habe also jetzt einen 
Endlostimer (natürlich nur bis zum Overflow der Variable) mit einer 
Auflösung von 500ms.
Ist das soweit alles richtig gedacht, funktioniert das theoretisch?
Jetzt die praktische Umsetzung:
Hier nur mein Void zum Initialisieren, die Variable und das Interrupt. 
Den restlichen Code kann man sich dazu denken, mein Projekt ist sowieso 
etwas größer. Wenn mein restlicher Code zwingend notwendig ist, stelle 
ich ihn auch rein. Momentan habe ich das mit den halben ms noch nicht 
umgesetzt, erstmal Schritt für Schritt:


uint64_t tick;

void init_TIMER()
{
  TCCR0 =(1<<CS02) | (1<<CS00); //Vorteiler 1024
  TIMSK |= (1<<TOIE0);
}

ISR(TIMER0_OVF_vect)
{
  tick+=0.128;
}


init_timer wird eben im int main vor der Endlosschleife aufgerufen, dann 
einfach die Abfragen, wie oben demonstriert.
Welche Variable muss ich nehmen? Kann ich einfach die uint64_t nehmen, 
oder benötige ich ein float? Ist ja ein Dezimalbruch... WIe hoch kann 
mein Timer mit einem float zählen?
Das Ganze funktioniert grundsätzlich, aber die Zeit haut nicht so ganz 
hin. Bevor ich da irgendwelche ausprobierten Zahlen auf tick aufaddiere, 
möchte ich wissen, wo der Rechenfehler ist, oder was noch zu beachten 
ist.
Ich hoffe, ihr habt das Prinzip und das Problem verstanden.
Vielen Dank für Eure Hilfe schonmal im Vorraus und
Viele Grüße
Jan

von Karl H. (kbuchegg)


Lesenswert?

> Ich habe einen 8MHz Quarz. Ich stelle meinen Vorteiler vom Timer 0
> auf 1024 ein. 8000000/1024=7812,5, also 7,8125kHz, also 7,8125 Mal
> in der Sekunde wird das Interrupt (das oder der Interrupt?) ausgelöst.

Der Interrupt.
Und nein, deine Berechnung stimmt nicht.
7812.5 mal in der Sekunde zählt der Timer um 1 weiter. Der Timer muss 
aber erst bis 255 zählen (weil der Timer 0 ein 8 Bit Timer ist), ehe er 
mit dem nächsten Zählschritt einen Überlauf auslöst.

Du hast also
8000000/1024/256 = 30.51 Überläufe in der Sekunde. Oder anders gesagt, 
hast du alle 1/30.51 = 0.032 Sekunden einen Überlauf Interrupt.


(die Idee vom Mindstorm ist nicht so prickelnd.

Machs besser so:
1
volatile int16_t timer1;
2
3
ISR( ..... )
4
{
5
  // wird zb 30 mal in der Sekunde aufgerufen
6
7
  if( timer1 > 0 )
8
    timer1--;
9
}
10
11
int main()
12
{
13
   Timter initialisieren
14
15
   timer1 = -1;     // den privaten Timer nicht laufen lassen
16
17
   while( 1 ) {     // Hauptschleife
18
19
     if( irgendein Ereignis ) {
20
        cli();
21
        timer1 = 10 * 30;     // 10 wegen 10 Sekunden,
22
                              // 30 weil 30 ISR Aufrufe in der Sekunde passieren
23
        sei();
24
25
        // jetzt läuft der 'Timer'. 30 mal in der Sekunde wird die Variable
26
        // timer1 um 1 verringert. Ist sie wieder bei 0 angelangt, dann sind
27
        // die hier eingestellten 10 Sekunden vorbei.
28
29
     }
30
31
     if( timer1 == 0 ) {    // der eigene Software-Timer ist abgelaufen
32
       timer1 = -1;         // auf -1 setzen, damit dieser if nicht noch
33
                            // ein 2-tes mal anschlägt
34
35
       .... tu, was es zu tun gibt, wenn der 10-Sekunden Timer abgelaufen ist.
36
   }
37
}

Die Idee ist es also, nicht vorwärts zu zählen, sondern wie eine Eieruhr 
in der ISR eine 'Zeit-Variable' rückwärts zählen zu lassen. In main() 
wird die Variable auf einen Wert größer 0 gesetzt, woraufhin sie in de 
ISR mit einer bekannten Rate wieder heruntergezählt wird. Ist die Uhr 
abgelaufen (auf 0), dann wird die entsprechende Aktion ausgelöst.

PS: Du kannst natürlich nach Ablauf der Zeit die Eieruhr sofort wieder 
neu aufziehen, indem du der Variablen timer1 gleich wieder einen neuen 
Countdownwert zuweist.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.