20-看门狗IWDG与WWDG.md 8.1 KB


tags: [source-summary] type: source source: "尚硅谷嵌入式技术之STM32单片机(扩展篇)V1.0.0 — 看门狗章节 + 配套代码27~28" author: "尚硅谷研究院" date: 2026-07-15

created: 2026-07-15

看门狗IWDG与WWDG

用生活理解:看门狗就像社区里的保安——你得定时去保安亭刷卡(喂狗),证明你还在正常工作。如果你一直没来刷卡(程序跑飞/卡死),保安就拉响警报(系统复位)。IWDG 是独立供电的保安(不怕停电),WWDG 是严格规定只能在特定时间窗口内刷卡的保安。


为什么要看门狗

嵌入式系统运行中可能因电磁干扰、电源波动、软件 bug 等原因导致程序跑飞或死循环。看门狗可以在程序异常时自动复位系统,保证设备不会"死机"。


独立看门狗 IWDG

IWDG(Independent Watchdog)使用独立的 LSI 时钟(~40KHz),与主时钟独立——即使主时钟停振,IWDG 仍然工作。

IWDG 框图

LSI(~40KHz) → 8位预分频器 → 12位递减计数器(RL[11:0]) → 复位
                                   ↑
                              键寄存器(KRW)写 0xAAAA 重装

IWDG 寄存器

寄存器 功能
KR (键寄存器) 写 0xCCCC 启动、写 0xAAAA 喂狗、写 0x5555 允许写 PR/RLR
PR (预分频器) 分频系数(3 位)
RLR (重装载寄存器) 12 位重装值(0~4095)

IWDG 超时时间计算

Tout = (RLR + 1) × 4 × 2^PR / LSI
LSI ≈ 40KHz

常见超时时间表(LSI=40KHz)

PR[2:0] 分频系数 RLR=0xFFF(4095) RLR=0xFFF(4095)+计算 分辨率
000 4 4096×4/40K = 0.41s 0.1ms
001 8 4096×8/40K = 0.82s 0.2ms
010 16 4096×16/40K = 1.64s 0.4ms
011 32 4096×32/40K = 3.28s 0.8ms
100 64 4096×64/40K = 6.55s 1.6ms
101 128 4096×128/40K = 13.11s 3.2ms
110 256 4096×256/40K = 26.21s 6.4ms
111 256 4096×256/40K = 26.21s 6.4ms

示例:PR=011(分频32), RLR=1000

Tout = 1001 × 32 × 4 / 40000 = 3.2s

IWDG 配置示例

void IWDG_Init(void)
{
    // 1. 写 0x5555 到 KR,允许修改 PR 和 RLR
    IWDG->KR = 0x5555;

    // 2. 设置预分频系数 = 32 (PR=011)
    IWDG->PR = 3;        // PR[2:0]=011

    // 3. 设置重装值 = 1000 → Tout ≈ 3.2s (40KHz)
    IWDG->RLR = 1000;

    // 4. 写 0xCCCC 启动 IWDG
    IWDG->KR = 0xCCCC;

    // 5. 喂狗(必须在 3.2s 内执行一次,否则复位)
    // 通常在 main 循环中调用
}

void IWDG_Feed(void)
{
    IWDG->KR = 0xAAAA;   // 写 0xAAAA 重装计数器
}

int main(void)
{
    IWDG_Init();          // 启动看门狗

    while (1)
    {
        // 主循环逻辑...

        IWDG_Feed();      // 每 3s 内喂一次狗(Tout=3.2s)

        // 如果程序卡死在此循环后面,3.2s 后系统复位
    }
}

注意:IWDG 一旦启动无法停止,只能通过系统复位关闭。


窗口看门狗 WWDG

WWDG(Window Watchdog)更加灵活——它要求必须在特定的时间窗口内喂狗:

特性 说明
时钟源 APB1(36MHz),不是独立时钟
计数器 7 位递减(T[6:0],最大值 127)
窗口上限 由 CFR 寄存器 W[6:0] 设定
喂狗时机 必须在 CNT > W[6:0] 且 CNT ≠ 0x3F 时喂狗
早期唤醒中断 (EWI) CNT=0x40 时产生中断(可提前预警)

WWDG 工作时序

                   窗口区(允许喂狗)          禁止区
CNT:  127 ← ... ← [W+1] ← ... ← 0x40 ← 0x3F → 复位
                    ↑                  ↑
              最早喂狗时机         最晚喂狗时机
  • 喂狗太早(CNT > W+1):复位
  • 喂狗太晚(CNT < 0x40/H):复位
  • 必须在 W < CNT ≤ 0x7F 范围内喂狗 → 正常

WWDG 超时计算

Tout = (CNT - W) × (PSC + 1) / APB1

WWDG 的最小喂狗间隔和最大喂狗间隔由 PSC 预分频系数和窗口值决定。

WWDG 配置示例

void WWDG_Init(void)
{
    // 1. 开启 WWDG 时钟
    RCC->APB1ENR |= RCC_APB1ENR_WWDGEN;

    // 2. 设置预分频系数 = 8 (APB1=36MHz → CK=4.5MHz)
    WWDG->CFR |= WWDG_CFR_WDGTB_1 | WWDG_CFR_WDGTB_0;  // WDGTB[1:0]=11: /8

    // 3. 设置窗口值 = 0x50(CNT 必须 < 0x50 才能喂狗,即最早喂狗点)
    WWDG->CFR &= ~WWDG_CFR_W;            // 清除窗口位域
    WWDG->CFR |= (0x50 << 0);

    // 4. 设置计数器初始值 = 0x7F(最大值)
    WWDG->CR = 0x7F;

    // 5. 使能窗口看门狗(WDGA=1)
    WWDG->CR |= WWDG_CR_WDGA;
}

void WWDG_Feed(void)
{
    // 必须在窗口内喂狗(CNT < W + 1)
    WWDG->CR = 0x7F;      // 重装计数器(必须在主循环写入此值)
}

void WWDG_IRQHandler(void)
{
    if (WWDG->SR & WWDG_SR_EWIF)         // 早期唤醒中断
    {
        // 距离复位还有 0x40 个计数周期,赶紧处理紧急事务
        // 注意:在 ISR 中不能喂狗!
        WWDG->SR = 0x00;                  // 清标志
    }
}

IWDG vs WWDG 对比

对比项 IWDG WWDG
时钟源 LSI (~40KHz) 独立 APB1(依赖主时钟)
计数器 12 位递减 7 位递减
超时范围 ~0.1ms ~ 26s ~0.1ms ~ 若干 ms
喂狗窗口 无窗口(任意时间喂即可) 有窗口:太早太晚都复位
可停止 ❌ 启动后不可停 ✅ 可通过软件停止
中断 ❌ 无 ✅ 有 EWI 早期唤醒中断
独立供电 ✅ 完全独立 ❌ 依赖主电源
选型建议 需要高可靠性、长时间定时的系统 需要精确检测程序执行时序的系统


HAL 库版看门狗

HAL IWDG

// HAL 库使用 IWDG_HandleTypeDef
IWDG_HandleTypeDef hiwdg;

hiwdg.Instance = IWDG;
hiwdg.Init.Prescaler = IWDG_PRESCALER_32;    // PR=011: 分频32
hiwdg.Init.Reload = 1000;                     // RLR=1000
HAL_IWDG_Init(&hiwdg);                        // 相当于 KR=0xCCCC 启动

// HAL 库喂狗
HAL_IWDG_Refresh(&hiwdg);                     // 相当于 KR=0xAAAA

HAL WWDG

WWDG_HandleTypeDef hwwdg;

hwwdg.Instance = WWDG;
hwwdg.Init.Prescaler = WWDG_PRESCALER_8;     // WDGTB=11
hwwdg.Init.Window = 0x50;                     // 窗口值
hwwdg.Init.Counter = 0x7F;                    // 计数值初始值
hwwdg.Init.EWIMode = WWDG_EWI_ENABLE;         // 使能早期唤醒中断
HAL_WWDG_Init(&hwwdg);

// HAL 库喂狗(就在主循环更新 CR 值)
HAL_WWDG_Refresh(&hwwdg);                     // 相当于 CR=0x7F

// HAL WWDG 早期唤醒中断回调
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
{
    // 距离 WWDG 复位还剩 64 个计数周期,执行紧急备份
}

核心速查表

IWDG 操作 寄存器 HAL 库
启动 KR = 0xCCCC HAL_IWDG_Init()
喂狗 KR = 0xAAAA HAL_IWDG_Refresh()
配置 KR = 0x5555 通过 Init 结构体
WWDG 操作 寄存器 HAL 库
启动 `CR = WDGA`
喂狗 CR = 0x7F HAL_WWDG_Refresh()
配窗口 CFR.W = value Init.Window = value
EWI 回调 手动中断函数 HAL_WWDG_EarlyWakeupCallback()

常见问题与避坑

  1. IWDG 一直在复位 → main 循环中忘了喂狗、喂狗间隔超过超时时间
  2. IWDG 启动后无法调试 → IWDG 一旦启动无法暂停,调试时需在断点前禁用 IWDG(Keil 中配置 DBGMCU)
  3. WWDG 喂狗太早也复位 → 检查窗口值配置(W 值必须小于 CNT 初始值)
  4. WWDG 应用场景:适合检测程序的执行时序是否正常(太早或太晚说明程序逻辑异常)
  5. LSI 频率不准 → LSI 约 30~60KHz(典型 40KHz),实际超时时间 ±50%,重要场合需实测
  6. Keil 调试关闭 IWDG/WWDG:在 Debug → Settings → Debug 选项卡中勾选 "Reset and Halt" 或配置 DBGMCU 寄存器