Lint für avr-gcc

OP #7365094
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?

Moderator Persönliche Seite #7365153
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.

OP #7365190
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.

Moderator Persönliche Seite #7365198
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.

OP #7365299
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.

Moderator Persönliche Seite #7365309
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.

OP #7365358
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?

Angehängte Dateien:
OP #7365364
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…

OP #7365411
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…

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren