1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-11-22 23:56:13 +01:00

explain ESP32 watchdog

This commit is contained in:
Harald Barth 2021-10-24 12:59:28 +02:00
parent b6cfc39d23
commit 05eb0d763a

67
esp32wdt.txt Normal file
View File

@ -0,0 +1,67 @@
The ESP-IDF has interrupt watchdogs and task watchdogs. Normally on
each core there is a very low prio idle task (IDLE0, ILDE1) that feeds
the watchdog (an internal timer) and if that timer expires a
reboot/reset happens. This thought to enure that even the lowest prio
tasks get run ever.
Now enter the Arduino IDE generated task loop(). When there are two cores,
loop() is run on core1. If loop runs continiously, IDLE1 is never run and
triggers the watchdog. It is said that this can be previented by one
of the following:
1. Call delay(X) with big enough X
2. Call yield()
While the delay() method works, big enough X to run idle seem to be in
the ms range and there are definitely applications that can not accept
a several ms long pause in loop().
The yield() method does not work because it only seems not to yield to
a low prio task like IDLE1 in all circumstances.
Then the makers of the Arduino IDE did get the brilliant idea to
disable that IDLE1 calls the watchdog. Then loop() can spin on core1
and other tasks (like wifi or interrupts or whatever) can run on core0
and are watched by the IDLE0 watchdog. All swell and well. Almost.
Enter: SINGLE CORE ESP32
As the IDLE0 watchdog is not disabled it will fire when loop() runs on
core0. The next idea is to feed the watchdog from loop() just
alongside the yield. There is a function called esp_task_wdt_feed(),
so can that be used to feed the watchdog? Yes and no. While it
will feed the watchdog, there is as well as check in the ESP-IDF
that the watchdog is fed from ALL tasks that should feed it. So
if the setup is that IDLE0 should feed the watchdog, we can not
get away by calling esp_task_wdt_feed() from loop(). BUMMER!
But there seems to be a way around this. The watchdog is implemented
by low level timers/counters and these are accessible. So we can feed
the dog behind the back of the ESP-IDF:
#include "soc/timer_group_struct.h"
#include "soc/timer_group_reg.h"
void feedTheDog(){
// feed dog 0
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable
TIMERG0.wdt_feed=1; // feed dog
TIMERG0.wdt_wprotect=0; // write protect
// feed dog 1
TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable
TIMERG1.wdt_feed=1; // feed dog
TIMERG1.wdt_wprotect=0; // write protect
}
As I do not have a single core ESP32 I tested this by enabling the
IDLE1 watchdog (which normally is disabled) and checking that I
do get watchdog resets. Then I call feedTheDog() from loop()
and the resets disappear. So I guess the feeding operation is
successful. For a single core ESP32 of course only dog0 has
to be fed.
Feed dog directly behind back of the ESP-IDF routines:
https://forum.arduino.cc/t/esp32-a-better-way-than-vtaskdelay-to-get-around-watchdog-crash/596889/13
Disable/Endable WDT code:
https://github.com/espressif/arduino-esp32/commit/b8f8502f
Get/set taskid on cores:
https://techtutorialsx.com/2017/05/09/esp32-get-task-execution-core/