ESP32 ESP-IDF看门狗TWDT

本文介绍ESP32上任务看门狗计时器(TWDT)的使用方法,包括初始化、订阅任务、重置及触发后的处理流程。通过实例演示如何避免程序因任务阻塞而崩溃。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

陈拓 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");
}

参考文档

  1. ESP32任务看门狗实践https://www.codeleading.com/article/25393323890/

### ESP32 看门狗配置与使用 #### 创建和初始化看门狗定时器 ESP-IDF 支持两种类型的看门狗定时器:中断看门狗定时器(IWDT)和任务看门狗定时器(TWDT)[^1]。为了防止系统因异常情况而陷入死循环或卡住,合理配置这两种看门狗是非常重要的。 对于中断看门狗而言,其主要作用在于监视ISR(中断服务程序),确保它们不会无故占用过多时间;而对于任务看门狗,则是用来监督普通任务执行状况,当某个任务持续运行超过设定时限时触发相应处理机制。 下面是一个简单的例子来展示如何创建并启动一个基本的任务看门狗: ```c #include "esp_task_wdt.h" // 初始化任务看门狗,默认超时期限为 1 秒钟 void init_watchdog() { esp_err_t err = esp_task_wdt_init(1, true); if (err != ESP_OK){ // 错误处理... } } // 将当前任务加入到受监控列表中 void add_current_task_to_watchdog(){ esp_err_t err = esp_task_wdt_add(NULL); if (err != ESP_OK){ // 错误处理... } } ``` 以上代码片段展示了怎样通过`esp_task_wdt_init()`函数设置全局参数以及利用`esp_task_wdt_add()`方法把特定任务纳入监测范围之内。 #### 定期喂狗操作 一旦设置了看门狗之后,在正常情况下应该周期性地调用刷新命令以重置计数器,从而避免意外重启发生。这通常是在主循环里完成的: ```c while(true){ // 执行一些耗时较长的操作... // 刷新任务看门狗以防溢出导致复位 esp_task_wdt_reset(); vTaskDelay(pdMS_TO_TICKS(50)); // 延迟一段时间再继续下一轮迭代 } ``` 上述示例中的`vTaskDelay()`用于模拟实际应用中存在的延迟现象,而在每次循环结束前都应当及时调用一次`esp_task_wdt_reset()`来进行喂狗动作。 #### 关闭看门狗 如果不再需要某项任务受到看门狗保护的话,可以通过如下方式将其移除掉: ```c esp_err_t result = esp_task_wdt_delete(NULL); if(result != ESP_OK){ // 处理错误... } ``` 此段代码实现了从被监控对象集合里面删除指定条目的功能,其中传入NULL表示针对当前正在运行的那个线程进行解除关联操作。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晨之清风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值