tags: [source-summary] type: source source: "尚硅谷嵌入式技术之STM32单片机(扩展篇)V1.0.0 — 看门狗章节 + 配套代码27~28" author: "尚硅谷研究院" date: 2026-07-16
用生活理解:看门狗就像社区里的保安——你得定时去保安亭刷卡(喂狗),证明你还在正常工作。如果你一直没来刷卡(程序跑飞/卡死),保安就拉响警报(系统复位)。IWDG 是独立供电的保安(不怕停电),WWDG 是严格规定只能在特定时间窗口内刷卡的保安。
嵌入式系统运行中可能因电磁干扰、电源波动、软件 bug 等原因导致程序跑飞或死循环。看门狗可以在程序异常时自动复位系统,保证设备不会"死机"。
IWDG(Independent Watchdog)使用独立的 LSI 时钟(~40KHz),与主时钟独立——即使主时钟停振,IWDG 仍然工作。
LSI(~40KHz) → 8位预分频器 → 12位递减计数器(RL[11:0]) → 复位
↑
键寄存器(KR)写 0xAAAA 重装
| 寄存器 | 功能 |
|---|---|
| KR (键寄存器) | 写 0xCCCC 启动、写 0xAAAA 喂狗、写 0x5555 允许写 PR/RLR |
| PR (预分频器) | 分频系数(3 位) |
| RLR (重装载寄存器) | 12 位重装值(0~4095) |
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
项目路径: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");
}
}
项目路径: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(Window Watchdog)更加灵活——它要求必须在特定的时间窗口内喂狗:
| 特性 | 说明 |
|---|---|
| 时钟源 | APB1(36MHz),不是独立时钟 |
| 计数器 | 7 位递减(T[6:0],最大值 127) |
| 窗口上限 | 由 CFR 寄存器 W[6:0] 设定 |
| 喂狗时机 | 必须在 CNT > W[6:0] 且 CNT ≠ 0x3F 时喂狗 |
| 早期唤醒中断 (EWI) | CNT=0x40 时产生中断(可提前预警) |
窗口区(允许喂狗) 禁止区
CNT: 127 ← ... ← [W+1] ← ... ← 0x40 ← 0x3F → 复位
↑ ↑
最早喂狗时机 最晚喂狗时机
Tout = (CNT - W) × (PSC + 1) / APB1
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;
}
}
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 | 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() |