Falls noch jemand anders bei so einem Problem graue Haare bekommt:
System: Ein neuerer UltraLowPower-STM32, also L-Reihe, U-Reihe oder
WL-Reihe.
Auslöser: Ein Stop- oder Standby-Modus wird genutzt.
Symptom: Nach dem Debuggen/Flashen mittels Debugger friert das Programm
ein. Auch ein Reset per NRST-Eingang/Button löst das nicht, das Programm
bleibt stehen. Nur die Spannungsversorgung zu trennen lässt das Programm
korrekt durchlaufen.
Bei Verwendung eines RTOS, welches in der Initialisierung vor der
main()-Funktion schon temporär den Stop/Standby-Modus nutzt, wird die
main() eventuell gar nicht erst aufgerufen.
Wichtiges Detail: Beim Betreten des Stop/Standby-Modus verliert der
Debugger die Verbindung, aber das Programm scheint zumindest ein Stück
weit weiter zu laufen. Ohne Debugger kann man aber nicht herausfinden wo
es stehen bleibt...
Der Grund ist so blöde wie unerwartet:
Der Debugger setzt beim Starten (nach dem Flashen) einen Breakpoint in
der main() und/oder woanders. Wenn der STM32 in den Stop/Standby geht
und die Verbindung zum Debugger getrennt wird, bleibt der Breakpoint
erhalten, und das Programm bleibt genau dort stehen, aber kann nicht
mehr fortgesetzt werden. Dieser Breakpoint bleibt auch nach einem Reset
per NRST-Pin erhalten, nur ein Power-Cycle löscht ihn.
Die Lösung:
Man kann den Debugger in Sleep/Stop/Standby am Leben erhalten indem man
beim Start einmal
1 | // Prüfe ob Debugger aktiv ist (dieses Bit überlebt den Reset per NRST):
|
2 | if (CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) {
|
3 | // Debugger auch im Sleep/Stop/Standby aktiv lassen
|
4 | DBGMCU->CR = DBGMCU_CR_DBG_STANDBY | DBGMCU_CR_DBG_STOP | DBGMCU_CR_DBG_SLEEP;
|
5 | }
|
macht. Bei Verwendung eines RTOS muss man aufpassen, dass dies früh in
der Initialisierung vor dem ersten Stop/Standby gemacht wird, weil bei
der Initialisierung von Treibern bereits der Stop/Standby betreten
werden könnte wenn dort ein Delay-Aufruf gemacht wird.
Dann tritt das Problem nicht mehr auf - allerdings muss man erstmal
drauf kommen, dass das unsaubere Trennen des Debuggers wirklich die
Problemursache ist. Das hatte ich zuerst als unwichtigen Seiteneffekt
abgetan. Ich hatte diese Bits im DBGMCU_CR-Register schon früh bei der
Suche gesetzt und festgestellt dass es dann funktioniert, dachte aber,
dass durch das Setzen dieser Bits vielleicht noch irgendwelche anderen
Teile des Prozessors (Takt, Power domains) erhalten bleiben welche das
Verhalten im Standby/Stop-Modus irgendwie beeinflussen und nur ein
Symptom beheben, aber tatsächlich noch irgendwo ein Software-Fehler
besteht. Insbesondere auch, weil ich vorher schon über diverse andere
Bugs im Zusammenhang mit dem Stop-Mode im RTOS gestolpert bin, die sich
sehr ähnlich äußerten.
Und weil man ja im Produktivbetrieb diese Bits nicht setzen möchte um
möglichst viel Strom zu sparen, soll es ja auch ohne diese funktionieren
- aber woher will man wissen, dass es dann auch garantiert funktioniert
(und die Bits nicht nur ein Symptom eines tieferlegenden Problems
maskieren) - man kann es ja so nicht debuggen. Man muss aber erstmal
drauf kommen dass es ohne diese Bits im Produktivbetrieb natürlich gar
kein Problem gibt, weil ja nie ein Debugger dran ist, und somit o.g.
Code auch immer drin bleiben kann.
Also ein lustiges Verwirrspiel: Die vermeintliche Symptombekämpfung
behebt tatsächlich das eigentliche Problem, und es gibt kein
tieferlegendes Problem. Sonst ist es immer anders rum! Drauf gekommen
bin ich erst als ich (mehr aus Verzweiflung als aus Methode) vor der
main() - und somit vor dem ersten Breakpoint - noch weitere Breakpoints
gesetzt hatte, und sich das Verhalten des Programms änderte obwohl der
Debugger ja getrennt wurde, was erst durch Power-Cycle "repariert"
wurde...