14-WDG看门狗.md 11 KB


type: raw source: bilibili lecture: "14-1 WDG看门狗, 14-2 独立看门狗 & 窗口看门狗" lecture_material: "14-1 WDG看门狗, 14-2 独立看门狗&窗口看门狗" project_code: "14-1 独立看门狗, 14-2 窗口看门狗"

tags: [stm32, watchdog, iwdg, wwdg, reset]

STM32-WDG 看门狗(IWDG & WWDG)

1 看门狗概念

看门狗(Watchdog)是一种硬件定时器,当程序卡死跑飞时,自动复位 MCU。

工作原理:程序正常运行时周期性喂狗(重装计数器);若超时未被喂狗,定时器溢出 → 产生系统复位。

教师讲解:看门狗就像一个"监工"——你必须在规定时间内告诉他"我还活着"。如果你没来得及说,他就认为你死了,直接重启整个系统。在工业现场——电机控制、PLC、汽车电子——这是必备的安全机制。

1.1 为什么需要看门狗?

  • 电磁干扰导致 PC 指针跑飞
  • 无限循环/死锁
  • 外设响应超时
  • 电池耗尽等异常

如果不看门狗,设备会"僵死"在现场,需要人工断电重启。

2 独立看门狗(IWDG)

IWDG 模块框图 上图:独立看门狗 IWDG 模块框图(来源:STM32入门教程 PPT 第188页)

2.1 IWDG 特性

参数
时钟源 LSI(内部低速 RC,约 40kHz)
计数器 12-bit 递减计数器
预分频 4 / 8 / 16 / 32 / 64 / 128 / 256
超时范围 ~0.1ms ~ ~26.2s(配置决定)
模式 只能复位
工作状态 待机/停止模式下仍持续计数
寄存器写保护 需要写入访问使能

2.2 超时计算公式

$$T{out} = \frac{PR scaler \times (RLR + 1)}{f{LSI}}$$

  • $f_{LSI}$ ≈ 40kHz(实际范围 30~60kHz)
  • $Prescaler$:4/8/16/32/64/128/256
  • $RLR$:重装值(0~4095)

示例Prescaler = 16RLR = 2499

$$T_{out} = \frac{16 \times (2499 + 1)}{40000} = 1.0s$$

教师强调:预分频越大的超时范围越大;但 LSI 本身精度不高(漂移 ±50%),所以实际超时时间需要留够余量。

2.3 常用超时配置表

预分频 RLR 理论超时(LSI=40kHz)
16 2499 1.0 s
64 2499 4.0 s
128 4095 13.1 s
256 4095 26.2 s

2.4 IWDG 配置流程(标准库)

  1. 写使能IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
  2. 设置预分频IWDG_SetPrescaler(IWDG_Prescaler_16);
  3. 设置重装值IWDG_SetReload(2499);
  4. 喂狗IWDG_ReloadCounter();
  5. 使能IWDG_Enable();

⚠ 使能后无法关闭,只能由复位停止。

2.5 标准库代码分析(14-1 独立看门狗)

int main(void)
{
    OLED_Init();
    Key_Init();

    // 判断复位来源
    if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST) == SET)
    {
        OLED_ShowString(2, 1, "IWDGRST");
        RCC_ClearFlag();
    }
    else
    {
        OLED_ShowString(3, 1, "RST");
    }

    // IWDG 配置:超时 1 秒
    IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
    IWDG_SetPrescaler(IWDG_Prescaler_16);   // 预分频 16
    IWDG_SetReload(2499);                    // RLR = 2499
    IWDG_ReloadCounter();                  // 首次喂狗
    IWDG_Enable();                          // 使能 → cannot stop

    while (1)
    {
        Key_GetNum();  // 阻塞式按键扫描(模拟主循环卡死时,按下按键不放会卡住)

        IWDG_ReloadCounter();  // 喂狗
        OLED_ShowString(4, 1, "FEED");
        Delay_ms(200);
        OLED_ShowString(4, 1, "    ");
        Delay_ms(600);  // 喂狗间隔 800ms < 1000ms → 不会复位
    }
}

2.6 IWDG HAL 库实现

IWDG_HandleTypeDef hiwdg;

void IWDG_Init_HAL(void)
{
    hiwdg.Instance       = IWDG;
    hiwdg.Init.Prescaler = IWDG_PRESCALER_64;   // 40kHz / 64 = 625Hz
    hiwdg.Init.Reload    = 625;                  // 625 / 625Hz ≈ 1s 超时
    HAL_IWDG_Init(&hiwdg);
}

// 喂狗
// HAL_IWDG_Refresh(&hiwdg);

// 复位来源判断(HAL 中通过 RCC 寄存器读取)
if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST) != RESET)
{
    __HAL_RCC_CLEAR_RESET_FLAGS();
}

实验现象:正常运行时 "FEED" 每秒闪烁一次;按住按键不放(卡死 2s)→ 系统超时复位 → OLED 显示 "IWDGRST"。

判定复位来源

  • RCC_GetFlagStatus(RCC_FLAG_IWDGRST) → IWDG 复位
  • RCC_GetFlagStatus(RCC_FLAG_WWDGRST) → WWDG 复位
  • RCC_ClearFlag() → 清除所有复位标志

2.7 IWDG 注意事项

  • 使能后无法关闭(由硬件决定)
  • LSI 精度差(30~60kHz),超时时间要留 50% 余量
  • 喂狗必须在软件的最顶层循环中做——不能在中断里喂狗(否则主循环卡死但中断还在跑,看门狗失效)

教师特别提醒:有些同学把喂狗放在 SysTick 中断里,这是致命的!如果主循环死锁但中断仍然正常触发,看门狗永远不会复位——掩盖了真正的故障。

3 窗口看门狗(WWDG)

WWDG 模块框图 上图:窗口看门狗 WWDG 模块框图(来源:STM32入门教程 PPT 第191页)

3.1 WWDG 特性

参数
时钟源 PCLK1(APB1 时钟,典型 36MHz)
计数器 7-bit 递减计数器(CNT,T[6:0])
喂狗窗口 必须在上窗口和下窗口之间喂狗
预分频 1 / 2 / 4 / 8
超时范围 ~0.05ms ~ ~36.86ms(PCLK1=36MHz)
复位方式 复位 或 提前唤醒中断(EWI)
工作模式 停止模式下停止计数

3.2 窗口机制

     T[6:0] 寄存器
         |
         v
  ┌───────────────┐
  │  7F  (127)   │  ← 上窗口(启动值)
  │     ↓        │
  │  窗口值       │  ← 必须在此之后喂狗
  │     ↓        │
  │  3F  (63)   │  ← 下窗口(固定为 0x40)
  │     ↓        │
  │      0        │  ← 计数器减到 0x3F 时复位
  └───────────────┘
  • 必须在窗口值 ≤ CNT ≤ 0x7F 之间喂狗
  • CNT 减到 0x3F(63 = 下窗口)之前未喂狗 → 复位。
  • CNT 大于窗口值时喂狗 → 复位。
  • 窗口值 越小,喂狗时间窗口越窄

教师讲解:窗口看门狗比独立看门狗更"严苛"——不仅不能太晚喂狗,还不能太早喂狗。这可以检测"程序跑飞但碰巧喂了狗"的情况。比如代码在错误分支碰巧执行了喂狗指令,如果在窗口之外,照样触发复位。

3.3 超时计算公式

$$T{out} = \frac{Prescaler \times (CNT{init} - 0x3F)}{f_{PCLK1}}$$

示例:PCLK1 = 36MHz,Prescaler = 8,CNT = 0x54(=84)

$$T_{out} = \frac{8 \times (84 - 63)}{36 \times 10^6} \approx 4.67\mu s$$

3.4 WWDG 配置流程(标准库)

  1. 开启时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
  2. 设置预分频WWDG_SetPrescaler(WWDG_Prescaler_8);
  3. 设置窗口值WWDG_SetWindowValue(0x40 | 21);(窗口值 = 21 + 63 = 84)
  4. 使能并初始喂狗WWDG_Enable(0x40 | 54);(CNT = 54 + 63 = 117)
  5. 喂狗WWDG_SetCounter(0x40 | 54);

⚠ WWDG 的寄存器和计数值使用 0x40 | xx 组合:0x40=64 是下窗口,加上 63 后才等于 127。

3.5 标准库代码分析(14-2 窗口看门狗)

int main(void)
{
    OLED_Init();
    Key_Init();

    // 判断复位来源
    if (RCC_GetFlagStatus(RCC_FLAG_WWDGRST) == SET)
    {
        OLED_ShowString(2, 1, "WWDGRST");
        RCC_ClearFlag();
    }
    else
    {
        OLED_ShowString(3, 1, "RST");
    }

    // WWDG 初始化
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);

    WWDG_SetPrescaler(WWDG_Prescaler_8);      // 预分频 8
    WWDG_SetWindowValue(0x40 | 21);          // 窗口值 84
    WWDG_Enable(0x40 | 54);                   // CNT = 117,超时约 12us

    while (1)
    {
        Key_GetNum();  // 阻塞式按键扫描

        OLED_ShowString(4, 1, "FEED");
        Delay_ms(20);
        OLED_ShowString(4, 1, "    ");
        Delay_ms(20);

        WWDG_SetCounter(0x40 | 54);  // 喂狗
    }
}

喂狗间隔 40ms。窗口值 84 ÷ 超时时间约 50us → 必须在 30~50us 内喂狗,否则复位。这就是 WWDG 的"窗口"约束。

3.6 WWDG HAL 库实现

WWDG_HandleTypeDef hwwdg;

void WWDG_Init_HAL(void)
{
    hwwdg.Instance     = WWDG;
    hwwdg.Init.Prescaler = WWDG_PRESCALER_8;
    hwwdg.Init.Window    = 0x50;             // 上窗口值
    hwwdg.Init.Counter   = 0x7F;             // 计数器初始值
    hwwdg.Init.EWIMode   = WWDG_EWI_ENABLE;  // 使能提前唤醒中断
    HAL_WWDG_Init(&hwwdg);
}

// 喂狗
// HAL_WWDG_Refresh(&hwwdg);

// 提前唤醒中断回调
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
{
    // 复位前保存关键数据
    Store_Save();
}

3.7 提前唤醒中断(EWI)

EWI 可在计数器到 0x40 时产生中断,在复位前执行抢救操作(如保存数据、记录日志)。

void WWDG_IRQHandler(void)
{
    if (WWDG_GetFlagStatus())
    {
        // 在复位前保存关键数据
        Store_Save();
        WWDG_ClearFlag();
    }
}

3.8 WWDG 与 IWDG 对比

对比项 IWDG WWDG
时钟 LSI 40kHz(专用) PCLK1(与系统时钟共享)
计数器宽度 12-bit 7-bit
超时范围 0.1ms ~ 26.2s 0.05ms ~ 36.86ms
喂狗条件 仅不能超时 窗口范围内 + 不超时
停止模式下 持续计数 停止计数
调试模式下 默认停止(可用 DBGMCU 配置) 默认停止(可用 DBGMCU 配置)
主要用途 系统级大周期保护 代码执行时间精确监控

教师总结:IWDG 适合"慢速保护"——主循环 1 秒内一定要跑完一圈;WWDG 适合"快速精准保护"——某个操作必须在 1 毫秒内完成,否则异常。实际项目中,两个看门狗可以同时使用(IWDG 兜底大循环、WWDG 监控关键任务)。

3.9 应用建议

场景 推荐 原因
工业电机控制 IWDG + WWDG IWDG 保护主循环,WWDG 保护 FOC 算环境
电池供电传感器 IWDG 超时可设置较长(防止低频唤醒后未喂狗)
通信网关 WWDG 对数据包处理时间有严格限制
汽车 ECU 两者同时使用 ISO 26262 功能安全有要求

⚠ 调试时:建议暂停调试器时关闭看门狗或延长超时,避免每次断点暂停都触发复位。