【ESP-IDF】看门狗定时器
简单来说,看门狗就是防止我们程序出现意外,可以帮我们重启的一个定时器。
看门狗定时器的中断执行函数就是给我们的程序重启,因此我们需要在它中断之前把计数器的值清零(喂狗),否则就会不断重启。
当然了,真实情况会更复杂,但是为了简单,可以先这么理解。
ESP-IDF支持三种类型的看门狗。
分别是中断看门狗定时器(IWDT),任务看门狗定时器(TWDT),外部晶振看门狗定时器(XTWDT,它其实不叫外部晶振看门狗定时器,编程指南里是叫它XTAL32K看门狗定时器的,因为它使用的时钟源是32K的外部晶振,所以我觉得这么叫比较好理解才这么叫的)。
32K的外部晶振就是下图这样的。
再回顾一下定时器组。
每个定时器组有两个通用定时器和一个看门狗定时器。
任务看门狗定时器是基于定时器组0的看门狗定时构建的,而中断看门狗定时器用的是定时器组1里的。
我们常用的是任务看门狗,编程指南里我也只找到了任务看门狗的API。不过这不意味着我们只能使用任务看门狗,因为中断看门狗是默认就开启的,这也是为什么我们不能在中断里执行太久,否则就会重启的原因。
如果要对中断看门狗的配置进行修改,那么我们就需要进入到设置里去(在VSCode底下的工具栏里,没出来的话需要先启动一下ESP-IDF的插件)。
我们搜索CONFIG_ESP_INT_WDT。
可以看到我们可以选择是否启动中断看门狗以及中断最长的执行时间(看门狗最长喂狗周期,不过不用我们手动去喂)。
一般情况下我们不用去修改,用默认的就行,但如果确实要修改,那么记得保存。
另外无论你修改没修改,只要点开了这个界面,那么退出后再编译就会出错,我们需要先clean一下再编译。
任务看门狗定时器也可以配置,搜索CONFIG_ESP_TASK_WDT。
外部晶振看门狗定时器按理说也是一样的。
但是我按照编程指南里的选项去搜却搜不到,不过不打紧,因为我不用它。
接下来我们直接看看怎么使用任务看门狗定时器。
首先包含头文件。
#include "esp_task_wdt.h"
接着我们按照编程指南里的流程走就行。
首先是初始化TWDT,使用下面这个函数。
esp_err_t esp_task_wdt_init(const esp_task_wdt_config_t *config)
传入一个结构体变量的指针来进行配置。
第一个是超时的时间,我们直接传入以毫秒为单位的时间。
第二个是初始化时订阅空闲任务的核心掩码,这个我们直接全订阅即可,我们可以参考一下官方的例程看看怎么写。
第三个是看门狗定时器超时之后是否触发恐慌(直译),实际上就是是否重启,这边叫它panic(恐慌)而不是重启,是因为恐慌的这个行为我们可以选择。
另外我们也看到了,启动时初始化任务看门狗定时器默认也勾上了,所以当我们初始化的时候就会发现是已经初始化的。
既然初始化过了,所以第一步是不需要我们自己去做的,它会自动执行。
那如果我们要修改默认的配置呢?
我们使用下面这个函数来重新配置,参数和初始化一样。
esp_err_t esp_task_wdt_reconfigure(const esp_task_wdt_config_t *config)
配置完之后我们需要给任务绑定任务看门狗定时器。
esp_err_t esp_task_wdt_add(TaskHandle_t task_handle)
传入任务句柄。
接下来就是喂狗了,使用下面这个函数。
esp_err_t esp_task_wdt_reset(void)
没有参数,会自动把绑定在当前任务的看门狗定时器的重置,也就是喂狗操作。
如果需要查看当前任务有没有绑定任务看门狗定时器,我们使用下面这个函数。
esp_err_t esp_task_wdt_status(TaskHandle_t task_handle)
传入NULL参数就表示是查看当前这个任务。
也可以取消绑定任务看门狗定时器。
esp_err_t esp_task_wdt_delete(TaskHandle_t task_handle)
接下来来个小实验。开启任务定时器看门狗绑个任务,任务慢慢延长喂狗时间,直到超过设定的时间,ESP32重启。
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_task_wdt.h"
void test(void*) {
TickType_t delayTime = 2500;
TickType_t curTime = xTaskGetTickCount();
while (1) {
vTaskDelayUntil(&curTime, pdMS_TO_TICKS(delayTime));
printf("delay time is %ld\r\n",delayTime);
esp_task_wdt_reset();
delayTime += 100;
}
}
void app_main(void) {
printf("hello world\r\n");
TaskHandle_t testHandle;
xTaskCreate(test, "test", 1024 * 2, NULL, configMAX_PRIORITIES / 2, &testHandle);
esp_task_wdt_config_t twdt_initer = {
.idle_core_mask = (1 << portNUM_PROCESSORS) - 1, //0100 - 1 -> 0011 表示使用所有核心
.timeout_ms = 3000,
.trigger_panic = true
};
esp_task_wdt_reconfigure(&twdt_initer);
esp_task_wdt_add(testHandle);
TickType_t curTime = xTaskGetTickCount();
while (1) {
vTaskDelayUntil(&curTime, pdMS_TO_TICKS(1000));
}
}
作者:折途想要敲代码