ESP32双核FreeRTOS任务看门狗与中断看门狗同时触发的定位方法
1. 原理讲解:TWDT与IWDT的差异与同时触发的含义
ESP32的FreeRTOS提供两种看门狗:
- 任务看门狗(TWDT):监控用户任务是否按时喂狗。每个任务可单独注册,超时后触发panic或打印任务状态。
- 中断看门狗(IWDT):监控中断是否长时间关闭(如临界区过长、中断嵌套过深),超时后直接复位。
两者同时触发通常意味着:某个高优先级任务或中断服务程序长时间占用CPU,导致低优先级任务无法喂TWDT,同时中断被长时间屏蔽触发IWDT。常见场景:
- 在临界区(
portENTER_CRITICAL)内执行耗时操作(如Flash写、长循环)。 - 高优先级任务死循环且未让出CPU,低优先级喂狗任务饿死。
- 中断服务程序(ISR)中调用阻塞API或执行过长。
- 双核间共享资源竞争导致自旋锁长时间持有。
2. 配置步骤:开启看门狗与日志
在menuconfig中配置:
-
Component config → ESP System Settings → Interrupt watchdog:使能IWDT,超时时间设为300ms(默认)。 -
Component config → FreeRTOS → Task watchdog:使能TWDT,超时时间设为5s。 -
Component config → ESP System Settings → Panic handler behaviour:选择Print registers and halt或GDB stub,便于捕获现场。 - 开启Core Dump到Flash或UART,用于事后分析。
代码中初始化TWDT并添加当前任务:
#include "esp_task_wdt.h"
void app_main(void)
{
// 初始化TWDT,超时5秒
esp_task_wdt_init(5, true); // true: 超时触发panic
// 将当前任务加入TWDT监控
esp_task_wdt_add(NULL);
while (1) {
// 业务逻辑
vTaskDelay(pdMS_TO_TICKS(1000));
esp_task_wdt_reset(); // 喂狗
}
}
3. 定位方法:联合分析看门狗日志与任务状态
当两者同时触发时,串口会打印类似信息:
E (12345) task_wdt: Task watchdog got triggered.
E (12345) task_wdt: Tasks currently running:
E (12345) task_wdt: CPU 0: IDLE
E (12345) task_wdt: CPU 1: main
E (12345) esp_int_wdt: Interrupt wdt timeout on CPU0
关键信息解读:
-
Tasks currently running列出触发时各CPU正在运行的任务。若某CPU显示非IDLE且非喂狗任务,说明该任务可能死循环。 -
Interrupt wdt timeout on CPUx指出哪个CPU的中断被长时间关闭。 - 结合Core Dump中的调用栈,定位到具体函数。
定位流程:
- 复现问题,抓取完整串口日志。
- 查看TWDT打印的“当前运行任务”,确认哪个任务霸占CPU。
- 查看IWDT的CPU编号,与TWDT信息交叉验证。
- 若Core Dump可用,使用
idf.py coredump-info解析调用栈。 - 检查代码中该任务或ISR是否包含长临界区、死循环或阻塞调用。
4. 完整代码示例:模拟同时触发并捕获
以下代码故意在临界区内执行长循环,同时让喂狗任务无法运行,模拟双看门狗触发:
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_task_wdt.h"
#include "esp_int_wdt.h"
static void feed_task(void *arg)
{
esp_task_wdt_add(NULL);
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000));
esp_task_wdt_reset();
printf("Feed task alive\n");
}
}
static void hog_task(void *arg)
{
// 高优先级任务,长时间关闭中断
while (1) {
portENTER_CRITICAL(&spinlock); // 关闭中断
// 模拟耗时操作,超过IWDT超时
for (volatile int i = 0; i < 100000000; i++);
portEXIT_CRITICAL(&spinlock);
vTaskDelay(1); // 短暂让出,但不足以喂狗
}
}
void app_main(void)
{
esp_task_wdt_init(5, true);
xTaskCreatePinnedToCore(feed_task, "feed", 2048, NULL, 1, NULL, 0);
xTaskCreatePinnedToCore(hog_task, "hog", 2048, NULL, 5, NULL, 1);
}
运行后,串口将先后打印IWDT超时和TWDT超时,通过日志可看到CPU1上hog任务正在运行,且CPU1中断被关闭。
5. 注意事项
-
避免在临界区内调用可能阻塞的API,如
vTaskDelay、printf(可能触发锁)。 - 喂狗任务优先级不宜过低,否则易被高优先级任务饿死;建议单独创建一个中等优先级任务专门喂狗。
- 双核场景下,每个核上的喂狗任务需分别注册,因为TWDT按任务监控,不区分核心。
- IWDT超时时间通常较短(300ms),任何超过该时间的临界区都需拆分。
-
使用Core Dump时确保Flash分区足够,并在
menuconfig中配置正确的分区表。 - 调试时可将TWDT超时设为较大值,先定位IWDT问题,再逐步收紧。
通过上述方法,可系统性地定位ESP32双核下双看门狗同时触发的根因,避免盲目修改代码。
暂无评论,来抢沙发~