Watchdog在C语言中如何实现与配置?

99ANYc3cd6
预计阅读时长 16 分钟
位置: 首页 C语言 正文

什么是 Watchdog(看门狗)?

Watchdog,直译为“看门狗”,是一种硬件或软件计时器,其主要作用是监控系统程序的运行状态

您可以把它想象成一个脾气暴躁的保安:

  1. 启动计时器:你启动一个倒计时器(10 秒)。
  2. “喂狗” (Kick the Dog / Pet the Dog):在倒计时结束前,你的程序必须定期执行一个特定的操作(比如向寄存器写入一个特定值),这被称为“喂狗”,这个动作告诉保安:“我还在正常运行,请重置你的计时器。”
  3. 超时处理:如果你的程序因为死循环、死锁、硬件故障等原因,没有在规定时间内“喂狗”,那么计时器归零,看门狗会认为程序已经“失控”。
  4. 复位系统:一旦看门狗超时,它会强制执行一个系统复位,让整个系统重新启动,目的是从异常状态中恢复过来,保证系统的长期稳定运行。

核心思想: 通过一个外部、独立的机制来检测主程序是否“活”着,防止系统永久卡死。


为什么需要 Watchdog?

在嵌入式系统中,程序可能会遇到各种意外情况:

  • 意外的无限循环:代码逻辑错误,导致某个 forwhile 循环永远出不来。
  • 死锁:多个线程/进程互相等待,导致谁也无法继续执行。
  • 硬件故障:某个关键外设(如内存、总线)出现问题,程序卡住。
  • 软件缺陷:未处理的异常中断,导致程序流程混乱。

在这些情况下,如果没有看门狗,系统就会彻底“死机”,需要人工断电重启,这在很多场景下是不可接受的(例如远程设备、服务器、汽车控制系统等),看门狗提供了一个自动恢复机制,大大提高了系统的可靠性和鲁棒性


Watchdog 的工作原理(硬件层面)

看门狗是一个独立的硬件电路,集成在微控制器或处理器的内部,它的工作流程如下:

  1. 使能:在程序初始化时,需要先使能看门狗模块。
  2. 配置:设置一个超时周期,这个时间长度需要根据应用程序的执行周期来合理设定,如果你的主循环每 100ms 执行一次,那么超时时间可以设置为 500ms 或 1s,留出足够的余量。
  3. 启动/重载:启动看门狗计时器,同时开始第一次“喂狗”操作。
  4. 循环喂狗:在程序的主循环或任务调度中,周期性地执行“喂狗”指令(通常是向一个特定的寄存器写入一个“魔法数”或执行一次清零操作),每次喂狗都会重置计时器,让它重新开始倒计时。
  5. 超时复位:如果因为某种原因,程序没有在超时周期内喂狗,计时器溢出,看门狗会立即拉低一个复位引脚,使整个 MCU/芯片复位,程序从头开始运行。

C 语言实现 Watchdog 的示例

下面我们以一个具体的、常见的 MCU——STM32(使用 STM32 HAL 库)为例,展示如何在 C 语言中配置和使用硬件看门狗。

1 硬件看门狗配置

main.c 文件中,我们首先初始化看门狗。

#include "main.h"
#include "stm32f4xx_hal.h" // 根据你的MCU型号修改
// 声明一个看门狗句柄
IWDG_HandleTypeDef hiwdg;
// 初始化独立看门狗
void MX_IWDG_Init(void)
{
    // 1. 设置看门狗初始化结构体
    hiwdg.Instance = IWDG;
    hiwdg.Init.Prescaler = IWDG_PRESCALER_256; // 预分频器,决定计数器时钟频率
    hiwdg.Init.Reload = 0x0FFF;              // 重载值,即超时计数器的值
    hiwdg.Init.Window = 0xFFF;               // 窗口值,用于限制喂狗时间窗口
    // 2. 初始化IWDG
    if (HAL_IWDG_Init(&hiwdg) != HAL_OK)
    {
        // 初始化错误处理
        Error_Handler();
    }
}

参数解释:

  • Prescaler (预分频器):IWDG 的时钟来自内部低速时钟,通常为 32kHz,预分频器会降低这个频率。IWDG_PRESCALER_256 表示时钟频率为 32kHz / 256 = 125Hz。
  • Reload (重载值):这是超时周期的核心,计时器从 Reload 值开始倒数,每次减 1,减到 0 就超时。
    • 超时时间 = (Reload + 1) / 预分频后的时钟频率
    • 在我们的例子中:超时时间 = (0x0FFF + 1) / 125Hz = 4096 / 125 = 768 秒
  • Window (窗口值):这是一个高级功能,如果设置了窗口值,你只能在计数器小于 Window 值的时候喂狗,如果计数器已经小于 Window,此时再喂狗就会导致复位,这可以防止在程序快要超时时才“喂狗”的作弊行为,如果不需要,可以设置为最大值 0xFFF

2 喂狗操作

喂狗操作非常简单,就是调用一个函数。

// 喂狗函数
void Feed_Watchdog(void)
{
    HAL_IWDG_Refresh(&hiwdg); // 刷新/重载IWDG计数器
}

3 在主循环中使用看门狗

我们将在 main 函数中使用它。

int main(void)
{
    // ... 系统时钟等初始化 ...
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_IWDG_Init(); // 初始化看门狗
    uint32_t last_tick = HAL_GetTick();
    while (1)
    {
        uint32_t current_tick = HAL_GetTick();
        // 模拟主循环任务
        if (current_tick - last_tick >= 1000) // 每1秒执行一次任务
        {
            last_tick = current_tick;
            HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // 翻转LED,表示系统在运行
            // 这里可以放你的主要业务逻辑
        }
        // 模拟一个致命错误(取消下面这行注释来测试看门狗复位)
        // while(1); 
        // 在主循环中周期性地喂狗
        // 喂狗的频率必须小于我们设置的超时时间(32.768秒)
        // 这里我们每500ms喂一次狗,留出足够的安全余量
        if (current_tick - last_feed_tick >= 500)
        {
            Feed_Watchdog();
            last_feed_tick = current_tick;
        }
    }
}

代码分析:

  1. 我们在 main 开始时调用了 MX_IWDG_Init() 启动了看门狗。
  2. while(1) 主循环中,我们有一个模拟的业务逻辑(每秒翻转一次 LED)。
  3. 我们还有一个 Feed_Watchdog() 的调用,每 500ms 执行一次。
  4. 测试:如果你取消 while(1); 的注释,程序会卡死,无法再执行 Feed_Watchdog(),大约 32.768 秒后,你会看到 STM32 板子上的 LED 灯突然熄灭然后重新亮起,这就是看门狗复位了系统。

软件看门狗

除了硬件看门狗,还可以用纯软件的方式实现一个看门狗,通常被称为 “喂狗任务” (Feeding Task)

工作原理:

  1. 创建一个高优先级的任务(线程)。
  2. 这个任务的核心逻辑就是一个 while(1) 循环,循环内部有一个 sleep()delay(),周期比看门狗的超时时间短。
  3. 这个任务在启动时,会“喂”一次硬件看门狗。
  4. 主程序在完成关键操作后,也需要“喂”一次硬件看门狗,并且释放一个信号量或设置一个标志位
  5. 软件看门狗任务在每次被唤醒时,会检查这个信号量或标志位。
    • 如果收到了信号,说明主程序正常,它就再次“喂”硬件看门狗,然后继续休眠。
    • 如果没有收到信号,说明主程序可能已经卡死,软件看门狗任务可以立即触发一个硬件看门狗喂狗(或者直接调用复位函数),或者采取其他恢复措施。

优点:

  • 更加灵活,可以根据不同任务的状态进行更精细的监控。

缺点:

  • 本身也是软件的一部分,如果系统栈溢出或内核崩溃,软件看门狗也会失效。硬件看门狗是最后一道防线,更可靠。

特性 硬件看门狗 软件看门狗
实现方式 独立于CPU的硬件电路 一个独立的任务/线程
可靠性 极高,独立于CPU,即使系统崩溃也能工作 较高,但依赖于操作系统和CPU的正常运行
触发方式 超时后直接拉低复位引脚 通过软件逻辑判断,再触发复位或喂狗
灵活性 较低,功能固定 极高,可以设计复杂的监控逻辑
适用场景 所有嵌入式系统,尤其是关键任务系统 复杂的实时操作系统,需要分层监控的场景

核心要点:

  • Watchdog 是一种故障恢复机制,不是故障预防机制。
  • 硬件看门狗是嵌入式系统的“标配”和“最后一道防线”。
  • 合理设置超时时间喂狗周期至关重要,喂狗周期必须小于超时时间,并且要留有足够的余量,以应对程序正常执行时可能出现的短暂延迟。
  • “喂狗”操作本身必须简单、快速、可靠,不能包含复杂的逻辑,否则它自己也可能成为卡点。
-- 展开阅读全文 --
头像
织梦虚拟主机幻灯组件如何使用?
« 上一篇 02-28
C语言如何处理日期时间?
下一篇 » 02-28

相关文章

取消
微信二维码
支付宝二维码

目录[+]