陈拓 2022/07/02-2022/07/02
1. 概述
此示例演示如何使用任务看门狗计时器Task Watchdog Timer (TWDT)的以下功能:
- 如何初始化和取消初始化TWDT
- 如何订阅和取消订阅TWDT的任务
- 如何向阅和取消订阅TWDT的用户
- 如何使任务和用户可以重置(即喂狗)TWDTs
2. 官方例程国内镜像
https://gitee.com/EspressifSystems/esp-idf/tree/master/examples/system/task_watchdog
3. 开发环境
《用乐鑫国内Gitee镜像搭建ESP32开发环境》
https://zhuanlan.zhihu.com/p/348106034
https://blog.csdn.net/chentuo2000/article/details/113424934?spm=1001.2014.3001.5501
4. 复制代码
- 克隆官方例程
将官方例子项目复制到ESP-IDF开发工具之外:
cd ~/esp
cp -r ~/esp/esp-idf/examples/system/task_watchdog ~/esp/
- 项目树
cd ~/esp/task_watchdog
tree
5. 构建项目
- 刷新esp-idf环境
get_idf
- 设定目标芯片
idf.py set-target esp32
- 配置项目
idf.py menuconfig
1) 将闪存设置为4MB
2) 在任务看门狗超时时调用紧急处理程序
保存,退出。
- 编译项目
idf.py build
- 烧写项目
查看USB转串口的COM口号:
烧写:
idf.py -p /dev/ttyS3 -b 115200 flash
- 启用监视器
idf.py monitor -p /dev/ttyS3
当示例正常运行时,将观察到以下输出:
用户可以注释掉esp_task_wdt_reset()或esp_task_wdt_reset_user()调用以测试触发TWDT,
//CHECK_ERROR_CODE(esp_task_wdt_reset(), ESP_OK);
这将导致以下输出:
看门狗被触发。
因为TWDT超时时间为3秒:
#define TWDT_TIMEOUT_S 3 // TWDT超时时间
延时为10秒:
vTaskDelay(pdMS_TO_TICKS(10000)); // 延时10秒
在10秒延时期间,看门狗有3次触发,之后程序会继续执行:
6. 看门狗触发后立即重启程序
如果你希望任务看门狗触发之后进行重启。
- 设置在任务看门狗超时时调用紧急处理程序
- 增加esp_task_wdt_isr_user_handler()函数
void esp_task_wdt_isr_user_handler(void)
{
esp_restart();
}
这样在看门狗触发时程序会重新启动:
7. 完整代码
/* Task_Watchdog Examples
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_task_wdt.h"
#define TWDT_TIMEOUT_S 3 // TWDT超时时间
#define TASK_RESET_PERIOD_S 2 // 任务重置周期时间
/*
* 宏,用于检查TWDT的输出,如果检测到返回错误码则触发中止程序执行。
*/
#define CHECK_ERROR_CODE(returned, expected) ({ \
if(returned != expected){ \
printf("TWDT ERROR\n"); \
abort(); \
} \
})
static TaskHandle_t task_handles[portNUM_PROCESSORS];
// 回调由app_main()创建的用户任务
void reset_task(void *arg)
{
// 为TWDT添加任务,并检查dwt状态看是否添加
CHECK_ERROR_CODE(esp_task_wdt_add(NULL), ESP_OK);
CHECK_ERROR_CODE(esp_task_wdt_status(NULL), ESP_OK);
while(1){
// 每2秒重置一次看门狗
//CHECK_ERROR_CODE(esp_task_wdt_reset(), ESP_OK); // 喂狗。注释此行以测试触发TWDT超时
vTaskDelay(pdMS_TO_TICKS(TASK_RESET_PERIOD_S * 1000));
}
}
void esp_task_wdt_isr_user_handler(void)
{
esp_restart();
}
void app_main(void)
{
printf("Initialize TWDT\n");
// 初始化或者重新初始化TWDT
CHECK_ERROR_CODE(esp_task_wdt_init(TWDT_TIMEOUT_S, false), ESP_OK);
// 如果启动时未订阅空闲任务,则将其订阅到TWDT
#ifndef CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0
esp_task_wdt_add(xTaskGetIdleTaskHandleForCPU(0));
#endif
#if CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1 && !CONFIG_FREERTOS_UNICORE
esp_task_wdt_add(xTaskGetIdleTaskHandleForCPU(1));
#endif
// 创建用户任务并添加到看门狗
for(int i = 0; i < portNUM_PROCESSORS; i++){
xTaskCreatePinnedToCore(reset_task, "reset task", 1024, NULL, 10, &task_handles[i], i);
}
printf("Delay for 10 seconds\n");
vTaskDelay(pdMS_TO_TICKS(10000)); // 延时10秒
printf("Unsubscribing and deleting tasks\n");
// 从任务看门狗中删除并取消订阅用户任务,然后取消订阅空闲任务
for(int i = 0; i < portNUM_PROCESSORS; i++){
vTaskDelete(task_handles[i]); // 首先删除用户任务(防止重置未订阅的任务)
CHECK_ERROR_CODE(esp_task_wdt_delete(task_handles[i]), ESP_OK); // 从TWDT取消订阅任务
CHECK_ERROR_CODE(esp_task_wdt_status(task_handles[i]), ESP_ERR_NOT_FOUND); // 确认任务已取消订阅
// 取消订阅空闲任务
CHECK_ERROR_CODE(esp_task_wdt_delete(xTaskGetIdleTaskHandleForCPU(i)), ESP_OK); // 从TWDT取消订阅空闲任务
CHECK_ERROR_CODE(esp_task_wdt_status(xTaskGetIdleTaskHandleForCPU(i)), ESP_ERR_NOT_FOUND); // 确认空闲任务已取消订阅
}
// 在所有任务取消订阅后取消TWDT
CHECK_ERROR_CODE(esp_task_wdt_deinit(), ESP_OK);
CHECK_ERROR_CODE(esp_task_wdt_status(NULL), ESP_ERR_INVALID_STATE); //Confirm TWDT has been deinitialized 确认TWDT已解除初始化
printf("Complete\n");
}
参考文档
- ESP32任务看门狗实践https://www.codeleading.com/article/25393323890/