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

一、简介

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

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 
#include 
#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)组件与用户任务监控

你可能感兴趣的:(ESP32学习笔记(40)——Watchdog看门狗使用)