Forum: Mikrocontroller und Digitale Elektronik AVR Timer1Overflow problem!


von Sanchez (Gast)


Lesenswert?

Hallo Freunde,

Ich hab ein Impuls Aufnahme Code aus dem Tutorium Anweisungen 
geschrieben, bei dem Flanken Zeitintervalle festgehalten werden sollte. 
Die Zeitintervalle sind für Standardabweichung meiner Messungen gedacht.

Tools: ATmega32, STK500, LCD 16x1 und FCPU ist 4MHz/64 teiler

Nun das Problem ist, dass bei der Aufnahme von kleinen Impuls Intervalle 
(kleiner 1s) nach jeden n zahl Intervall eine Overflow eintritt und 
meine Werte um 1s vergrößert.

Ich vermute dass der Overflow Zähler unabhängig von der Impuls Aufnahme 
arbeitet und dadurch eine Überschneidung entsteht. Vieles habe ich 
versucht um Overflow mit dem „Starttime und Endtime“ in Verbindung zu 
setzen, aber keine Erfolg…

Weiß jemand wo der knack Punkt ist.. Wie kann ich das Problem 
beseitigen?

Danke
Gruß
Sanchez

1
ISR( TIMER1_CAPT_vect )
2
{
3
  Si_wert++;
4
    static unsigned char ErsteFlanke = TRUE;
5
  
6
  if( ErsteFlanke == TRUE )
7
  {
8
    Starttime = ICR1;    // Startzeit= ICR 1
9
    Durchlauf = TRUE;
10
    ErsteFlanke = FALSE;         // Die naechste Flanke ist das Ende der Messung
11
  }
12
  else
13
  {
14
    Endtime = ICR1;            // Stopzeit für die Messung= ICR1
15
    Durchlauf = TRUE;        // Eine vollst?ndige Messung. Sie kann ausgewertet werden 
16
    ErsteFlanke = TRUE;          // Bei der naechsten Flanke beginnt der naechste Messzyklus
17
  }
18
}
1
ISR( TIMER1_OVF_vect )
2
{
3
   Overflow++;
4
}
1
void Impulse_berechnen(void)
2
{
3
  Zeit_ti = (Endtime) - (Starttime);
4
    if (Overflow != 0)
5
    {
6
      Zeit_ti = (Overflow * 65536) + Zeit_ti;
7
    }
8
  
9
  Zeit1_ti =(Zeit_ti*1000)/65535; //        Zeit_ti in sekunden   
10
  .
11
  .
12
  .
13
  Zeit_ti = 0 ;
14
  Zeit1_ti = 0 ;
15
  Overflow = 0;

von Kluchscheißernder N. (kluchscheisser)


Lesenswert?

Wenn ich Dich richtig verstanden habe, willst Du Impulszeiten messen. 
Dazu ist der Timer-Überlauf-Interrupt denkbar ungeeignet.

Schau Dir mal den ICP-Interrupt an (Input-Capture), der ist genau für 
diesen Zweck vorgesehen. Notfalls kann man noch Software-ICP mittels 
externem Interrupt machen, ist nicht ganz so exakt, erlaubt aber mehrere 
Messkanäle.

Die Vorgehensweise ist Folgende:
Bei steigender Flanke liest man den Timer aus und sichert den 
Zeitstempel als Startwert. Nutzt man den ICP-Interrupt, dann liest man 
das Input-Capture-Register aus, denn dies wurde von der Hardware bereits 
beim Auftreten der Flanke aktualisiert. Nutzt man den externen 
Interrupt, muss man halt den Timerstand auslesen. Zusätzlich ist noch 
auf fallende Flanke umzuschalten.
Bei fallender Flanke liest man den Timer aus und erhält den Zeitstempel, 
der neuer (größer, wenn man sich die Zeit als "fortlaufende Zahl" 
vorstellt) als der Zeitstempel des Startwertes ist. Die Impulsdauer 
erhält man durch Subtraktion (Endwert-Startwert). Da der Zählumfang des 
Timers nur 16 Bit beträgt, braucht man sich dabei nicht um Überläufe zu 
kümmern, die korrigieren sich von alleine. Sieh die 16 Bits des Timers 
als die unteren 16 Bit einer riesengroßen Zahl, die die Timertakte seit 
dem Urknall zählt... ^^

Ist die Periodendauer zu messen, dann triggert man nur auf steigende 
Flanke und sichert den neuen Zeitstempel als alten für die nächste 
Messung. Also neuen Wert einlesen, Kopie davon anlegen, von Kopie alten 
Zeitstempel subtrahieren und als Periodendauer sichern, neuen 
Zeitstempel in alten kopieren, fertig.

Mit ein paar Variablen mehr kann man natürlich auch Impulsdauer, 
Impulspause und Periode separat ermitteln.

MfG

von Sanchez (Gast)


Lesenswert?

Danke fürs Antworten

Ich verwende ja ICP-Interrupt und meine Code Basiert sich auf code von 
der Forum seite:
Beitrag "Input Capture Pin (ICP) auslesen ( Frequenz messen)"

In meine code gehe ich ja auch so vor wie du es beschrieben hast (glaube 
ich!), aber das man sich um überläufe nicht kummern soll verstehe ich 
nicht wie.

16bit timer = 65535 = 1s = 1 Overflow (so ist das doch oder?)

Also beim Intervalle größer 1s müssen auch Overflows beachtet werden.

Das alles passiert auch schon und gut, aber nach bestimmte Anzahl von 
flanke triftt eine Overflow zuviel, was nicht passieren dürfte.

Ich vermute das nach jede volle sekunde (1s=65535) eine Überlauf gezählt 
wird. Und Falls der erste flanke beim 65300 passiert und der zweite beim 
250 er sieht das als eine überlauf.  (Overflow== 65535)

Das ist was ich verstanden habe und vermute.. lege ich daneben oder 
darauf? ;)
Gruß

von Karl H. (kbuchegg)


Lesenswert?

1
 Zeit_ti = (Endtime) - (Starttime);
2
    if (Overflow != 0)
3
    {
4
      Zeit_ti = (Overflow * 65536) + Zeit_ti;
5
    }

diese Berechnung ist falsch.

Du musst die Anzahl der Overflows schon berücksichtigen. Allerdings ist 
die Sache die:

Wenn Endtime kleiner als Starttime ist, dann wurde 1 Overflow zuviel 
gezählt und du musst nur 1 Overflow weniger als festgestellt 
berücksichtigen. Ist Endtime größer/gleich Starttime dann ist die Anzahl 
der Overflows korrekt und wird auch so verrechnet.

Am besten macht man sich das klar, wenn man sich einen Zähler vorstellt, 
der zb nur bis 8 zählen kann.

Angenommen Starttime wäre 3 gewesen.
Der Zähler zählt jetzt weiter

4 5 6 7

Jetzt kommt der Interrupt für das Ende. Was haben wir?

Starttime :=  3
Endtime   :=  7
Anzahl Overflows := 0

Die Anzahl der gezählten Ticks ist dann   7 - 3 -> 4
was ja auch korrekt ist.

Anderes Beispiel
Starttime sei wieder 3 und der Zähler tickt

4 5 6 7 0 1

Starttime := 3
Endtime   := 1
Anzahl Overflows := 1

Da diesmal Endtime kleiner als Starttime ist, muss man die Overflows 
prinzipiell berücksichtigen, allerdings um 1 weniger als detektiert, 
weil durch die unsigned Rechnerei 1 Overflow automatisch berücksichtigt 
wird.

   1 - 3  + 0 * 8 -> 6

Neues Beispiel

Starttime 3, der Zähler tickt

4 5 6 7 0 1 2 3 4

Starttime := 3
Endtime   := 4
Anzahl Overflows: 1

Hier ist jetzt Endtime größer als Starttime und die Anzahl der Overflows 
geht so wie sie ist in die Berechnung ein

 4 - 3   + 1 * 8  ->   1 + 8  -> 9

zähl die Ticks nach, stimmt auffallend.

Wenn der Timer dann mehr als einmal überläuft kann man das Ganze weiter 
durchprobieren und ich rate dir auch dazu, das am Papier auszuprobieren.

von Kluchscheißernder N. (kluchscheisser)


Lesenswert?

Gut, dann habe ich Dich falsch verstanden, sorry.

Zum Überlauf...
Wenn mir der Zählumfang des 16-Bit-Timers reicht (davon gehe ich aus, 
ansonsten wähle ich einen besser passenden Vorteiler), muss ich mich 
nicht um den Überlauf kümmern, zumindest nicht in ASM. Ich subtrahiere 
den Startwert vom Endwert und erhalte die Differenz. Da ich die Zahlen 
als unsigned betrachte, gibt es immer ein positives Ergebnis. Da der 
Zähler des Timers als geschlossener Ring arbeitet, bleibt das Ergebnis 
auch dann richtig, wenn bei der Subtraktion das Borrow-Flag (in Form des 
Carry-Flags) in Folge eines Unterlaufes beeinflusst wird.

Wie man das in C notieren muss, entzieht sich meiner Kenntnis, ich 
mach's nunmal in ASM. Das wäre aber nicht der erste Fall, wo man in C 
arge Verrenkungen machen muss, um Dinge zu realisieren, die in ASM ganz 
einfach sind.

MfG

von Sanchez (Gast)


Angehängte Dateien:

Lesenswert?

Danke fürs eure Ausführliche Erklärungen, ich habe es leider Bisschen zu 
spät gesehen und während  dieser zeit hab meine Code um einiges 
verändert.
Mit
if(Endtime < Starttime)
  {
    Overflow--;
  }
Hab ich das Erste Problem beseitigt. Aber danach kamm eine weitere 
Overflow Problem der um 65 mal heftiger ist als das davor.  Und es 
lässt mich nicht schlaffen.

Ich hab einen Simulierte Eingang Frequenz von ca. 70- 100Hz. Für 
Bestimmung meine werte, wird Sekunde, Impuls Anzahl, Überlauf Anzahl und 
Summe aller Zeiten auf eine 16x1 LCD angezeigt.

Das Sekunden Zähler und Summe aller Zeiten sollen parallel hoch zählen. 
Das passiert auch paar Sekunden lang (Unterschiedlich), aber dann 
schlagartig springt Summe der Zeiten um 65s Hoch.

Wenn ich denn Overflow komplett auskommentiere, dann Läuft alles 
perfekt. Wo hab ich denn mein Fehler. Ich komm nicht drauf.

Na gut ich gibs erst mal auf, schlaffen ist angesagt..  meine eigene 
Overflow ist schon am durchbrennen.. ;)

von Sanchez (Gast)


Lesenswert?

Hallo,

Hat Jemand eine Idee woran das Problem Liegt..

von Karl H. (kbuchegg)


Lesenswert?

Sanchez schrieb:
> Hallo,
>
> Hat Jemand eine Idee woran das Problem Liegt..

NOch nicht.

Du solltest aber hier
1
ISR( TIMER1_CAPT_vect )
2
{  
3
  Si_wert++;
4
    
5
    Endtime = ICR1;            // Stopzeit für die Messung= ICR1
6
    Durchlauf = TRUE;        // Eine vollst?ndige Messung. Sie kann ausgewertet werden 
7
}

den kompletten Satz aktueller Daten (Timerwert + Overflowert) 
wegspeichern. Ansonsten hast du nämlich keine Garantie, dass nicht in 
der Zwischenzeit vom Capture Interrupt bis dann endlich die 
Auswertefunktion aufgerufen wird, sich die Anzahl Overflows verändert.


Du hast da ein bischen naiv überall implizit die Annahme drinnen, dass 
dein Programm alles in 0-Zeit machen kann bzw. deine zu messende 
Frequenz so freundlich ist und darauf wartet, dass dein Programm mit der 
Auswertung fertig ist ehe es dann wieder weiter geht.

Auch gibt es da noch ein Problemchen,
2 Interrupts können ja nicht gleichzeitig ausgeführt werden, sondern nur 
hintereinander. Wenn nun aber der CaptureInterrupt bei einem Zählerstand 
vom Maximum Timer erfolgt, wurde dann der Overflow Interrupt schon 
ausgeführt oder nicht (ich weiß es nicht auswendig)

Ganz andere Frage: Kann es dir überhaupt passieren, dass deine 
Timerdifferenz bei den dich interessierenden Frequenzen größer als 65535 
werden kann? Wenn das eh nicht passieren kann, brauchst du auch die 
Overflows nicht berücksichtigen und damit umschiffst du alle damit 
zusammenhängenden Probleme.

von Sanchez (Gast)


Lesenswert?

Wauuu... danke Karl,

Aber ich bin überfordert, das hier ist meine aller erste code in C und 
überhaupt Programmieren..

>den kompletten Satz aktueller Daten (Timerwert + Overflowert)
>wegspeichern. Ansonsten hast du nämlich keine Garantie, dass nicht in
>der Zwischenzeit vom Capture Interrupt bis dann endlich die
>Auswertefunktion aufgerufen wird, sich die Anzahl Overflows verändert.

Muss das nach jede Flanke gemacht werden, verstehe nicht ganz genau wie 
das gehen soll..

>Du hast da ein bischen naiv überall implizit die Annahme drinnen, dass
>dein Programm alles in 0-Zeit machen kann bzw. deine zu messende
>Frequenz so freundlich ist und darauf wartet, dass dein Programm mit der
>Auswertung fertig ist ehe es dann wieder weiter geht.

Heißt das Programm Aufbau umschreiben so dass der Berechnung parallel zu 
Flanken Aufnahme abläuft?

>Auch gibt es da noch ein Problemchen,
>2 Interrupts können ja nicht gleichzeitig ausgeführt werden, sondern nur
>hintereinander. Wenn nun aber der CaptureInterrupt bei einem Zählerstand
>vom Maximum Timer erfolgt, wurde dann der Overflow Interrupt schon
>ausgeführt oder nicht (ich weiß es nicht auswendig)

2te Interrupt ist der Sekunden Zähler gemeint? Ich brauche aber eine 
Sekunden Zähler.. Kann man nicht die beide mit einem Interrupt 
realisieren?

>Ganz andere Frage: Kann es dir überhaupt passieren, dass deine
>Timerdifferenz bei den dich interessierenden Frequenzen größer als 65535
>werden kann? Wenn das eh nicht passieren kann, brauchst du auch die
>Overflows nicht berücksichtigen und damit umschiffst du alle damit
>zusammenhängenden Probleme.

Ich werde größere Frequenzen haben..

Das Code am Anhang ist nur eine Ausschnitt von ganzem, den Rest wollt 
ich nicht zeigen da sonst keiner durch blicken wird. Also das Ganze soll 
eine Windgeschwindigkeit Messgerät werden. In dem eigentlichen Code habe 
ich noch Datenspeicherung auf der SD-Karte und noch Uhr und Datum.

Es gibt einmal die variable Sekunde das für gesamt zeit zählen dient: 
der macht seine Arbeit vernünftig. Und es gibt noch variable ss, der die 
Uhr hoch zählen muss. Obwohl ss und Sekunde an dem selben Interrupt 
angeschlossen sind, ss kann nur bis 12 hoch zählen dann bleibt er stehen 
merwürdig...

von Sanchez (Gast)


Lesenswert?

Hallo

Ich wollte nur wiesen ob ich mir Hoffnung machen kann oder nicht.. :)

Karl hast du vielleicht was raus..

Danke euch

von Kluchscheißernder N. (kluchscheisser)


Lesenswert?

Bei Windstärkemessung interessiert nicht unbedingt die Dauer jedes 
einzelnen Impulses (oder Impulsabstandes), denn in den Impulsen ist ja 
keine serielle Information codiert. Es interessiert vielmehr die Anzahl 
der Impulse pro Zeiteinheit (z.B. Sekunde). Daher würde ich es nicht 
über den Input-Capture machen, sondern einen Timer/Counter als Zähler 
mit externem Takteingang nutzen. Ein anderer Timer dient dann als 
Zeitnormal (Sekundentakt) zum Auslesen und Löschen des Zählers.

Ein Sekundentakt lässt sich auch mit einem 8-Bit-Timer erzeugen, 
notfalls als Hundertstelsekunde mit nachgeschaltetem Zähler in Software 
(Zählvariable). Also wird Timer0 zum Erzeugen des Sekundentaktes 
genutzt.

Timer1 wird als Zähler genutzt, sein Zähleingang ist der T1-Pin. Falls 
seine 16 Bit nicht ausreichen kann man den OVF-Interrupt des Timer1 
nutzen, um eine Zählvariable hochzuzählen, die den Timer auf 24 oder 32 
Bit aufstockt.

Im Interrupt des Timer0 (also im Sekundentakt) werden Zählerstand von 
Timer1 und Zählvariable gesichert (in andere Variablen kopiert) und auf 
0 gesetzt. Zusätzlich wird der Mainloop über einen Merker (Flag, 
Semaphore, Zustandsvariable, Boolean-Variable, Jobauftrag) mitgeteilt, 
dass ein neues Ergebnis vorliegt.

Die Mainloop fragt zyklisch alle Jobflags ab und verzweigt zu einem Job, 
wenn ein Auftrag vorliegt. Hier ist es das Jobflag für die neue Sekunde, 
das zum Aufruf der Handler-Routine führt. Diese macht nun Folgendes:
- Jobauftrag (Merker) löschen (Job wird ja erledigt)
- gesicherten Timerstand und Zählvariable zu einer Zahl (24 oder 32
  Bit) zusammensetzen
- Ergebnis skalieren, also so umrechnen, dass es ein Mensch versteht
- Ergebnis per UART versenden
- Ergebnis am LCD anzeigen
- Entscheidungen treffen (Markisen einfahren, (A)Lärm auslösen)
- ...

Das Zählen der Impulse erfolgt dabei in Hardware, also ohne Zutun von 
Programmcode (außer Initialisierung). Lediglich Überläufe des Zählers 
nach jeweils 65536 Impulsen lösen einen Interrupt aus, in dem der 
High-Teil hochgezählt wird. Das ist vernachlässigbar wenig Prozessorlast 
und tritt, wenn überhaupt, nur bei starkem Sturm auf. Ich würde sogar 
auf den Übertrag verzichten und evtl. mit einem variablen 
Abfrage-Intervall arbeiten.

Also die Grundfunktion ist sehr einfach realisierbar und es bleibt 
verdammt viel Spielraum für Erweiterungen und Gimmicks. Da braucht es 
auch keinen ATMega32 für, das macht ein ATTiny2313 mit links und 40 
Fieber.

MfG

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.