Liebe Kollegen,
habe einen Programmier-Fehler entdeckt, mit dem ich im Moment nicht
weiter komme. Im Timer-Interrupt inkrementiere ich (jede 100 ms) eine
Variable, die dann im Hauptprogramm abgefragt wird (counter >= 600), um
einmal pro Minute etwas durchführen zu können.
Wenn ich mir diesen Counter ausgeben lasse, enthält er fast immer genau
den Wert 600 aber ab und zu auch noch den Wert 512 und nur diesen.
Warum? Mit meinem Realtime-Clock (DS1339) habe ich herausgefunden, dass
diese If-Abfrage zu FRÜH kommt, also nach 51,2 Sekunden nach dem letzten
Durchlauf.
Diese Werte von 512 kommen unregelmässig, einmal sogar zwei
hintereinander, einmal ist der Abstand 7 Minuten, einmal 19 Minuten. Ich
sehe darin keine Regelmässigkeit.
Zu meinem Setup: ATMEGA1284P, IDE: Eclipse mit WinAVR, AVR-Studio4 mit
JTAGICEII zum Flashen.
Hier ein Auszug aus meinem Programm:
1
volatileuint16_tone_hundred_ms_ticks_counter=0;
2
//gets incremented in timer1 compare match interrupt
3
4
ISR(INT0_vect)
5
{"ADC auslesen"}
6
7
// Timer 1 interrupt
8
ISR(TIMER1_COMPA_vect)
9
{
10
one_hundred_ms_ticks_counter++;
11
}
12
13
intmain(){
14
cli();//global disable interrupts
15
....
16
//start timer
17
TCCR1B|=(1<<CS11)|(1<<CS10);// Pre-scaler for Timer1 set to 64 (p.136 data sheet)
Und hier die Ausgabe am Terminal:
2012-04-10 11:50:47
one_hundred_ms_ticks_counter = 600.
2012-04-10 11:51:47
one_hundred_ms_ticks_counter = 600.
2012-04-10 11:52:47
one_hundred_ms_ticks_counter = 600.
2012-04-10 11:53:38
one_hundred_ms_ticks_counter = 512.
2012-04-10 11:54:38
one_hundred_ms_ticks_counter = 600.
Warum funktioniert die IF-Abfrage so komisch?
Vielen Dank für eure Tipps und Hilfe!
Karel
MaWin schrieb:> Du darfst dir in Abfragen nicht per Interrupt reinpfuschen lassen.>> cli();> if(one_hundred_ms_ticks_counter >= 600) //1 minute> {> USART_puts(getTimeStamp());> USART_puts(itoa(one_hundred_ms_ticks_counter, buff,> 10));USART_puts("\r\n"); //GIBT MANCHMAL 512 :-(> one_hundred_ms_ticks_counter = 0;> }> sei();
Wobei das zu weit gefasst ist da die USART... Funktionen und der
getTimestamp ziemlich lange dauern könnten. Ausserdem könnte es sein daß
darin auch cli() oder sei() aufgerufen wird.
besser ist hier vor dem if
cli();
int tempCounter = one_hundred_ms_ticks_counter;
sei()
und dann mit dem tempCounter weiterarbeiten
am Anfang der Schleife (in der Schleife dann):
cli();
one_hundred_ms_ticks_counter = 0;
sei();
Und wenn dein counter genauer laufen soll und gleichzeitig die
IRQ-Sperre nicht die ganzen UART-ausgaben mit abdecken soll, ist es
vielleicht auch sinnvoll, es umzustellen:
>Du darfst dir in Abfragen nicht per Interrupt reinpfuschen lassen.
Anstelle von cli/sei würde ich jedoch die Variable
one_hundred_ms_ticks_counter am Beginn der while(1) Loop kopieren und
dann diese Kopie abfragen.
Erich schrieb:> Anstelle von cli/sei würde ich jedoch die Variable> one_hundred_ms_ticks_counter am Beginn der while(1) Loop kopieren und> dann diese Kopie abfragen.
und dann? dann wird halt unsinn kopiert.
>Anstelle von cli/sei würde ich jedoch die Variable>one_hundred_ms_ticks_counter am Beginn der while(1) Loop kopieren und>dann diese Kopie abfragen.
Da brauchst Du aber trotzdem ein cli/sei beim kopieren.
Hi
Ich kann mich zwar in C nicht ausdrücken, aber warum nicht in der ISR
einfach nur hochzählen, solange kleiner 600. Dann in einem "Else" -Zweig
die Variable auf 0 und einen Merker setzen, der dann im ganz normalen
Programm abgefragt wird. Ist er gesetzt, erfolgt die beabsichtigte
Aktion und der Merker wird wieder gelöscht. Es ist doch sicherlich
egal, wenn jede Minute eine Aktion ausgeführt werden soll, ob diese in
einem Zeitfenster von +- 2.3 mSekunden stattfindet. Auf diese Weise wird
die ISR überhaupt nicht belastet. Und nach einer Stunde sind auch 60
Aktionen durchgeführt, nach einer Woche 10080 ...
Gruß oldmax
Hi
Vergesst mein vorheriges Schreiben... sehe grad, das es im Prinzip so
gemacht, wie vorgeschlagen, also die Bearbeitung außerhalb der ISR.
Grußß oldmax
16-Bit Werte zwischen interrupt und Hauptprogramm zu teilen ist, wie
hier schon hinreichend beschreiben, problematisch.
Anstatt jedoch interrupts global zu deaktivieren und so zu riskieren
welche zu verpassen, habe ich mir angewöhnt Zähler nur innerhalb der
Interrupts zu halten und (atomar änderbare) 8-Bit Variablen für die
Ereignisse zu verwenden:
USART_puts(getTimeStamp());// UART ausgabe kann lange dauern...
6
...
7
}
8
sei();
Und wenn schon eine Abfrage auf größer-gleich drin ist, dann würde ich
noch das hier vorschlagen:
1
if(one_hundred_ms_ticks_counter>=600){// 1 Minute
2
one_hundred_ms_ticks_counter-=600;// um 1 Minute zurückstellen
Denn was wäre, wenn der Zähler schon auf 601 stehen würde?
Aber mir ist auch klar: keiner schreibt Routinen, die länger als 100ms
dauern. Sicher... ;-)
Die Frage ist, wie sieht das Ganze im Assembler aus?
Folgendes Szenario:
16Bit Timer steht auf 0x01FF. Mit dem Clock-Cycle, in dem vom 8Bit
Register gelesen wird, springt der Zähler um auf 200.
Dann liest du in der IF-Abfrage zunächst das Low-Byte (0xFF) danach das
High-Byte (0x02), => Integer-Wert 0x02FF = 767.
767>600, also If-Abfrage True, nächste Abfrage vom Timer (in der
If-Abfrage) liefert danach 0x0200 = 512.
Hallo Kollegen,
ihr seid alle goldig!!!
Vielen Dank für so viele konstruktiven Beiträge. Seit 8 Stunden zählt
der Prozessor schon richitig jede 1 ganze Minute :-)
Gute Nacht
Karel
PS: Diesen Thread kann man schliessen.