Hallo,
heute mal wieder ein Problem. Ich habe einen Timer, der genau alle 1ms
einen Interupt auslöst. Dort wird eine Zahl, die vorher einen Wert
bekommen hat heruntergezählt.
Hier die Codeausschnitte dazu :
1
//Timer1 Interupt Compare
2
3
inttimerCounter=0;
4
5
ISR(SIG_OUTPUT_COMPARE1A)
6
{
7
if(timerCounter>0)
8
timerCounter--;
9
10
}
11
12
....
13
14
voidwaitMs(intms)
15
{
16
timerCounter=ms;
17
printf("WarteZeit = %i\r\n",timerCounter);
18
while(timerCounter>0){}
19
printf("Ende = %i\r\n",timerCounter);
20
}
Folgendes Problem. Wenn ich die Funktion waitMs() aufrufe, kommt es alle
3-4 mal dazu, das die letzte printf-Ausgabe in der Funktion(also "Ende="
) den Wert 255 ausgibt, die Variable timerCounter also nicht bis 0
heruntergezählt ist, was ja aber durch den Vergleich (timerCounter > 0)
eigentlich nicht sein kann.
Vielleicht einer eine Idee ...
Gruß
Micha
Erstens: volatile int timerCounter = 0;
Zweitens: Wenn das ein AVR ist, dann kann das auch dann nicht sauber
funktionieren. Weil in waitMs die Variable als 2 getrennte Bytes
verarbeitet wird, und wenn dazwischen der Interrupt reinläuft, ist hat
das eine schon geladene Byte den Zustand von vorher, das andere danach
geladene den Zustand nachher.
Aus
http://www.mikrocontroller.net/articles/Interrupt#Atomarer_Datenzugriff:
"Ein ähnliches Problem entsteht bei Variablen, deren Grösse die
Wortbreite der Maschine übersteigt. Bei 8-Bit-Prozessoren wie AVR oder
i51 also bereits bei normalen "int" Variablen. Diese Variablen werden
zwangsläufig byteweise verarbeitet. Wenn genau dazwischen ein Interrupt
erfolgt, wird ein falscher Wert gelesen. Wenn beispielsweise eine
Interrupt-Routine einen 16-Bit-Zähler verwendet und von 0x00FF auf
0x0100 hochzählt, dann kann das Hauptprogramm auch schon mal
versehentlich die Werte 0x01FF oder 0x0000 lesen."
Hallo,
Danke erstmal für die schnellen Antworten !!
das mit dem volatile hatte ich schon drin, nur gestern Abend beim
herumprobieren irgendwie wieder draußen gelassen.
Ich kann mir schon vorstellen das es genau das ist, was ihr mir hier
beschreibt, nur wie mache ich es dann am besten.. na mal sehen ob mir
auch was einfällt.
Gruß
Micha
Moin,
du musst den Vergleich timerCounter > 0 einkapseln mit cli(); und
sei();, damit der Interrupt nicht dazwischenfunken kann (siehe
Beschreibung von Andreas)
Gruß
mo
Aber damit sollte doch dann mein Zeitzähler ziemlich ungenau werden oder
sehe ich das falsch. Brauche schon ziemlich genaue Zeit, da das ganze
von einem Kameraauslöser ist ...
Gruß
Micha
Michael Kentschke wrote:
> Aber damit sollte doch dann mein Zeitzähler ziemlich ungenau werden
definiere 'ungenau'.
Spielt es wirklich eine Rolle, wenn der Vergleich ein paar
Nanosekunden zu spät kommt, weil der Interrupt warten muss
bis der int einmal auf 0 geprüft wurde?
> oder> sehe ich das falsch. Brauche schon ziemlich genaue Zeit, da das ganze> von einem Kameraauslöser ist ...
Ich denke nicht, dass da ein paar Nanosekunden eine Rolle spielen.
>du musst den Vergleich timerCounter > 0 einkapseln mit cli(); und>sei();
was aber bei einem while-Konstrukt nicht geht.
Am kleinsten wird die "Ungenauigkeit", wenn du die volatile-Variable in
eine lokale Variable umkopierst, und nur das mit cli()/sei() kapselst.
Aus
1
cli();
2
inta=timerCounter;
3
sei();
wird
1
cli
2
lds r24, 0x0060
3
lds r25, 0x0061
4
sei
mit maximal 5 Taktzyklen Verzögerung. Macht selbst bei langsamen 1MHz
nur 5 Mikrosekunden Ungenauigkeit.
Oliver
Ok, vielleicht verstehe ich das falsch, aber ich versuches es mal zu
Re-Konstruieren.
Ich könnte ja jetzt folgendes Machen:
1
inta=timerCounter;
2
while(a>0)
3
{
4
cli();
5
a=timerCounter;
6
sei();
7
}
Dann habe ich doch aber jedesmal eine Verzögerung. z.B. 1000ms warten,
da wird der TimerInterrupt doch ziemlich oft ab und angeschaltet werden,
oder verstehe ich das falsch.
Gruß
Micha
Da steckt der gleiche Fehler ja immer noch drin (in a = ...). Besser:
1
inta;
2
do{
3
cli();
4
a=timerCounter;
5
sei();
6
}while(a>0)
> da wird der TimerInterrupt doch ziemlich oft ab und angeschaltet werden,> oder verstehe ich das falsch.
Nö, das verstehts du richtig. Und was stört dich daran? Der geht dadurch
nicht verloren und wird nur unmerklich verzögert (wenige CPU-Takte).
Andreas Kaiser wrote:
> Nö, das verstehts du richtig. Und was stört dich daran? Der geht dadurch> nicht verloren.
Ich denke Michael hat Angst, dass ihm hier irgendwie Zeit verloren
geht und sich das aufsummiert. Dem ist aber nicht so. Der Interrupt
wird trotzdem immer zeitgerecht ausgelöst, weil dieses Auslösen ja von
der Hardware gemacht wird. Lediglich die Abarbeitung der Interrupt
Funktion kann sich unter Umständen ein klein wenig verzögern.
Das ist so, wie wenn du eine Uhr hast, bei der der Sekundenzeiger
jedesmal wenn er bei 0 ist einen Summton auslöst. Wenn du zufällig
mal bei einem Summton keine Zeit hast und 5 Sekunden später auf
die Uhr schaust, dann siehst du das eine mal etwas zu spät auf die
Uhr. Aber der nächste Summton kommt wieder pünktlich zur nächsten
vollen Minute.
Danke @Karl heinz Buchegger, du hattest schon recht mit der Vermutung
das ich dachte, es summiert sich auf.
Aber ich denke nun habe ich es verstanden und baue dann mal mein
Programm um.
Danke @all