Forum: PC Hard- und Software Lint für avr-gcc


von Moriz (untertaucher)


Lesenswert?

Ich habe eine ganz üble Gurke in meinem Programm:

Nachdem eeprom_update_block 6 von 8 Bytes ins EEPROM geschrieben hat, 
gibts einen Watchdog-Interrupt, obwohl der WD abgeschaltet ist. Auch ein 
wdt_reset vor dem eeprom_update_block hilft nix.

Wahrscheinlich zerkloppe ich irgendwo anders was, was dann zu dem 
Problem führt.

Da bietet sich eigentlich an, mal Lint über das Paket laufen zu lassen.

Gibt es irgendwo ein Beispiel, wie man das konfigurieren muss?

Gibt es bewährte Alternativen?

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?


von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Moriz schrieb:
> obwohl der WD abgeschaltet ist.

Du hast aber dran gedacht, dass man den Watchdog, wenn er einmal 
getriggert hatte, ganz lieb streicheln muss, bevor er wirklich wieder 
zahm ist, ja?

Also einfach nur abschalten hilft da nicht, dann knallt er nach dem 
minimalen Timeout wieder rein (15 ms).

von Oliver S. (oliverso)


Lesenswert?

Jörg W. schrieb:
> Du hast aber dran gedacht, dass man den Watchdog, wenn er einmal
> getriggert hatte, ganz lieb streicheln muss, bevor er wirklich wieder
> zahm ist, ja?

Oder auch nach jedem anderen Reset, z.B brown out...

Oliver

von Stefan F. (Gast)


Lesenswert?

Ich benutze gerne Qt Creator als Texteditor, wegen der umfangreichen 
Checks, die er macht:
http://stefanfrings.de/avr_tools/index.html#qtcreator

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Oliver S. schrieb:
> Oder auch nach jedem anderen Reset, z.B brown out...

Nein, eben nicht.

Das einmal gesetzte WDRF-Bit bleibt über alle Resets hinweg erhalten – 
mit Ausnahme natürlich des Power-On, da geht's logischerweise nicht 
anders.

Daher auch mein Einwurf – ich hatte mir vor langer Zeit danach selbst 
mal einen Wolf gesucht.

Das Verhalten ist übrigens zwischen "ganz alten" AVRs (also ATmega16 / 
ATmega128 und so) und aktuelleren (ATmega1281 etc.) mal geändert worden.

: Bearbeitet durch Moderator
von Moriz (untertaucher)


Lesenswert?

Jörg W. schrieb:
> Du hast aber dran gedacht, dass man den Watchdog, wenn er einmal
> getriggert hatte, ganz lieb streicheln muss, bevor er wirklich wieder
> zahm ist, ja?

Was noch außer wdt_disable() und MCUSR &= ~(1 << WDRF)?

Ich hatte auch versuchsweise den WD gar nicht aktiviert, aber trotzdem 
ein wdt_reset() als erste Anweisungen im Code gemacht – hat am 
Fehlerbild nicht das Geringste geändert.

> Das einmal gesetzte WDRF-Bit bleibt über alle Resets hinweg erhalten

Das wäre meine nächste Frage: bewirkt das WDRF irgend was, oder ist das 
nur eine Information? Aus dem Datenblatt des 328P würde ich erst mal 
letzteres schließen. Dass da womöglich doch noch was mehr dahinter ist, 
kann man nur aus dem Hinweis zum WD-Code erahnen, dass man WDRF 
zurücksetzen soll.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Moriz schrieb:
> Was noch außer wdt_disable() und MCUSR &= ~(1 << WDRF)?

Das sollte genügen, nur anders herum.

> Ich hatte auch versuchsweise den WD gar nicht aktiviert, aber trotzdem
> ein wdt_reset() als erste Anweisungen im Code gemacht – hat am
> Fehlerbild nicht das Geringste geändert.

Je nach Länge des Startups kann es sich günstig machen, das Rücksetzen 
des WDRF bereits irgendwo vor main() zu machen. Ist aber eigentlich nur 
relevant, wenn du sehr viele statische Initialisierungsdaten bei 
geringer CPU-Taktfrequenz hast (das Kopieren erfolgt ja mit der 
CPU-Taktfrequenz, aber der Wachhund beißt immer nach schon 15 ms, wenn 
er beißt).

>> Das einmal gesetzte WDRF-Bit bleibt über alle Resets hinweg erhalten
>
> Das wäre meine nächste Frage: bewirkt das WDRF irgend was, oder ist das
> nur eine Information?

Du kannst den Wachhund nicht ausschalten, solange das Bit gesetzt ist.

Betrifft natürlich nur den Fall, dass man ihn in der laufenden Sitzung 
mal irgendwann irgendwie (ggf. versehentlich) hat triggern lassen und 
dann vergisst, das zurückzusetzen. Nach einem Power-On ohne Wachhund 
sollte das Bit ja nie gesetzt sein – wenn du ihn nicht gerade per Fuse 
erzwingst.

von Oliver S. (oliverso)


Lesenswert?

Stefan F. schrieb:
> Ich benutze gerne Qt Creator als Texteditor, wegen der
> umfangreichen
> Checks, die er macht:

Die „umfangreichen Checks“ finden aber nichts, was dem TO weiterhelfen 
könnte, und auch nicht mehr, als der Compiler dann auch findet.

Oliver

von Moriz (untertaucher)


Lesenswert?

Jörg W. schrieb:
> Du kannst den Wachhund nicht ausschalten, solange das Bit gesetzt ist.
>
> Betrifft natürlich nur den Fall, dass man ihn in der laufenden Sitzung
> mal irgendwann irgendwie (ggf. versehentlich) hat triggern lassen und
> dann vergisst, das zurückzusetzen. Nach einem Power-On ohne Wachhund
> sollte das Bit ja nie gesetzt sein – wenn du ihn nicht gerade per Fuse
> erzwingst.

Interessant… Ich habe meinen Code entsprechend geändert – hat leider nix 
gebracht. Den Reset gibts noch immer mitten im eeprom Update und beim 
Neustart steht MCUSR auf 0. Das muss also eine andere Sauerei sein. Ich 
frage mich, wie man das hin bekommen kann. Ein zerdroschener Stack kann 
es ja kaum sein, wenn das Problem in der Update-Schleife von 
eeprom_update_block passiert.

Der Optiboot Bootlader setzt übrigens WDRF zurück, nachdem er den 
Registerwert in ein Prozessorregister geladen hat, das zur Anwendung 
durchgereicht wird.

Ich habe eben im ASM-Code meines Programmes nachgesehen: WDRF wird dort 
nicht gelöscht – das muss man selbst machen. Da ich den WD auch zum 
gewollten Reset benutze, muss das alles korrekt abgehandelt werden. Das 
ist auch der Grund, warum MCUSR vom Optiboot zugegriffen wird.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Moriz schrieb:
> Der Optiboot Bootlader setzt übrigens WDRF zurück, nachdem er den
> Registerwert – wenn ich mich recht erinnere – an einer festen Adresse
> gespeichert hat.

Das ist auch die sinnvolle Variante: zeitig im Startupcode (wenn man 
einen Bootloader hat, dann in diesem) auslesen und löschen.

Ich kann mir aber eigentlich nicht vorstellen, dass man das Ding aus 
Versehen einschalten kann … wobei, wenn dein Code irgendeinen 
unbedachten Wert versehentlich auf Adresse 0x60 schreibt, klar, dann 
könnte das passieren.

von Moriz (untertaucher)


Angehängte Dateien:

Lesenswert?

Das Irre ist, dass ich bei nicht initialisiertem WD und der Sequenez
    cli();
    MCUSR &= ~(1 << WDRF);
    WDTCSR &= ~(1 << WDIE);
    wdt_disable();
ganz am Anfang des Programms in der ISR des Watchdogs lande. (Die 
Interrupts sind wieder freigegeben, wenn der Absturz passiert.) Und das 
mitten in eeprom_update_block.

Die Fuses des Controllers: E:FD, H:DA, L:FF – passt, oder liege ich da 
falsch?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Wenn du in der ISR des Watchdogs landest, solltest du dir doch ausgeben 
lassen können, welchen Wert WDTCSR an der Stelle hat.

Ich dachte, du bekommst einen Reset. (Sorry, hatte ich überlesen.)

: Bearbeitet durch Moderator
von Moriz (untertaucher)


Lesenswert?

Jörg W. schrieb:
> welchen Wert WDTCSR an der Stelle hat.

Das war ein guter Tipp: WDTCSR=08 – WDE = 1. Dann muss ich eigentlich 
nur noch rausfinden, "welches Schwein" das war…

Übrigens: S. 55 des 328P-Datenblatts sagt:
1
• Bit 3 – WDE: Watchdog System Reset Enable
2
WDE is overridden by WDRF in MCUSR. This means that WDE is always set when WDRF is set. To clear WDE, WDRF must be cleared first. This feature ensures multiple resets during conditions causing failure, and a safe start-up after the failure.

Gut versteckt…

von Moriz (untertaucher)


Lesenswert?

So, Übeltäter gefunden. Japs.

Es geschah in einer wait-Funktion, die per Task-Schduler ein halbaktives 
Wait ausführt und in einer Schleife das vom Scheduler zu setzende Bit 
abfragt und während der Wartezeit idle() aufruft:
1
void idle(void) {
2
    uint8_t timeout = (WDTCSR & ((1 << WDP2) | (1 << WDP1) | (1 << WDP0))) | ((WDTCSR & (1 << WDP3) ? 8 : 0));
3
4
    wdt_reset();
5
    WDTCSR |= (1 << WDIE);                              // Enable WDT interrupt
6
7
    sleep_enable();
8
    sleep_cpu();
9
    sleep_disable();
10
11
    wdt_reset();
12
    wdt_enable(timeout);                                // remember timeout
13
    WDTCSR |= (1 << WDIE);                              // Enable WDT interrupt
14
}

wenn während sleep_cpu() der Wachhund überläuft, gibts den WD-Interrupt:
1
ISR(WDT_vect) {
2
    if ((_SLEEP_CONTROL_REG & (uint8_t)_SLEEP_ENABLE_MASK)) {
3
        // watchdog interrupt in idle
4
        MCUSR &= ~(1 << WDRF);
5
        wdt_reset();
6
        WDTCSR |= (1 << WDIE);                          // Enable WDT interrupt
7
    } else {
8
        // *** RESET MCU NOW ***
9
        wdt_enable(WDTO_15MS);
10
        while (1) ;                                     // wait for 2nd watchdog overflow
11
    }
12
}

Eigentlich sollte der if()-Zweig ablaufen, tut er aber nicht und so 
endet die ganze Chose mit einem zünftigen Reset…

Die Ursache: die if-Bedingung ist Quatsch – dort muss SleepEnable (SE) 
im SMCR getestet werden und die Manipulation des Watchdog in idle() 
unterbleibt…

Tja, mit Hanseline saust die Maschine…

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Schön, dass du es gefunden hast!

Ich fürchte, gegen solche Logikfehler hätte auch kein Lint geholfen – um 
nochmal auf den Thread-Titel zurück zu kommen.

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.