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
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.
Warum nimmst du nicht Timer1? Mit ICP kann der das fast ganz alleine. mfg.
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
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.
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
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.
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.
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.
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.
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 :-)
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.
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.
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
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
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
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.
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.
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.
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
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.
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"
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.
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
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
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.
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
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.
Hallo, und warum ist das Löschen des Timer Overflow Flags quatsch? Grüße Matthias
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++; }
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.