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
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.
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
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.
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.
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
---------------------------------oderhier
5
actInt0Flag=int0Flag;//save actually counter value of t0Flag
6
---------------------------------oderhier
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
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.
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"
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.
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.