mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-12-25 21:41:23 +01:00
68 lines
2.9 KiB
Plaintext
68 lines
2.9 KiB
Plaintext
|
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/
|