解決esp32看門狗復位的問題


 

在esp32程序中freeRTOS任務運行的時候經常都會遇到看門狗復位的問題,會在日志中打印類似如下的消息:

E (36942) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time: E (36951) task_wdt: - IDLE0 (CPU 0) E (36951) task_wdt: Tasks currently running: E (36951) task_wdt: CPU 0: WiFiTask E (36951) task_wdt: CPU 1: IDLE1 E (36951) task_wdt: Aborting. abort() was called at PC 0x4016113c on core 0

ELF file SHA256: 0000000000000000

Backtrace: 0x40094274:0x3ffbfd00 0x400944ed:0x3ffbfd20 0x4016113c:0x3ffbfd40 0x40090f2d:0x3ffbfd60 0x40085e05:0x3ffd2e60 0x400918e7:0x3ffd2e80 0x40091a46:0x3ffd2ea0 0x4017f20d:0x3ffd2ec0 0x400e4941:0x3ffd2ee0 0x400e16ec:0x3ffd2f00 0x400e1943:0x3ffd2f20 0x400e0b05:0x3ffd2f40 0x400e1086:0x3ffd2f90 0x400e12c5:0x3ffd3080 0x400d5746:0x3ffd3140 0x400d61e3:0x3ffd31e0 0x400954f2:0x3ffd3270

Rebooting...

在github上有個貼子也是討論這個的,見https://github.com/espressif/arduino-esp32/issues/595

其中有一種解決辦法是在循環中添加vTaskDelay(),即添加一個延時來供系統喂狗,但是這樣會大大降低程序性能,freeRTOS中的延時基本都是微秒級別的,在我的程序中會降低程序采樣率。在這個貼子中給出了很多方法,諸如自己構造一個很小的延時,關閉看門狗,在循環中喂狗等等,觸發看門狗的原因下面有個老哥解釋如下:

ESP-IDF creats a watchdog timer for idle tasks in addition to any tasks doing your work. IE, it creates two extra tasks IDLE0 & IDLE 1 (one for each core), whose sole purpose is to do nothing, ie idle. These idle tasks simply feed the watchdog whenever its respective core is idling. Whenever any task, in Arduino or in ESP-IDF, as well as interrupt routines, run with a higher priority than the IDLE tasks without delaying or blocking sufficiently long/often enough (such as when executing in a tight while loop), it triggers the watchdog, because that core is now fully busy, meaning it is no longer idling often enough, meaning the idle "tasks" get starved, so they can't reset the watchdog. Your app_main() in ESP-IDF and loop() in Arduino, run with higher priorities than the IDLE tasks. This is the source of the problem. You either have to make some call that blocks your code for atleast 10mS in sufficient intervals, or you have to call yield() or vTaskDelay(NUMBER_OF_MILLISECONDS_TO_DELAY / portTICK_PERIOD_MS) with a minimum of 10mS (otherwise, due to discreteness, it amounts to 0, ie no delay at all, thus won't work. You can work around this by increasing the scheduler frequency).

An alternative is fully disabling the watchdog, but this nullifies its uses. There is a more elegant solution. If you do need something to consume all available CPU time, and don't want to bother with considerations of delay or blocking, and don't want to disable the watchdog, but don't want it to bark, then put all intensive code operations in its own function or set of functions, and call it/each using the following task creation function:

xTaskCreate(someFunction, "HumanReadableNameofTask", 4096, NULL, tskIDLE_PRIORITY, NULL);

where your function has the following signature:

void someFunction(void* arg)

This won't trip the watchdog no matter what, because it uses the tskIDLE_PRIORITY priority. I'm not sure what value it has, but it seems greater than 10, because I tried 10 and it didn't work. Not sure if 11 did, which would be lower priority than the IDLE tasks. Note though, any code you place in someFunction(), won't execute at all except during times when the CPU is IDLE. So all your other code must still sleep/delay/block sufficiently enough to give it time to run. This is the ideal place for endless intensive code, such as motor control and PID control loops. But beware, tasks with identical priority can starve each other. So if you use more than one such function, each containing a tight loop, then I don't know whether one task can fully starve the other or not. Placing one at an even lower priority than the other won't help, it would only guarantee that that one gets starved, unless the one with higher priority blocks.

You can even do this in Arduino. Note: The loop() in Arduino is not intended for intensive calculations, which is why it not-so-smartly was placed at same priority as wifi. Whenever you do need to start intensive operations, start them on separate FreeRTOS tasks as mentioned above.

翻譯過來大概就是:

ESP-IDF 為空閑任務創建了一個看門狗計時器,除了執行您工作的任何任務。IE,它創建了兩個額外的任務 IDLE0 和 IDLE 1(每個內核一個),其唯一目的是什么都不做,即空閑。每當其各自的內核處於空閑狀態時,這些空閑任務只是為看門狗提供服務。每當 Arduino 或 ESP-IDF 中的任何任務以及中斷例程以比 IDLE 任務更高的優先級運行而沒有延遲或阻塞足夠長/經常足夠長的時間(例如在緊密的 while 循環中執行時),它都會觸發看門狗,因為該核心現在完全忙碌,這意味着它不再經常空閑,這意味着空閑的“任務”會被餓死,所以他們無法重置看門狗。ESP-IDF 中的 app_main() 和 Arduino 中的 loop() 以比 IDLE 任務更高的優先級運行。這就是問題的根源。

另一種方法是完全禁用看門狗,但這會使它的用途無效。有一個更優雅的解決方案。如果你確實需要一些東西來消耗所有可用的 CPU 時間,並且不想考慮延遲或阻塞,不想禁用看門狗,但又不想讓它吠叫,那么把所有密集的代碼在其自己的函數或函數集中進行操作,並使用以下任務創建函數調用它/每個:

xTaskCreate(someFunction, "HumanReadableNameofTask", 4096, NULL, tskIDLE_PRIORITY, NULL);

您的函數具有以下簽名:

void someFunction(void* arg)

這無論如何都不會觸發看門狗,因為它使用 tskIDLE_PRIORITY 優先級。我不確定它有什么價值,但它似乎大於 10,因為我嘗試了 10 並且它不起作用。不確定 11 是否有,這將比 IDLE 任務的優先級低。但請注意,您放置在 someFunction() 中的任何代碼都不會執行,除非在 CPU 空閑時。因此,您所有其他代碼仍必須足夠睡眠/延遲/阻塞以使其有時間運行。這是無休止的密集代碼的理想場所,例如電機控制和 PID 控制循環。但請注意,具有相同優先級的任務可能會互相餓死。因此,如果您使用多個這樣的函數,每個函數都包含一個緊密循環,那么我不知道一個任務是否會完全餓死另一個任務。將一個置於比另一個更低的優先級不會'

你甚至可以在 Arduino 中做到這一點。注意:Arduino 中的 loop() 不適用於密集計算,這就是為什么它不那么聰明地被置於與 wifi 相同的優先級的原因。每當您確實需要啟動密集操作時,如上所述在單獨的 FreeRTOS 任務上啟動它們。

大概就是你的任務優先級比空閑任務高且你的任務在高密度的運行,所以導致系統的空閑函數無法執行喂狗從而導致看門狗觸發,所以在建立任務時使用優先級tskIDLE_PRIORITY,根據后面老哥的解釋他應該是0,反正是等效於空閑任務的優先級的,親測看門狗不會被觸發。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM