Hallo zusammen,
ein Teil meines Codes läuft nicht richtig. Leider kann ich mir nicht
erklären, warum das so ist.
Folgendes möchte ich machen: In einem Timerinterrupt wird eine Variable
dekrementiert, wenn ungleich null. Wenn diese Variable null ist, dann
wird ein Flag gesetzt, das in der Hauptroutine abgefragt wird.
Irgendwann wird in dem unten anstehenden Beispiel der Timer
sTimer.uiTimerModulAlive einfach nicht mehr laufen, da er nicht mehr neu
aufgezogen wird. Die Variable sSchedFlags.bIncLiveTime wird nicht mehr 1
(im ICE Debugger nachvollzogen). Dieser Fehler tritt auch schon mal erst
nach 30 oder 40 Stunden Betriebszeit auf.
Warum ist das so?
PS: Im eigentlichen programm tummeln sich noch ein paar mehr von diesen
Timern herum, im Moment genau 13 Stück.
Vielen Dank im Voraus für eure Hilfe!
Variablen:
Aus dem bischen Code kann man nicht wirklich was erkennen.
Wenn aber ein Programm seltsame, unerklärliche Dinge tut, dann ist ein
häufiger Grund darin zu suchen, dass irgendwelche Arrays out of Bounds
beschrieben werden
1
uin8_tdata[5];
2
3
data[5]=8;
und damit ungewollt andere Variablen (oder noch schlimmer: der
Returnstack) verändert werden.
Hallo,
eine Feldgrenzenverletzung kann ich nicht ausschließen, halte ich hier
aber für wenig wahrscheinlich, da nicht über Arrays oder Pointer auf
Daten zugegriffen wird. Zudem kommt der Fehler zu selten vor, so daß ich
eher auf Interrupts tippe, die den unten beschriebenen Vorgang irgend
wie stören. (Und wie ich Feldgrenzenverletzungen bei einem uC überprüfe,
das frage ich mal in einem anderen Thread :)
Ich versuche noch einmal, den gesamten Programmablauf genauer zu
beschreiben:
1. Hardware Timer initialisieren, Ablaufzeit 4ms
2. Softwaretimer 'Sekundentakt' starten
3. In der Interruptroutine (ISR(TIMER0_COMP_vect)) wird der
Softwaretimer heruntergezählt. Wenn Null, dann wird das Flag
'sSchedFlags.bIncLiveTime' gesetzt
1
if(sTimer.uiTimerModulAlive!=0)
2
{
3
sTimer.uiTimerModulAlive--;
4
if(sTimer.uiTimerModulAlive==0)
5
{
6
sSchedFlags.bIncLiveTime=1;
7
}
8
}
4. Das gesetzte Flag 'sSchedFlags.bIncLiveTime' wird in der main
überprüft, eine Aktion wird ausgeführt, das Flag wird zurückgesetzt und
der Timer wird neu gestartet.
Wenn das folgende stimmt
>PS: Im eigentlichen programm tummeln sich noch ein paar mehr von diesen>Timern herum, im Moment genau 13 Stück.
dann müssen wir davon ausgehen, das in Deinem TimerInterrupt 13 Timer à
la
1
if(sTimer.uiTimerModulAlive!=0)
2
{
3
sTimer.uiTimerModulAlive--;
4
if(sTimer.uiTimerModulAlive==0)
5
{
6
sSchedFlags.bIncLiveTime=1;
7
}
8
}
behandelt werden? Ist das so?
Das könnte unter Umständen zu lange dauern, wenn noch andere Interrupts
beteiligt sind.
Ja, das ist tatsächlich so.
Aber was wird passieren? Hardwaretimer0 wird ablaufen. Im Moment wird
gerade ein anderer IRQ bedient. Nachdem das beendet ist, wird der
HW-Timer0 wieder angesprungen und arbeitet brav alles ab. Nach meinem
Verständnis kommt das andauernd vor und sorgt für einen Jitter in der
Ausführung, der aber akzeptabel ist...
Ich werde aber dennoch einmal überprüfen, ob der Stack ins RAM wächst
(120Byte Stack habe ich, das sollte genug sein).
>Ja, das ist tatsächlich so.>Aber was wird passieren? Hardwaretimer0 wird ablaufen. Im Moment wird>gerade ein anderer IRQ bedient. Nachdem das beendet ist, wird der>HW-Timer0 wieder angesprungen und arbeitet brav alles ab. Nach meinem>Verständnis kommt das andauernd vor und sorgt für einen Jitter in der>Ausführung, der aber akzeptabel ist...
Oh, da kann noch so allerhand Anderes passieren.
Z.B könnte noch während die Interrupt-Routine läuft ein weiterer und
noch ein weiterer Timerinterrupt auftreten, so das welche verlorengehen.
Oder höherpriore Interrupts verdecken mehrfaches auftreten des
Timerinterrupts.
Aber ich muss einräumen, das ich nur so global ein blödes Gefühl dabei
habe.
Um genauer zu werden ist einfach zu wenig Code vorhanden.
Im allgemeinen aber würde ich bei sovielen Timerinterrupts mit
virtuellen Timern arbeiten, so das faktisch immer nur eine Zählvariable
verändert wird.
Ausserdem würde ich im Interrupt an sich, Funktionsaufrufe die sich
schonmal lang anhören, z.B. StateMachineReset(); garnicht einsetzen.
An sich wäre es empfehlenswert, das Programm soweit zu reduzieren, das
der Fehler noch auftritt aber alles nicht notwendige rausgelassen wird.
Aber das wird hier schlecht möglich sein.
Eine Möglichkeit die ich Dir sehr empfehlen würde, wäre ein Lint-Lauf.
Der findet auch einige Varianten von falschen Speicherzugriffen usw.
Guck mal hier: http://www.thefreecountry.com/programming/debuggers.shtml
Hmmm, hab schon gehofft, daß der Lint-Kelch an mir vorübergeht. Ich
stimme Dir aber zu, daß das durchaus sinnvoll ist.
Um vielleicht noch einmal für Klarheit zu sorgen: Die
Sourcecodeschnipsel stellen im Prinzip alle am Desaster beteiligten
Komponenten dar. Was sich hier nicht darstellen lässt, ist die
Gesamtfunktionalität. Natürlich läuft dort noch mehr ab, aber außer
Prozessorlast passiert dort nichts außergewöhnliches.
Dennoch - Dein Lösungsansatz deckt sich mit meiner Vermutung: Mein
Timersystem ist nicht beherrschbar.
ChristianS schrieb:
> Hmmm, hab schon gehofft, daß der Lint-Kelch an mir vorübergeht. Ich> stimme Dir aber zu, daß das durchaus sinnvoll ist.> Um vielleicht noch einmal für Klarheit zu sorgen: Die> Sourcecodeschnipsel stellen im Prinzip alle am Desaster beteiligten> Komponenten dar.
Du machst hier einen Denkfehler.
Du siehst die Symptome.
Deine Ursache kann ganz woanders stecken. Sie kann im Timersystem sein,
sie muss es aber nicht.
> Um vielleicht noch einmal für Klarheit zu sorgen: Die> Sourcecodeschnipsel stellen im Prinzip alle am Desaster beteiligten> Komponenten dar.
Entschuldige bitte, aber sowas haben vor Dir schon viele gedacht, das
denken im Moment viele und werden noch eine Menge Leute denken. Ich
nehme mich selbst da nicht aus. Leider sagt die Menge der Leute
überhaupt nichts darüber aus, ob es stimmt. (Siehe das Fliegenbeispiel).
Im Moment weisst Du meiner Meinung nach nicht, ob der Fehler in den
Codeschnipseln liegt, soweit das eine Frage der Meinung ist.
Nichts für ungut. :-)
Deswegen die Lint-Empfehlung.
Abgesehen davon ist das mit dem Lint nun wirklich nicht schlimm. Es gibt
zwar immer einige überflüssige Warnungen, aber vieles ist wirklich
relevant.
>Dennoch - Dein Lösungsansatz deckt sich mit meiner Vermutung: Mein>Timersystem ist nicht beherrschbar.
Das scheint mir, nach oder neben Lint, der sinnvollste Ansatzpunkt zu
sein.
Was bei etwas verzwickteren Abläufen auch sinnvoll sein kann, ist ein
kleines OS mit Events und Messages/Semaphoren zu verwenden. Da gerade
bei grösserer Anzahl von Resourcen und mehreren Nebenläufigkeiten übel
zu findende Fehler auftreten können, könnte das helfen sie zu finden,
bzw. garnicht erst auftreten zu lassen.
Es gibt da so diverse Freeware ua. auf avrfreaks. Oder auch ucOS zum
runterladen.
Ich bin mir nicht sicher, ob das mit den extra Flags sinnvoll ist,
Bitoperationen sind aufm AVR nämlich nicht der Brüller (Byte lesen,
AND/OR, Byte zurückschreiben).
Die Zählvariable auf 0 testen, sollte doch reichen.
Deine Zugriffe scheinen ordentlich mit CLI/SEI atomar zu sein. Im
Assemblerlisting müßte man das besser sehen können.
Ich habe für meinen Scheduler den Timertick auch in der Mainloop
gemacht. Der Timerinterrupt setzt nur ein Flag, daß ein Timertick um
ist. Damit sind sämtliche atomic Sachen vom Tisch.
Beitrag "Wartezeiten effektiv (Scheduler)"
Peter
Hallo Peter,
Deine Lösung ist die Umsetzung von dem, was ich im Kopf gehabt habe, als
ich angefangen habe den Timer zu implementieren. Obwohl, zugegeben, das
was Du gemacht hast ist schon die 'Eierlegende-Woll-Milch-Sau'.
Vom Prinzip her unterscheiden sich unsere Ansätze auf den 'kleinen'
Unterschied, daß Du im Timer-IRQ auf echter Atom-Ebene bist (byte). Auch
Deine Routine kann einen Jitter verkraften, wenn mal ein IRQ verpasst
wird, das sollte mein Scheduler auch bringen.
Ich werd das Teil noch mal umcoden, auch wenn's nicht gleich eine
verkettete Liste sein muß :-)
Vielen Dank für eure konstruktiven und fachlichen Beiträge!
Gruß,
Christian