看门狗

[English]

ESP 看门狗定时器分类

以 ESP32-C3 为例,如下:

  • ESP32-C3
    • 数字看门狗定时器
      • 主系统看门狗定时器(MWDT0 & MWDT1)

      • RTC 看门狗定时器(RWDT)

    • 模拟看门狗定时器
      • 超级看门狗(SWD)

看门狗触发原理

以 ESP32-C3 为例,它有两个主系统看门狗定时器,分别为 MWDT0 和 MWDT1。数字看门狗在运行期间会经历多个阶段,每个阶段均可配置单独的超时时间和超时动作。现有逻辑如下:

任务看门狗使用 MWDT0,中断看门狗使用 MWDT1,如果 ESP 没有及时喂狗导致看门狗超时后会触发看门狗中断。

备注

ESP32-C2 只有一个定时器组,因此只有一个主系统看门狗 MWDT0,它绑定到中断看门狗上,此时任务看门狗使用 esp_timer 实现。

中断看门狗

中断看门狗主要用于检测 FreeRTOS 长时间无法进行任务调度的场景。在实时操作系统中,任务调度机制确保需要实时处理的高优先级任务能够快速获得执行机会,从而避免出现长时间延时。如果系统无法正常进行任务调度,一些关键任务(如 Wi-Fi 收发包处理)可能会出现异常。

工作原理

中断看门狗使用 Timer Group 1 的硬件看门狗定时器,通过在滴答定时器(SysTick)中断中添加喂狗操作来监控系统任务调度状态。SysTick 中断在 ESP-IDF 中默认为最低中断优先级 1,而中断看门狗定时器的中断优先级为 4,高于大多数外设的中断等级,且无法被系统轻易屏蔽。

当长时间(默认 300 ms)未在 SysTick 中断中执行喂狗操作时,中断看门狗中断将被触发,系统进入中断看门狗异常处理程序,打印寄存器信息并发生崩溃。

备注

关中断操作只会关闭优先级 3 以下的中断。在 Xtensa 指令集中,中断优先级 4 及以上的中断只能在汇编函数中执行,因此中断看门狗中断无法被常规的关中断操作屏蔽。

中断看门狗工作原理图

中断看门狗工作原理图

滴答定时器(SysTick)

滴答定时器是芯片提供的系统节拍定时器,具有自动重载和溢出中断功能。在 RTOS 中,SysTick 为任务调度提供必要的时钟节拍,相当于系统的”心跳”。每当 SysTick 中断触发时,系统会进行一次任务切换。

可通过配置项 Component config -> FreeRTOS -> Tick rate 调整 tick 值来更改溢出中断的时间间隔。默认值为 100,即每 10 ms 触发一次 SysTick 中断。

警告

  • 过快的 SysTick 中断会导致频繁的任务调度,降低系统效率

  • 过慢的 SysTick 中断可能导致任务响应延迟

  • 操作系统的所有时间相关行为均以 Tick 为基准,如 vTaskDelay(100) 在 Tick rate 为 100 时延时 1 s,在 Tick rate 为 1000 时仅延时 100 ms

触发原因

中断看门狗触发的根本原因是 SysTick 中断无法正常执行,主要包括以下三种情况:

  1. 长时间关中断

    关中断操作用于保护不能被中途打断的程序段,禁止系统响应中断请求。长时间关中断是导致中断看门狗触发的最主要原因。

    常见场景包括:

    • 进入临界区(portENTER_CRITICAL

    • 使用自旋锁(spinlock)

    • 其他需要关中断保护的代码段

  2. 中断服务程序中存在阻塞

    中断服务程序(ISR)应遵循快速处理原则,将耗时操作转移到非中断环境中处理。如果 ISR 中存在复杂逻辑或阻塞操作,未能在中断看门狗超时前退出,将触发异常。

    常见问题:

    • ISR 中包含死循环

    • ISR 中执行耗时操作

    • ISR 中调用阻塞函数

  3. 未清除中断标志

    ISR 执行完毕后需要清除相应的中断标志位。如果未正确清除,中断将持续触发,阻止 SysTick 中断的正常执行。

    备注

    清除相应的中断标志位往往由系统自动完成,用户无需手动操作。

配置选项

中断看门狗相关配置位于 Component config ESP System Settings

  • Interrupt watchdog:启用或禁用中断看门狗功能

  • Interrupt watchdog timeout (ms):中断看门狗超时时间,默认为 300 ms

问题分析方法

当触发中断看门狗异常时,系统会停留在问题发生的位置。可通过以下方式进行分析:

  1. 查看程序计数器(PC)和调用栈(Backtrace):定位问题代码位置

  2. 检查崩溃信息中的上下文标识

    • 包含 Core X was running in ISR context 表示问题出现在中断函数中

    • 不包含此信息通常表示是关中断导致的问题,需要根据问题位置分析具体原因

实际案例分析

中断看门狗问题在实际应用中往往与 FreeRTOS 系统相结合,表现形式多样。详细的实际案例分析和解决方案,请参考 Interrupt wdt timeout on CPU0/CPU1 章节,其中包含了完整的问题分析流程和多个典型案例。

任务看门狗

任务看门狗用于检测特定任务长时间占用 CPU 资源的情况。在 FreeRTOS 抢占式内核中,高优先级任务总是优先获取 CPU 资源,这可能导致低优先级任务长时间无法执行。任务看门狗通过监控关键任务的执行状态,确保系统的多任务特性得到正确体现。

工作原理

任务看门狗使用 Timer Group 0 的硬件看门狗定时器。默认情况下,系统通过监控 IDLE 任务来判断是否出现看门狗现象。IDLE 任务是 FreeRTOS 中优先级最低的系统任务,如果该任务能够正常执行,说明其他任务未长时间占用 CPU 资源。

系统在进入 IDLE 任务时,会通过 IDLE 任务的钩子函数重置看门狗定时器。如果长时间未能进入 IDLE 任务,看门狗将触发超时。

任务看门狗工作原理图

任务看门狗工作原理图

监控任务管理

除了默认的 IDLE 任务监控外,还可以添加其他任务作为监控对象:

  • 使用 esp_task_wdt_add(NULL) 将当前任务添加到监控列表

  • 被监控的任务必须周期性调用 esp_task_wdt_reset() 进行喂狗操作

  • 系统要求所有被监控任务都完成喂狗后才会重置看门狗定时器

配置选项

任务看门狗相关配置位于 Component config ESP System Settings

  • Initialize Task Watchdog Timer on startup:启用或禁用任务看门狗

  • Invoke panic handler on Task Watchdog timeout:设置触发看门狗时是否重启系统

  • Task Watchdog timeout period (seconds):任务看门狗超时时间,默认为 5 秒

  • Watch CPU0 Idle Task:监控 CPU0 的 IDLE 任务(双核系统可选择是否监控 CPU1)

问题诊断

基础诊断

任务看门狗触发时,系统会打印相关信息,包括:

  • 当前正在运行的任务

  • 相关的调用栈信息

高级诊断

对于复杂应用,可使用 vTaskGetRunTimeStats() 接口分析 CPU 使用情况:

  1. 启用相关配置:

    • Component config -> FreeRTOS -> Enable FreeRTOS trace facility

    • Component config -> FreeRTOS -> Enable FreeRTOS stats formatting functions

    • Component config -> FreeRTOS -> Enable FreeRTOS to collect run time stats

  2. 循环调用统计接口:

    void run_time_monitor(void)
    {
        char *pbuffer = (char *)calloc(1, 2048);
        printf("----------------------------------------------\r\n");
        vTaskGetRunTimeStats(pbuffer);
        printf("%s", pbuffer);
        printf("----------------------------------------------\r\n");
        free(pbuffer);
    }
    

备注

使用 vTaskGetRunTimeStats 接口时需要关闭 Invoke panic handler on Task Watchdog timeout 配置,否则系统会因看门狗触发而重启,无法打印统计信息。

解决方案

代码优化方案

  1. 对于确实存在问题的任务

    • 在循环中使用带超时的阻塞接口,如 xSemaphoreTake() (超时时间不能为 0)

    • 使用 vTaskDelay() 主动让出 CPU 资源

    • 优化算法,减少单次执行时间

  2. 对于正常的高负载任务(不希望降低运行效率)

    • 在高负载任务中手动添加喂狗操作

手动喂狗操作

对于需要长时间运行的合理任务,可采用手动喂狗方式:

static void long_running_task(void* arg)
{
    // 移除 IDLE 任务的看门狗监控
    esp_task_wdt_delete(xTaskGetIdleTaskHandleForCPU(0));
    // 将当前任务添加到监控列表
    esp_task_wdt_add(NULL);

    while (1) {
        // 执行耗时操作

        // 定期喂狗
        esp_task_wdt_reset();
    }

    // 任务结束时恢复 IDLE 任务监控
    esp_task_wdt_delete(NULL);
    esp_task_wdt_add(xTaskGetIdleTaskHandleForCPU(0));
}

警告

手动喂狗操作较为复杂,因为 ESP-IDF 要求所有被监控任务都完成喂狗后才会重置看门狗定时器。不建议随意修改 ESP-IDF 内部实现。

TimerGroup 看门狗

常见 LOG:

1. rst:0x8 (TG1WDT_SYS_RST),boot:0xc (SPI_FAST_FLASH_BOOT)
2. rst:0x7 (TG0WDT_SYS_RST),boot:0xc (SPI_FAST_FLASH_BOOT)

触发条件:

  • 任务中断和任务调度无法正常触发,可以认为 CPU 运行存在异常

可能原因:

  • CPU 取指出现异常,此时要怀疑 Flash 或 PSRAM 通信是否有硬件干扰,比如异常占用了 Flash 或 PSRAM 引脚,高频干扰等

  • 供电不稳定

  • 可能的野指针问题

RTC 看门狗

常见 LOG:

rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)

特点:

  • 除复位数字外设外,也会复位 RTC 模块

  • 可在程序中调用

能触发 RTC 看门狗的阶段:

  • Boot 阶段:需要通过 CONFIG_BOOTLOADER_WDT_ENABLE 选项来在此阶段使能此看门狗

  • Restart 阶段

  • Panic 阶段

  • 进入 light sleep 阶段,进入 light sleep 后硬件会自动关闭 RTC 看门狗

其它看门狗

外部硬件看门狗

用户如存在以下异常场景:

  • 硬件存在不稳定供电

  • 偶尔出现芯片 hang 住没有触发任何复位

此时建议外接一个硬件看门狗,可通过软件程序定期翻转 GPIO 实现喂狗操作,来避免 ESP 芯片一直 hang 住的情况。