Forum: Compiler & IDEs Timersystem geht nicht


von ChristianS (Gast)


Lesenswert?

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:
1
typedef struct{
2
    uint16_t    uiTimerRxData;
3
    uint16_t    uiTimerModulAlive;
4
}sTimer_t;
5
6
typedef struct{
7
    uint8_t bIncLiveTime:1;                                                 // Sekundenzähler erhöhen
8
}sSchedFlags_t;

Timer-Interrupt:
1
ISR(TIMER0_COMP_vect)   // Timer0 (System Tick 4ms @ 8MHz)
2
{
3
    // Timerinterrupthandler
4
    if(sTimer.uiTimerRxData != 0)
5
    {
6
        sTimer.uiTimerRxData--;
7
        if(sTimer.uiTimerRxData == 0)
8
        {
9
            StateMachineReset();
10
        }
11
    }
12
13
    if(sTimer.uiTimerModulAlive != 0)
14
    {
15
        sTimer.uiTimerModulAlive--;
16
        if(sTimer.uiTimerModulAlive == 0)
17
        {
18
            sSchedFlags.bIncLiveTime = 1;
19
        }
20
    }
21
}

Aufziehen des Timers geschieht so:
1
void PmSwvSystemTimer(uint8_t ucTimername, uint8_t ucZustand)
2
{
3
    cli();
4
5
    switch(ucTimername)
6
    {
7
        case eTimerRxData:              sTimer.uiTimerRxData = (ucZustand) ? PM_SWV_SCHEDULER_SERIAL_TIMEOUT:0;         break;
8
        case eTimerModulAlive:          sTimer.uiTimerModulAlive = (ucZustand) ? PM_SWV_SCHEDULER_ONESECOND:0;          break;
9
        default: break;
10
    }
11
    sei();
12
}

Das Hauptprogramm fragt die Bools so ab:
1
    if(sSchedFlags.bIncLiveTime)
2
    {
3
        IncLivetime();
4
        cli();
5
        sSchedFlags.bIncLiveTime = 0;
6
        sei();
7
        PmSwvSystemTimer(eTimerModulAlive, eTOn);
8
    }

von Karl H. (kbuchegg)


Lesenswert?

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_t data[5];
2
3
  data[5] = 8;

und damit ungewollt andere Variablen (oder noch schlimmer: der 
Returnstack) verändert werden.

von ChristianS (Gast)


Lesenswert?

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
1
PmSwvSystemTimer(eTimerModulAlive, eTOn); // setzt sTimer.uiTimerModulAlive = 250 (1sek)
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.
1
    if(sSchedFlags.bIncLiveTime)
2
    {
3
        IncLivetime();
4
        cli();
5
        sSchedFlags.bIncLiveTime = 0;
6
        sei();
7
        PmSwvSystemTimer(eTimerModulAlive, eTOn); // setzt sTimer.uiTimerModulAlive = 250 (1sek)
8
    }

5. Nun sollte der gesamte Vorgang neu starten. Im Fehlerfall wird der 
Softwaretimer jedoch nicht mehr aufgezogen über
1
PmSwvSystemTimer(eTimerModulAlive, eTOn);
, so daß die Gesamtfunktion nicht mehr gegeben ist.

von Ahem (Gast)


Lesenswert?

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.

von ChristianS (Gast)


Lesenswert?

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).

von Ahem (Gast)


Lesenswert?

>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

von ChristianS (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Ahem (Gast)


Lesenswert?

> 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.

von Ahem (Gast)


Lesenswert?

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.

von Ahem (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

von ChristianS (Gast)


Lesenswert?

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

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
Noch kein Account? Hier anmelden.