Forum: Mikrocontroller und Digitale Elektronik Problem Frequenzmessung mit Timer0


von Matze (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,
ich bin dabei eine Frequenz mit dem Timer0 des AtMega8 zu messen. Dabei 
lasse ich den Timer0 ständig laufen, und zähle die Überläufe mit. Die zu 
messende Frequenz, liegt an INT0 an, und erzeugt bei jeder steigenden 
Flanke einen Interrupt. In diesem Interrupt wird dann der aktuelle TCNT0 
Wert, sowie die Überläufe gesichert.
Die Frequenz, kann so bis ca 1500 Hz mit einer Genauigkeit von 1Hz 
erfasst werden.
Nun zu meinem Problem. Wenn ich eine Frequenz wähle, bei der der externe 
Interrupt und der Timer Overflow Interrupt des Timer0 theoretisch 
gleichzeitig auftreten, dann springt mein Messwert hin und her. Durch 
Debugging, habe ich herraus gefunden, dass das Überlauf Flag int0flag 
hochgezählt wird, also die ISR (TIMER0_OVF_vect) noch ausgeführt wird, 
aber der TCNT0 Wert verbleibt auf 255, so das die Messung um ca. 255 
Timerwerte verfälscht wird.
Was kann da schief gehen?

Danke für eure Infos

grüße Matthias

von m.n. (Gast)


Lesenswert?

Sieh Dir im Beispielprogramm die ISR (PCINT0_vect) und ISR 
(TIMER1-OVF_vect) an.
Beitrag "Frequenz / Drehzahl, 4-stell. 7-Segm.-LCD, ATtiny45"
Mit INT0 und Timer0 funktioniert die Auswertung des Überlaufs identisch.

von Thomas E. (thomase)


Lesenswert?

Warum nimmst du nicht Timer1?

Mit ICP kann der das fast ganz alleine.

mfg.

von Matthias (Gast)


Lesenswert?

Hallo danke erstmal für eure Antworten...


Thomas Eckmann schrieb:
> Warum nimmst du nicht Timer1?
>
> Mit ICP kann der das fast ganz alleine.
>
> mfg.
Timer1 erzeugt für zwei Motoren eine PWM außerdem möchte ich zwei 
Frequenzen messen, also den INT1 auch noch verwenden...

@m.n. weist du den was genau bei meinem Programm im beschriebenen Fall 
passiert?

Grüße

von m.n. (Gast)


Lesenswert?

Matthias schrieb:
> @m.n. weist du den was genau bei meinem Programm im beschriebenen Fall
> passiert?

Während des INTO-Interrupts wird der Überlauf nicht berücksichtigt, wenn 
er nahezu gleichzeitig auftritt und nicht bearbeitet werden kann. Darum 
muß man 'zu Fuß' nachsehen, ob das passiert ist.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Matze schrieb:
> Nun zu meinem Problem. Wenn ich eine Frequenz wähle, bei der der externe
> Interrupt und der Timer Overflow Interrupt des Timer0 theoretisch
> gleichzeitig auftreten, dann springt mein Messwert hin und her. Durch
> Debugging, habe ich herraus gefunden, dass das Überlauf Flag int0flag
> hochgezählt wird, also die ISR (TIMER0_OVF_vect) noch ausgeführt wird,
> aber der TCNT0 Wert verbleibt auf 255, so das die Messung um ca. 255
> Timerwerte verfälscht wird.
> Was kann da schief gehen?

 In der ISR(INT0) TCNT0 nicht auf Null stellen, sondern dessen Wert
 als StartWert übernehmen, beim Ausrechnen einfach abziehen.
 Sollte deinen Problem lösen, glaube ich.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Marc Vesely schrieb:

>  In der ISR(INT0) TCNT0 nicht auf Null stellen, sondern dessen Wert
>  als StartWert übernehmen, beim Ausrechnen einfach abziehen.
>  Sollte deinen Problem lösen, glaube ich.

Nicht wirklich.
Das Problem ist die Race Condition. Wenn der µC in einer ISR steckt, 
läuft der Timer ja weiter. Und wenn das Timing 'passt' kann es 
passieren, dass nach dem Einsprung in den INT0_vec gerade ein Timer 
Overflow passiert. Der wird per Flag korrekt registreiert, aber die 
zugehörige ISR läuft klarerweise noch nicht, daher wird er dann auch 
nicht gezählt. Der Timer hat dann zb bereits den Wert 0 aber der 
Overflow wird hier
1
...
2
  actInt0Flag=int0Flag;   //save actually counter value of t0Flag
3
  actTCNT0=TCNT0;
noch nicht berücksichtigt, weil ja die Overflow-ISR noch nicht gelaufen 
ist.

Wenn ich mich recht erinnere macht Grossmeister PeDa das so, dass er 
sich da zusätzlich auch noch das Overflow-Flag ansiehst und dann den 1 
fehlenden Overflow auch noch mitzählt, wenn der TCNT klein ist.
Denn ansonsten kann es wieder passieren, dass er einen Overflow zu viel 
zählt
1
...
2
    TCNT sichern
3
    Overflow Flag ansehen
4
...

wenn der Overflow genau hier
1
...
2
    TCNT sichern
3
                     <--------------
4
    Overflow Flag ansehen
5
...
passiert.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Karl Heinz schrieb:
> Nicht wirklich.
> Das Problem ist die Race Condition. Wenn der µC in einer ISR steckt,

 Der TO sagt es aber anders:
Matze schrieb:
> Debugging, habe ich herraus gefunden, dass das Überlauf Flag int0flag
> hochgezählt wird, also die ISR (TIMER0_OVF_vect) noch ausgeführt wird,
> aber der TCNT0 Wert verbleibt auf 255, so das die Messung um ca. 255
> Timerwerte verfälscht wird.

von Karl H. (kbuchegg)


Lesenswert?

Marc Vesely schrieb:
> Karl Heinz schrieb:
>> Nicht wirklich.
>> Das Problem ist die Race Condition. Wenn der µC in einer ISR steckt,
>
>  Der TO sagt es aber anders:

das mag schon sein, dass er das anders sagt.

> Matze schrieb:
>> Debugging, habe ich herraus gefunden, dass das Überlauf Flag int0flag
>> hochgezählt wird, also die ISR (TIMER0_OVF_vect) noch ausgeführt wird,
>> aber der TCNT0 Wert verbleibt auf 255, so das die Messung um ca. 255
>> Timerwerte verfälscht wird.

Genau. Ein Timer bleibt bei laufendem Takt bei 255 stehen, wenn der den 
Overflow von 255 auf 0 macht?

Ich würde mal sagen: was immer er da debugged hat, er hat falsch 
debugged oder ist zum falschen Schluss gekommen.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Karl Heinz schrieb:
> Genau. Ein Timer bleibt bei laufendem Takt bei 255 stehen, wenn der den
> Overflow von 255 auf 0 macht?
>
> Ich würde mal sagen: was immer er da debugged hat, er hat falsch
> debugged oder ist zum falschen Schluss gekommen.
 Ja. Hab auch nicht sonderlich darüber nachgedacht, mehr über sein
 (unnötiges) zurückstellen von TCNT0.

 Atmel sagt:
 In normal operation the Timer/Counter OverflowFlag (TOV0) will be set
 in the same timer clock cycle as the TCNT0 becomes zero.

 Also:
 TCNT0 == 0, TOV0 == 1, Programm in der INT0 ISR ergibt + 1 Overflow.

von Karl H. (kbuchegg)


Lesenswert?

Marc Vesely schrieb:

>  Also:
>  TCNT0 == 0, TOV0 == 1, Programm in der INT0 ISR ergibt + 1 Overflow.

Klingt vernünftig.
Das TOV0 in diesem Fall zurücksetzen nicht vergessen. SOnst wird dieser 
Overflow hinterher von der anderen ISR noch mal gezählt :-)

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:
> Marc Vesely schrieb:
>
>>  Also:
>>  TCNT0 == 0, TOV0 == 1, Programm in der INT0 ISR ergibt + 1 Overflow.
>
> Klingt vernünftig.
> Das TOV0 in diesem Fall zurücksetzen nicht vergessen. SOnst wird dieser
> Overflow hinterher von der anderen ISR noch mal gezählt :-)

Wobei: ich würde mich da nicht auf einen Zählerstand von 0 kaprizieren. 
Bei kleinen Vorteilern kann der Timer dann auch schon weiter sein.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Karl Heinz schrieb:
> Das TOV0 in diesem Fall zurücksetzen nicht vergessen. SOnst wird dieser
> Overflow hinterher von der anderen ISR noch mal gezählt :-)
 Hab es schon vergessen. ;-D


> Wobei: ich würde mich da nicht auf einen Zählerstand von 0 kaprizieren.
> Bei kleinen Vorteilern kann der Timer dann auch schon weiter sein.
 Klingt vernünftig.
 Besonders beim c, wo wahrscheinlich noch etliche Register neben dem
 SREG gerettet werden.

von Karl H. (kbuchegg)


Lesenswert?

Was frische Luft nicht alles bewirken kann :-)

es gibt tatsächlich ein Szenario, bei dem er einen Zählerstand von 255 
erhält und 1 Overflow zu viel!

Allerdings sind die Dinge ein bischen verwickelter.
Wichtig: in der INT0 hat er das Overflow Flag nicht gelöscht, wohl aber 
den Timer auf 0 zurück gesetzt (eine Technik, der ich sowieso nichts 
abgewinnen kann, aber seis drum)

Der Code
1
ISR (INT0_vect)
2
{
3
  actInt0Flag=int0Flag;   //save actually counter value of t0Flag
4
  actTCNT0=TCNT0;     
5
  int0Flag=0;            //clear t0Flag 
6
  TCNT0=0;
7
  s[FREQ_TIMER0]=0;    //to indicate a missing clock source  
8
}
Angenommen der Timer steht auf 255. Noch ist kein Overflow passiert. 
int9Flag wird korrekt ausgelesen, ebenso TCNT0.
Aber jetzt passierts. Noch vor dem TCNT0 = 0 erreicht der Timer seinen 
Overflow. Das TOV0 wird gesetzt.
Die ISR läuft unbeeindruckt weiter. TCNT0 wird gesetzt, aber das löscht 
ja nicht das TOV0 Flag.
Die ISR wird verlassen. Das TOV0 Flag ist immer noch gesetzt, die 
Overflow ISR wird aufgerufen und zählt 1 Overflow.
Die Auswirkungen machen sich dann erst beim nächsten INT0 Ereignis 
spürbar. Da wurde jetzt ein Overflow registriert, den es in diesem 
Messzyklus nie gegegebn hat. Ist der Messzyklus dergestalt, dass der 
Timer wieder bis 255 kommt, dann haben wir genau die Situation. Obwohl 
nur 255 Timerticks vergangen sind, registriert das Programm 255 
Timerticks + 1 Overflow.

Ergo: Nach dem TCNT0 = 0 muss auf jeden Fall das TOV0 zurück gesetzt 
werden.

: Bearbeitet durch User
von Thomas E. (thomase)


Lesenswert?

Karl Heinz schrieb:
> Wobei: ich würde mich da nicht auf einen Zählerstand von 0 kaprizieren.
> Bei kleinen Vorteilern kann der Timer dann auch schon weiter sein.
Wozu dient die Abfrage überhaupt?

Wenn TOV0 gesetzt wird, ist TCNT0 in jedem Falle 0.
Wird das Flag abgefragt, hat der Timer allenfalls, je nach Prescaler, 
ein paar Takte weitergezählt. Das ist immer so. Also muss es nicht 
abgefragt werden.

Das Problem des "Überzählens" ist ein anderes:

TCNT0 = 245 und INT0 wird ausgelöst.

ISR(INT0_vect)
{
 value = TCNT0 (= 254,99999)
 //Jetzt kommt der Overflow
 if(TOV0) Overflow++; //Das reicht aber nicht
 //Sondern:
 //An dieser Stelle MUSS TCNT0 neu eingelesen werden
 if(TOV0)
 {
   value = TCNT0;
   Overflow++;
   ClearTOV0;
 }
}

mfg.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Thomas Eckmann schrieb:
> Karl Heinz schrieb:
>> Wobei: ich würde mich da nicht auf einen Zählerstand von 0 kaprizieren.
>> Bei kleinen Vorteilern kann der Timer dann auch schon weiter sein.
> Wozu dient die Abfrage überhaupt?

Weil es sein kann, dass der Timer hier, zu diesen Zeitpunkt
1
ISR (INT0_vect)
2
--------------------------------- hier
3
{
4
--------------------------------- oder hier
5
  actInt0Flag=int0Flag;   //save actually counter value of t0Flag
6
--------------------------------- oder hier
7
  actTCNT0=TCNT0;
seinen Overflow erreicht.
Dann hat zwar TCNT0 schon seinen Wert von 0, aber der Overflow ist in 
int0Flag noch nicht gezählt worden

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Karl Heinz schrieb:
> Ergo: Nach dem TCNT0 = 0 muss auf jeden Fall das TOV0 zurück gesetzt
> werden.

 Oder besser: Nicht am TCNT0 rumfummeln, sondern einfach ubernehmen.

von Karl H. (kbuchegg)


Lesenswert?

Thomas Eckmann schrieb:

>  if(TOV0) Overflow++; //Das reicht aber nicht
>  //Sondern:
>  //An dieser Stelle MUSS TCNT0 neu eingelesen werden

Ah. richtig.
Danke für den Hinweis.

von Karl H. (kbuchegg)


Lesenswert?

Thomas Eckmann schrieb:
> Karl Heinz schrieb:
>> Wobei: ich würde mich da nicht auf einen Zählerstand von 0 kaprizieren.
>> Bei kleinen Vorteilern kann der Timer dann auch schon weiter sein.
> Wozu dient die Abfrage überhaupt?

Da hab ich wohl die Frage falsch verstanden.

Die Abfrage auf den Zählerstand hätte den Zweck festzustellen, ob der 
Overflow vor oder nach
1
ISR( ... )
2
{
3
....
4
5
   var = TCNT0;
6
7
....
passiert ist.

Ist TOV0 gesetzt und hat var einen kleinen Wert, dann war der Overflow 
vor dem Auslesen des TCNT0. Hat var aber einen grossen wert (255), dann 
ist der Overflow erst passiert, nachdem das TCNT0 ausgelesen wurde. Das 
kann also nur mehr hier
1
ISR (INT0_vect)
2
{
3
  actInt0Flag=int0Flag;   //save actually counter value of t0Flag
4
  actTCNT0=TCNT0;   
5
6
---------------------------------------------- hier
7
8
  if( actTCNT0 < 10 && Flagregister & TOV0  )
9
    ....

passiert sein. Diesen Overflow will ich aber wieder nicht gezählt haben. 
Du umgehst das in deinem Code, in dem du den TCNT0 dann einfach noch mal 
ausliest. Es müsste aber, denke ich, auch so gehen.

von Thomas E. (thomase)


Lesenswert?

Karl Heinz schrieb:
> passiert sein. Diesen Overflow will ich aber wieder nicht gezählt haben.
> Du umgehst das in deinem Code, in dem du den TCNT0 dann einfach noch mal
> ausliest. Es müsste aber, denke ich, auch so gehen.

Mag in diesem Falle so sein.
Problem ist aber wenn der UART in seiner ISR gerade ein Byte in den 
Buffer schreibt, kommst du mit <10 nicht aus. Dann musst du die 
Konstante grösser machen. Oder es geht, geht nicht.

Immer korrekte Werte bekommst du aber, wenn du value neu einliest und 
korrigierst:

if(OVF) value = TCNT0 + 255 - value;

mfg.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Thomas Eckmann schrieb:
> Karl Heinz schrieb:
>> passiert sein. Diesen Overflow will ich aber wieder nicht gezählt haben.
>> Du umgehst das in deinem Code, in dem du den TCNT0 dann einfach noch mal
>> ausliest. Es müsste aber, denke ich, auch so gehen.
>
> Mag in diesem Falle so sein.
> Problem ist aber wenn der UART in seiner ISR gerade ein Byte in den
> Buffer schreibt, kommst du mit <10 nicht aus. Dann musst du die
> Konstante grösser machen. Oder es geht, geht nicht.

Das ist ein Argument.
Hab ich nicht bedacht.

von Peter D. (peda)


Lesenswert?

Karl Heinz schrieb:
> Wenn ich mich recht erinnere macht Grossmeister PeDa das so, dass er
> sich da zusätzlich auch noch das Overflow-Flag ansiehst und dann den 1
> fehlenden Overflow auch noch mitzählt, wenn der TCNT klein ist.

Beitrag "AVR Timer mit 32 Bit"

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Karl Heinz schrieb:
> Das ist ein Argument.
> Hab ich nicht bedacht.


Mit rumfummeln:
1
ISR (INT0_vect)
2
{
3
 // Alle werte übernommen, TCNT0 war auf 255, will jetzt TCNT0 auf Null setzen...
4
  <--------------------------------- hier ist OVF, TCNT0=0, TOV0 wird gesetzt
5
  TCNT0=0;     // Ist schon auf Null, also nutzlos, aber in der darauffolgenden
6
               // TOV0 ISR wird ein Overflow zuviel gezahlt

Ohne:
1
ISR (INT0_vect)
2
{
3
 ActCnt = TCNT0;
4
 // Alle werte übernommen, TCNT0 war auf 255, will nicht TCNT0 auf Null setzen...
5
  <--------------------------------- hier ist OVF, TCNT0=0, TOV0 wird gesetzt

 In der darauffolgenden TOV0 ISR wird (TCNT0 + 256 - ActCnt) gezählt,
 ergibt die genaue Anzahl der Timerticks.
 Also:
 Nicht am TCNT0 rumfummeln, keine Overflows zählen, sondern
 Zählerstand addieren.

von Matze (Gast)


Lesenswert?

Hey zusammen,
und vielen Dank für die Diskusion. Ich denke es ist einiges brauchbares 
dabei, und ich werde mich heute Abend gleich mal dran machen...dann meld 
ich mich natürlich wieder..

grüße Matthias

von Teimer Null (Gast)


Lesenswert?

Matze schrieb:
> und ich werde mich heute Abend gleich mal dran machen...dann meld
> ich mich natürlich wieder..
>
> grüße Matthias

Ich zähl' auf Sie!

gez. Teimer Null

von m.n. (Gast)


Lesenswert?

Ich hätte ja nicht gedacht, dass man um ein so kleines Problem so ein 
riesen Tamtam machen kann. Die Geschichte mit dem Timerüberlauf war doch 
schon geklärt.

von Matze (Gast)


Angehängte Dateien:

Lesenswert?

So...einen schönen Abend wünsch ich erstmal :-)

Hab mich jetzt nochmals dran gesetzt und Ansätze von euch umgesetzt. Nun 
funktioniert meine Messung soweit wie sie soll. Nur vereinzelt, in 
Interruptnähe des INT0, werden ab und zu +- 5...10 Hz zu viel oder zu 
wenig angezeigt. Habe jetzt aber keine Zeit mehr mir das Ganze noch 
genauer an zuschauen...das werde ich aber nächste Woche machen.

Ein wichtiger Punkt, war die Abfrage im INT0 Interrupt, auf das 
Vorhanden sein eines TIMER0 Overflow, und die Behandlung dessen in der 
IF Anweisung. Ein weiterer Punkt der das Messergebnis nochmals 
verbesserte, war das "freilaufen lassen" des TIMER0 (ohne 0 setzen --> 
dran rumfummeln :-) ).
Wie gesagt, jetzt passt alles so weit. Danke euch dafür.

Eins ist mir nur noch nicht ganz klar, auch wenn ich es selber im Code 
geschrieben habe (manchmal hilft auch ausprobieren ;-) ). In der 
folgenden Codezeile, wird doch, sobald der alte TCNT0 Wert größer als 
der neue TCNT0 Wert ist, nichts mehr zum actDivisor hinzu gefügt. Warum 
funktioniert den das?


actDivisor=tempInt0Flag*0x100 + (tempTCNT0-oldTempTCNT0);



grüße Matthias

von m.n. (Gast)


Lesenswert?

Matze schrieb:
> Eins ist mir nur noch nicht ganz klar, auch wenn ich es selber im Code
> geschrieben habe (manchmal hilft auch ausprobieren ;-) ).

Mit Probiererei liegst Du an dieser Stelle völlig daneben.
Dass Du in der INT0-Routine das TOV0-flag löscht, ist auch voll daneben. 
Solange Du den Wert von Timer0 und dessen Überläufe nicht sauber 
verarbeitest, kommst Du nicht auf einen grünen Zweig.

von Matze (Gast)


Lesenswert?

Hallo,
und warum ist das Löschen des Timer Overflow Flags quatsch?

Grüße Matthias

von m.n. (Gast)


Lesenswert?

Matze schrieb:
> und warum ist das Löschen des Timer Overflow Flags quatsch?

Das siehst Du letzlich am Ergebnis; es stimmt nur manchmal.
Ganz oben hatte ich Dir ein Beispielprogramm gezeigt, mit dem die 
Überlaufe des Timers korrekt erkannt und ausgewertet werden.

Umgeschrieben auf T0 ist dies die entscheidene Stelle:

// Interrupt der Eingangsimpulse
ISR (INT0_vect)
{
uint8_t T0_temp;
uint32_t temp_ueberlauf;
    T0_temp = TCNT0;
    eingangs_impulse++;
    temp_ueberlauf = ueberlauf;
    if(TIFR & BIT(TOV0) && (T0_temp < 0x80))
      temp_ueberlauf++;
    zeitpunkt = temp_ueberlauf * 0x100 + T0_temp;
}

Hier wird der 'zeitpunkt' zu einem Eingangsimpuls genommen, der 
berücksichtigt, ob zwischenzeitlich ein Überlauf aufgetreten ist,
und (!) dieser auch plausibel zu T0_temp paßt.
Keinesfalls darf TOV0 gelöscht werden, da 'ueberlauf' in der anderen ISR 
weitergezählt werden muß. Siehe hier:

ISR(TIMER0_OVF_vect)
{
  ueberlauf++;
}

von Matze (Gast)


Lesenswert?

Hallo,

m.n. schrieb:
> // Interrupt der Eingangsimpulse
> ISR (INT0_vect)
> {
> uint8_t T0_temp;
> uint32_t temp_ueberlauf;
>     T0_temp = TCNT0;
>     eingangs_impulse++;
>     temp_ueberlauf = ueberlauf;
>     if(TIFR & BIT(TOV0) && (T0_temp < 0x80))
>       temp_ueberlauf++;
>     zeitpunkt = temp_ueberlauf * 0x100 + T0_temp;
> }
Habe deine Code (auch den Link) mal noch genauer angeschaut, und frage 
mich was mit der Variable "ueberlauf++" passiert? Ich kann niergends 
sehen das diese auch mal wieder gelschöt wird, bzw. was passiert wenn 
diese überläuft?

Grüße Matthias

von m.n. (Gast)


Lesenswert?

Matze schrieb:
> Ich kann niergends
> sehen das diese auch mal wieder gelschöt wird, bzw. was passiert wenn
> diese überläuft?

Die Variable'ueberlauf' kann ruhig überlaufen, da die Zeitdifferenz 
immer richtig berechnet wird. Ausnahme: die Messdauer liegt über 536 s 
(@8 MHz). Vorher hat aber schon TIMEOUT dafür gesorgt, dass die laufende 
Messung abgebrochen wird und die nächste Messung ungültig ist. Danach 
kann wieder normal gemessen werden.

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.