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?
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.
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.
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.
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.
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?
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.
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:
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…