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?
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).
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
Ich benutze gerne Qt Creator als Texteditor, wegen der umfangreichen Checks, die er macht: http://stefanfrings.de/avr_tools/index.html#qtcreator
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
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.
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.
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
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
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.
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?
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
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…
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…
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.