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


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

created: 2026-07-15

看门狗IWDG与WWDG

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


为什么要看门狗

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


独立看门狗 IWDG

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

IWDG 框图

LSI(~40KHz) → 8位预分频器 → 12位递减计数器(RL[11:0]) → 复位
                                   ↑
                              键寄存器(KR)写 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=4095 超时 分辨率
000 4 0.41s 0.1ms
001 8 0.82s 0.2ms
010 16 1.64s 0.4ms
011 32 3.28s 0.8ms
100 64 6.55s 1.6ms
101 128 13.11s 3.2ms
110 256 26.21s 6.4ms
111 256 26.21s 6.4ms

实验配置:PR=4(分频64), RLR=2499

Tout = 2500 × 64 × 4 / 40000 = 16s

实验 1:寄存器版 IWDG

项目路径stm32/27_iwdg_test_register

文件:stm32/27_iwdg_test_register/Hardware/IWDG/iwdg.h

#ifndef __IWDG_H
#define __IWDG_H

#include "stm32f10x.h"

void IWDG_Init(void);
void IWDG_Refresh(void);     // 喂狗

#endif

文件:stm32/27_iwdg_test_register/Hardware/IWDG/iwdg.c(实际代码:先启动,再配置,最后喂狗)

#include "iwdg.h"

void IWDG_Init(void)
{
    IWDG->KR = 0xCCCC;       // 启动看门狗
    IWDG->KR = 0x5555;       // 解除寄存器保护
    IWDG->PR = 4;            // 预分频 64
    IWDG->RLR = 2499;        // 重装载值 2499 → 超时约 16s
    IWDG_Refresh();           // 重新加载计数值
}

void IWDG_Refresh(void)
{
    IWDG->KR = 0xAAAA;       // 喂狗
}

文件:stm32/27_iwdg_test_register/User/main.c

#include "usart.h"
#include "delay.h"
#include "key.h"
#include "iwdg.h"

uint8_t flag;

int main(void)
{
    USART_Init();
    Key_Init();
    IWDG_Init();

    printf("尚硅谷独立看门狗实验...\n");

    while (1)
    {
        printf("正常执行程序...\n");
        Delay_ms(3000);         // 模拟主循环 3s 耗时

        if (flag)               // 按键中断中设置的标志位
        {
            Delay_ms(3000);     // 模拟额外耗时 3s(累计 6s,超时 16s 以内,不会复位)
            flag = 0;
        }

        IWDG_Refresh();         // 喂狗
        printf("程序执行结束,喂狗成功!\n");
    }
}

实验 2:HAL 库版 IWDG

项目路径stm32/28_iwdg_test_hal

文件:stm32/28_iwdg_test_hal/Core/Src/main.c

#include "main.h"
#include "iwdg.h"
#include "usart.h"
#include "gpio.h"

uint8_t flag;

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_IWDG_Init();          // CubeMX 生成的 IWDG 初始化
    MX_USART1_UART_Init();

    printf("尚硅谷独立看门狗实验...\n");

    while (1)
    {
        printf("正常执行程序...\n");
        HAL_Delay(3000);

        if (flag)
        {
            HAL_Delay(3000);
            flag = 0;
        }

        HAL_IWDG_Refresh(&hiwdg);    // HAL 库喂狗
        printf("程序执行结束,喂狗成功!\n");
    }
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    flag = 1;
    printf("按键按下...\n");
}

HAL 库 SystemClock_Config 需使能 LSI:

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI | RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.LSIState = RCC_LSI_ON;

窗口看门狗 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 配置示例(寄存器版)

void WWDG_Init(void)
{
    RCC->APB1ENR |= RCC_APB1ENR_WWDGEN;

    WWDG->CFR |= WWDG_CFR_WDGTB_1 | WWDG_CFR_WDGTB_0;  // WDGTB=11: 分频 8
    WWDG->CFR &= ~WWDG_CFR_W;
    WWDG->CFR |= (0x50 << 0);                            // 窗口值 0x50

    WWDG->CR = 0x7F;                                     // 计数器初始值 127
    WWDG->CR |= WWDG_CR_WDGA;                            // 使能 WWDG
}

void WWDG_Feed(void)
{
    WWDG->CR = 0x7F;      // 重装计数器
}

void WWDG_IRQHandler(void)
{
    if (WWDG->SR & WWDG_SR_EWIF)
    {
        // 距离复位还有 0x40 个计数周期(约 284μs @ 36MHz/8)
        WWDG->SR = 0x00;
    }
}

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_WWDG_Refresh(&hwwdg);                     // 喂狗

void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
{
    // 距离复位还剩 64 个计数周期
}

IWDG vs WWDG 对比

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

核心速查表

IWDG 操作 寄存器 HAL 库
启动 KR = 0xCCCC HAL_IWDG_Init()
配置 PR/RLR KR = 0x5555 通过 Init 结构体
喂狗 KR = 0xAAAA HAL_IWDG_Refresh()
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:在 Debug → Settings → Debug 选项卡中配置 DBGMCU 寄存器