SOC-ESP32S3部分:6-任务看门狗
飞书文档https://x509p6c8to.feishu.cn/wiki/WGkkw8K3miMgvckU806c7FBkn6b
1.1、看门狗作用和默认设置
ESP32-S3的任务看门狗功能是一个重要的系统保护机制,它可以确保在程序发生异常(如进入死循环或跑飞)时,系统能够自动重启,以保障系统的稳定运行。
在学习单片机的时候,看门狗功能一般是放到后面讲解的,但是学习ESP32时,为什么提前说呢?因为ESP32 IDF是默认开启空闲任务看门狗功能的。不信?我们直接来跑下下面的代码:
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"static const char* TAG = "MyModule";void app_main(void)
{int count = 0;ESP_LOGI(TAG,"app_main run");while (1) {count++;}
}
5s左右就会看到以下打印:
I (282) MyModule: app_main run
E (5282) task_wdt: Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:
E (5282) task_wdt: - IDLE0 (CPU 0)
E (5282) task_wdt: Tasks currently running:
E (5282) task_wdt: CPU 0: main
E (5282) task_wdt: CPU 1: IDLE1
E (5282) task_wdt: Print CPU 0 (current core) backtraceBacktrace: 0x4200A8D2:0x3FC932A0 0x4200ACE8:0x3FC932C0 0x403771A9:0x3FC932F0 0x42009205:0x3FC97DA0 0x4201709F:0x3FC97DC0 0x4037A3BD:0x3FC97DF0
--- 0x4200a8d2: task_wdt_timeout_handling at /home/leo/work/esp/esp-idf/components/esp_system/task_wdt/task_wdt.c:434
0x4200ace8: task_wdt_isr at /home/leo/work/esp/esp-idf/components/esp_system/task_wdt/task_wdt.c:507
0x403771a9: _xt_lowint1 at /home/leo/work/esp/esp-idf/components/xtensa/xtensa_vectors.S:1240
0x42009205: app_main at /home/leo/work/esp/demo07/build/../main/main.c:12 (discriminator 1)
0x4201709f: main_task at /home/leo/work/esp/esp-idf/components/freertos/app_startup.c:208
0x4037a3bd: vPortTaskWrapper at /home/leo/work/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:139
这就是看门狗导致的异常,因为我们在while循环中没有任何延时或挂起操作,导致IDLE0空闲任务喂狗不及时从而触发错误。
在这里,我们可以知道默认情况下TWDT是开启的,间隔时间默认是5s,这个点,我们在menuconfig中也是可以看到的。
所以当我们的工程出现以上错误时,我们就可以主动去排查下是不是有耗时的操作导致空闲任务不能及时喂狗了。
也借这个示例给大家说明一下,后续我们执行代码遇到异常的问题,我们只需要仔细看下错误的提示即可,ESP32的错误日志是非常完善的,看错误日志就可以判断出是哪里出现了问题,如果我们看不懂,我们可以把日志贴给AI工具或者搜索引擎,也可以快速定位问题,不要看到一大串英文就慌,其实英文越多这个错误就越具体,只需要仔细查看错误的描述即可。 |
上述代码想要顺利执行,只需要添加vTaskDelay(1000 / portTICK_PERIOD_MS);即可,因为app_main调用延时函数就会自动挂起,释放处理器,空闲任务就可以完成喂狗操作啦。
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"static const char* TAG = "MyModule";void app_main(void)
{int count = 0;ESP_LOGI(TAG,"app_main run");while (1) {count++;vTaskDelay(1000 / portTICK_PERIOD_MS);}
}
2.1、修改看门狗超时时间
看门狗的喂狗间隔时间默认是5s,如何修改看门狗的超时时间呢?
方法1:
可以通过menuconfig修改Component config → ESP System Settings。
方法2:
通过代码修改
头文件
#include "esp_task_wdt.h"esp_err_t esp_task_wdt_reconfigure(const esp_task_wdt_config_t *config);
参数说明
config:
说明:指向任务看门狗配置结构体 esp_task_wdt_config_t 的指针。该结构体包含以下字段:
timeout_ms:
说明:任务看门狗的超时时间,单位为毫秒。trigger_panic:
说明:是否在任务看门狗超时后触发 panic。
触发 panic 是指系统遇到严重错误时,主动进入一种错误处理状态,通常会导致系统重启或停止运行。
Panic 是一种保护机制,用于防止系统在异常状态下继续运行,从而避免更严重的问题(如数据损坏或硬件损坏)。idle_core_mask:
说明:指定是否监控 IDLE 任务。
0:不监控任何 IDLE 任务。
0x1:监控 CPU 0 的 IDLE 任务。
0x3:监控 CPU 0 和 CPU 1 的 IDLE 任务。
所以我们可以通过以下代码修改看门狗超时时间
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"static const char* TAG = "MyModule";void app_main(void)
{esp_task_wdt_config_t twdt_config = {.timeout_ms = 10000, // 超时时间 10000 毫秒(10 秒).trigger_panic = true, // 超时后触发 panic.idle_core_mask = 1, //开启空闲任务看门狗};esp_task_wdt_reconfigure(&twdt_config);int count = 0;ESP_LOGI(TAG,"app_main run");while (1) {count++;vTaskDelay(1000 / portTICK_PERIOD_MS);}
}
3.1、为某个任务添加看门狗
上面我们可以看到默认设置监控的都是空闲任务看门狗功能,那如何为某个任务添加看门狗呢?例如为app_main添加看门狗功能,我们就可以使用下方代码:
头文件
#include "esp_task_wdt.h"esp_err_t esp_task_wdt_add(TaskHandle_t task_handle);
用于将任务添加到任务看门狗(Task Watchdog, TWDT)的监控列表中。
参数说明
task_handle:
说明:要添加到任务看门狗监控列表的任务句柄。
NULL:表示将当前任务(即调用 esp_task_wdt_add 的任务)添加到监控列表。
非 NULL:表示将指定的任务添加到监控列表。esp_err_t esp_task_wdt_reset(void);
用于重置调用该函数的任务的看门狗计数器。
当一个任务被添加到任务看门狗的监控列表后,它需要定期调用 esp_task_wdt_reset 函数,以避免看门狗超时触发异常。
最终代码如下:
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_task_wdt.h"void app_main(void)
{esp_task_wdt_config_t twdt_config = {.timeout_ms = 5000, // 超时时间 5000 毫秒(5 秒).trigger_panic = true, // 超时后触发 panic.idle_core_mask = 1,};esp_task_wdt_reconfigure(&twdt_config);// 将当前任务添加到看门狗监控列表esp_task_wdt_add(NULL);int i = 0;while (1) {printf("[%d] Hello world!\n", i);i++;// 让任务进入阻塞状态,延时 1 秒vTaskDelay(1000 / portTICK_PERIOD_MS);// 喂狗esp_task_wdt_reset();}
}
上述代码中,如果我们把vTaskDelay(1000 / portTICK_PERIOD_MS); 改为 vTaskDelay(10000 / portTICK_PERIOD_MS);,那就会导致main任务不能及时喂狗,触发错误