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 |
|
2 |
|
3 |
|
4 |
|
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...