ESP32学习笔记(40)——Watchdog看门狗使用

本文详细介绍了ESP32中的中断看门狗和任务看门狗(TWDT)功能,用于在程序出现故障时重启系统。中断看门狗检测FreeRTOS任务切换的阻塞,而TWDT则监视任务是否长时间占用CPU。ESP-IDF提供了相应的API进行初始化、添加任务和重置看门狗。示例代码展示了如何使用TWDT并测试其超时功能。

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

一、简介

看门狗其实就是一个定时器,从功能上说它可以让微控制器在程序发生意外(程序进入死循环或跑飞)的时候,能重新回复到系统刚上电状态,以保障系统出问题的时候可以重启一次。说的复杂一点,看门狗就是能让程序出问题是能重新启动系统。

ESP-IDF 支持两种类型的看门狗:中断看门狗定时器和任务看门狗定时器(TWDT)。中断看门狗定时器和 TWDT 都可以使用项目配置菜单启用,但是 TWDT 也可以在运行时启用。中断看门狗负责检测 FreeRTOS 任务切换被长时间阻塞的情况。TWDT 负责检测任务长时间不让步 CPU 运行的情况。

1.1 中断看门狗

中断看门狗可确保 FreeRTOS 任务切换中断长时间不被阻止。这种情况很不好,因为没有其他任务能获得 CPU 运行时间,包括可能重要的任务,如 WiFi 任务和空闲任务。阻塞的任务切换中断可能发生当一个程序运行到无限循环并且中断被禁用或挂起中断。

中断监视程序的默认操作是调用 panic 处理程序。将会把寄存器转储以便于使用 OpenOCD 或 gdbstub找到,在禁用中断的情况下会阻塞哪些代码。根据 panic 处理程序的配置,它还可以盲目地重置 CPU,这在生产环境中可能是首选。

中断看门狗是围绕定时器组 1 中的硬件看门狗构建的。如果由于某种原因这个看门狗无法执行调用 panic 处理程序的 NMI 处理程序(例如,因为 IRAM 被垃圾覆盖),它将硬重置 SOC。

1.2 任务看门狗

任务看门狗定时器 (TWDT) 负责检测运行的任务在长时间没有让出 CPU 的情况。这是 CPU "饥饿"的症状,通常是由一个高优先级任务不让出 CPU 资源的循环引起,从而使较低优先级任务无法获得 CPU 资源。这可能是外围设备上的代码写得不好,也可能是陷入无限循环的任务。

默认情况下,TWDT 将监视每个 CPU 的空闲任务,但任何任务都可以选择由 TWDT 监视。每个观察任务必须定期“重置” TWDT 以指示它们已被分配 CPU 时间。如果任务未在 TWDT 超时期限内重置,则将打印一条警告,其中包含有关哪些任务未能及时重置 TWDT 以及哪些任务当前正在 ESP32 CPU 上运行的信息。并且还有可能在用户代码中重新定义函数 esp_task_wdt_isr_user_handler 以接收此事件。

默认情况下,make menuconfig 中的 CONFIG_TASK_WDT 将被启用,导致 TWDT 在启动期间自动初始化。同样,CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0 和 CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 也会默认启用,导致两个空闲任务在启动期间订阅 TWDT。

默认情况下TWDT是开启的,间隔时间默认是5s。可以通过menuconfig修改,也可以在代码中重新初始化修改间隔。

ESP-IDF 编程指南——Watchdogs

二、API说明

以下 TWDT 接口位于 esp_system/include/esp_task_wdt.h

2.1 esp_task_wdt_init

2.2 esp_task_wdt_add

2.3 esp_task_wdt_reset

三、看门狗测试

使用 examples\system\task_watchdog 中的例程

#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_task_wdt.h"

#define TWDT_TIMEOUT_S          3
#define TASK_RESET_PERIOD_S     2

/*
 * Macro to check the outputs of TWDT functions and trigger an abort if an
 * incorrect code is returned.
 */
#define CHECK_ERROR_CODE(returned, expected) ({                        \
            if(returned != expected){                                  \
                printf("TWDT ERROR\n");                                \
                abort();                                               \
            }                                                          \
})

static TaskHandle_t task_handles[portNUM_PROCESSORS];

//Callback for user tasks created in app_main()
void reset_task(void *arg)
{
    //Subscribe this task to TWDT, then check if it is subscribed
    CHECK_ERROR_CODE(esp_task_wdt_add(NULL), ESP_OK);
    CHECK_ERROR_CODE(esp_task_wdt_status(NULL), ESP_OK);

    while(1){
        //reset the watchdog every 2 seconds
        CHECK_ERROR_CODE(esp_task_wdt_reset(), ESP_OK);  //Comment this line to trigger a TWDT timeout
        vTaskDelay(pdMS_TO_TICKS(TASK_RESET_PERIOD_S * 1000));
    }
}

void app_main(void)
{
    printf("Initialize TWDT\n");
    //Initialize or reinitialize TWDT
    CHECK_ERROR_CODE(esp_task_wdt_init(TWDT_TIMEOUT_S, false), ESP_OK);

    //Subscribe Idle Tasks to TWDT if they were not subscribed at startup
#ifndef CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0
    esp_task_wdt_add(xTaskGetIdleTaskHandleForCPU(0));
#endif
#ifndef CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1
    esp_task_wdt_add(xTaskGetIdleTaskHandleForCPU(1));
#endif

    //Create user tasks and add them to watchdog
    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));   //Delay for 10 seconds

    printf("Unsubscribing and deleting tasks\n");
    //Delete and unsubscribe Users Tasks from Task Watchdog, then unsubscribe idle task
    for(int i = 0; i < portNUM_PROCESSORS; i++){
        vTaskDelete(task_handles[i]);   //Delete user task first (prevents the resetting of an unsubscribed task)
        CHECK_ERROR_CODE(esp_task_wdt_delete(task_handles[i]), ESP_OK);     //Unsubscribe task from TWDT
        CHECK_ERROR_CODE(esp_task_wdt_status(task_handles[i]), ESP_ERR_NOT_FOUND);  //Confirm task is unsubscribed

        //unsubscribe idle task
        CHECK_ERROR_CODE(esp_task_wdt_delete(xTaskGetIdleTaskHandleForCPU(i)), ESP_OK);     //Unsubscribe Idle Task from TWDT
        CHECK_ERROR_CODE(esp_task_wdt_status(xTaskGetIdleTaskHandleForCPU(i)), ESP_ERR_NOT_FOUND);      //Confirm Idle task has unsubscribed
    }


    //Deinit TWDT after all tasks have unsubscribed
    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

    printf("Complete\n");
}

编译后运行:

将喂狗函数进行屏蔽后:

//CHECK_ERROR_CODE(esp_task_wdt_reset(), ESP_OK);  //Comment this line to trigger a TWDT timeout


• 由 Leung 写于 2021 年 7 月 28 日

• 参考:ESP32任务看门狗实践
    ESP32 任务看门狗(TaskWDT)组件与用户任务监控

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Leung_ManWah

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

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

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

打赏作者

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

抵扣说明:

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

余额充值