www.mikrocontroller.net

Forum: Compiler & IDEs Timersystem geht nicht


Autor: ChristianS (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
typedef struct{
    uint16_t    uiTimerRxData;
    uint16_t    uiTimerModulAlive;
}sTimer_t;

typedef struct{
    uint8_t bIncLiveTime:1;                                                 // Sekundenzähler erhöhen
}sSchedFlags_t;

Timer-Interrupt:
ISR(TIMER0_COMP_vect)   // Timer0 (System Tick 4ms @ 8MHz)
{
    // Timerinterrupthandler
    if(sTimer.uiTimerRxData != 0)
    {
        sTimer.uiTimerRxData--;
        if(sTimer.uiTimerRxData == 0)
        {
            StateMachineReset();
        }
    }

    if(sTimer.uiTimerModulAlive != 0)
    {
        sTimer.uiTimerModulAlive--;
        if(sTimer.uiTimerModulAlive == 0)
        {
            sSchedFlags.bIncLiveTime = 1;
        }
    }
}

Aufziehen des Timers geschieht so:
void PmSwvSystemTimer(uint8_t ucTimername, uint8_t ucZustand)
{
    cli();

    switch(ucTimername)
    {
        case eTimerRxData:              sTimer.uiTimerRxData = (ucZustand) ? PM_SWV_SCHEDULER_SERIAL_TIMEOUT:0;         break;
        case eTimerModulAlive:          sTimer.uiTimerModulAlive = (ucZustand) ? PM_SWV_SCHEDULER_ONESECOND:0;          break;
        default: break;
    }
    sei();
}

Das Hauptprogramm fragt die Bools so ab:
    if(sSchedFlags.bIncLiveTime)
    {
        IncLivetime();
        cli();
        sSchedFlags.bIncLiveTime = 0;
        sei();
        PmSwvSystemTimer(eTimerModulAlive, eTOn);
    }

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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
  uin8_t data[5];

  data[5] = 8;

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

Autor: ChristianS (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
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
    if(sTimer.uiTimerModulAlive != 0)
    {
        sTimer.uiTimerModulAlive--;
        if(sTimer.uiTimerModulAlive == 0)
        {
            sSchedFlags.bIncLiveTime = 1;
        }
    }
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.
    if(sSchedFlags.bIncLiveTime)
    {
        IncLivetime();
        cli();
        sSchedFlags.bIncLiveTime = 0;
        sei();
        PmSwvSystemTimer(eTimerModulAlive, eTOn); // setzt sTimer.uiTimerModulAlive = 250 (1sek)
    }

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

Autor: Ahem (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
 if(sTimer.uiTimerModulAlive != 0)
    {
        sTimer.uiTimerModulAlive--;
        if(sTimer.uiTimerModulAlive == 0)
        {
            sSchedFlags.bIncLiveTime = 1;
        }
    }
behandelt werden? Ist das so?
Das könnte unter Umständen zu lange dauern, wenn noch andere Interrupts 
beteiligt sind.

Autor: ChristianS (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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).

Autor: Ahem (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: ChristianS (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Ahem (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Ahem (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Ahem (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: ChristianS (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.