┌──────────────┐
│ 计数器递减 │
│ CNT ──▶ 0 │
└──────┬───────┘
│
┌────────▼────────┐
│ CNT == 0 ? │
└────────┬────────┘
│
┌────────────┴────────────┐
│ 是 │ 否
▼ ▼
┌───────────┐ ┌───────────┐
│ 系统复位 │ │ 继续递减 │
└───────────┘ └───────────┘
▲
│
在 CNT 到 0 前
"喂狗" (重载计数器)
核心概念:
时钟源:LSI (40kHz RC 振荡器,独立于主时钟)
LSI 40kHz ──▶ 预分频器 (4/8/16/.../256) ──▶ 12位递减计数器 ──▶ 复位
▲ │
│ │
IWDG_SetPrescaler() IWDG_ReloadCounter()
溢出时间公式:
T_ovf = (4 × 2^PR) × RL / 40kHz
= 预分频系数 × 重载值 / 40kHz
| 预分频 (PR) 系数 | 4×2^PR | 取值 |
|---|---|---|
| 0 | 4 | 4 |
| 1 | 8 | 8 |
| 2 | 16 | 16 |
| 3 | 32 | 32 |
| 4 | 64 | 64 |
| 5 | 128 | 128 |
| 6 | 256 | 256 |
| 7 | 256 | 保留 (不推荐) |
示例:PR=4 (64分频), RL=625
T = 64 × 625 / 40000 ≈ 1.0 秒
时钟源:APB1 (36MHz),经 4096 分频后
APB1 ──▶ /4096 ──▶ 预分频器 (/1/2/4/8) ──▶ 7位递减计数器 ──▶ 复位
▲ │
│ │
WWDG_SetPrescaler() WWDG_SetCounter()
│ │
└──────────────────────┘
窗口期喂狗条件:
0x40 < CNT ≤ W[6:0]
计数器值
│
0x7F ─── 最大计数值
│ ┌───────────────┐
│ │ 允许喂狗区域 │ ← 窗口期 (CNT > W[6:0])
W ────┤ │
│ │ 禁止喂狗 │ ← 在此喂狗 → 复位
│ │ (必须继续减) │
0x40 ───┤ │ ← EWIF 置位 (早期唤醒中断)
│ └───────────────┘
0x3F ─── 复位阈值
│
▼ 复位
WWDG 特性:
| 函数 | 说明 |
|---|---|
void IWDG_WriteAccessCmd(uint16_t IWDG_WriteAccess) |
使能写访问 (必须先调用) |
void IWDG_SetPrescaler(uint8_t IWDG_Prescaler) |
设置预分频系数 |
void IWDG_SetReload(uint16_t Reload) |
设置重载值 (12位, 0~0xFFF) |
void IWDG_ReloadCounter(void) |
喂狗 (重载计数器) |
void IWDG_Enable(void) |
使能 IWDG (一旦使能无法软件关闭) |
FlagStatus IWDG_GetFlagStatus(uint16_t IWDG_FLAG) |
获取状态标志 (PVU/RVU) |
IWDG_Prescaler 可选值:
| 宏 | 分频系数 |
|---|---|
IWDG_Prescaler_4 |
4 |
IWDG_Prescaler_8 |
8 |
IWDG_Prescaler_16 |
16 |
IWDG_Prescaler_32 |
32 |
IWDG_Prescaler_64 |
64 |
IWDG_Prescaler_128 |
128 |
IWDG_Prescaler_256 |
256 |
| 函数 | 说明 |
|---|---|
void WWDG_Enable(uint8_t Counter) |
使能 WWDG 并设置初始计数值 |
void WWDG_SetWindowValue(uint8_t WindowValue) |
设置窗口值 (W[6:0]) |
void WWDG_SetCounter(uint8_t Counter) |
喂狗 (重载计数器) |
void WWDG_EnableIT(void) |
使能早期唤醒中断 (EWI) |
void WWDG_SetPrescaler(uint32_t WWDG_Prescaler) |
设置预分频 |
FlagStatus WWDG_GetFlagStatus(void) |
获取 EWIF 标志 |
void WWDG_ClearFlag(void) |
清除 EWIF 标志 |
WWDG_Prescaler 可选值:
| 宏 | 分频系数 |
|---|---|
WWDG_Prescaler_1 |
1 |
WWDG_Prescaler_2 |
2 |
WWDG_Prescaler_4 |
4 |
WWDG_Prescaler_8 |
8 |
/*------------------------------------------------
* IWDG_Example.c
* 溢出时间 ≈ 1 秒, 主循环 500ms 喂狗
*------------------------------------------------*/
#include "stm32f10x.h"
static void Delay_ms(uint32_t ms);
/* IWDG 初始化:溢出约 1 秒 */
void IWDG_Init(void)
{
/* 计算:LSI ≈ 40kHz, PR=4 (64分频), RL=625 */
/* T = 64 × 625 / 40000 = 1.0 秒 */
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); // 允许写
IWDG_SetPrescaler(IWDG_Prescaler_64); // 64 分频
IWDG_SetReload(625); // 重载值 625
IWDG_ReloadCounter(); // 喂第一次
IWDG_Enable(); // 使能 IWDG
}
int main(void)
{
IWDG_Init();
while (1)
{
/* 用户代码 */
IWDG_ReloadCounter(); // 500ms 喂一次狗
Delay_ms(500); // 模拟工作延时
/* 如果在 1 秒内没有喂狗,系统复位 */
}
}
/*------------------------------------------------
* WWDG_Example.c
* 窗口值 0x5F, 计数器 0x7F, 早期唤醒中断
*------------------------------------------------*/
#include "stm32f10x.h"
static void Delay_ms(uint32_t ms);
void WWDG_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
/* 窗口值 = 0x5F, 计数值 = 0x7F, 预分频 = /8 */
/* 超时时间 ≈ 4096 × 8 × (0x7F - 0x3F) / 36MHz ≈ 5.8ms */
/* 窗口上限 ≈ 4096 × 8 × (0x7F - 0x5F) / 36MHz ≈ 2.9ms */
WWDG_SetPrescaler(WWDG_Prescaler_8);
WWDG_SetWindowValue(0x5F); // 窗口值
WWDG_EnableIT(); // 使能 EWI 中断
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = WWDG_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
WWDG_Enable(0x7F); // 使能 WWDG,计数器=0x7F
}
/* WWDG 早期唤醒中断处理 */
void WWDG_IRQHandler(void)
{
if (WWDG_GetFlagStatus())
{
WWDG_SetCounter(0x7F); // 喂狗
WWDG_ClearFlag(); // 清标志
}
}
int main(void)
{
WWDG_Init();
while (1)
{
/* 用户代码 */
/* WWDG 在中断中喂狗 */
}
}
/*------------------------------------------------
* DualWatchdog_Example.c
* IWDG: 长超时 (1秒), 主循环喂狗
* WWDG: 短超时 (5.8ms), 中断喂狗
*------------------------------------------------*/
#include "stm32f10x.h"
void Watchdog_Init(void)
{
/* ---- IWDG: 1秒 ---- */
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
IWDG_SetPrescaler(IWDG_Prescaler_64);
IWDG_SetReload(625);
IWDG_ReloadCounter();
IWDG_Enable();
/* ---- WWDG: 5.8ms ---- */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
WWDG_SetPrescaler(WWDG_Prescaler_8);
WWDG_SetWindowValue(0x5F);
WWDG_EnableIT();
WWDG_Enable(0x7F);
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = WWDG_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
void WWDG_IRQHandler(void)
{
if (WWDG_GetFlagStatus())
{
WWDG_SetCounter(0x7F);
WWDG_ClearFlag();
}
}
int main(void)
{
Watchdog_Init();
while (1)
{
IWDG_ReloadCounter(); // 主循环喂 IWDG
/* 如果主循环卡死超过 1 秒,IWDG 复位 */
/* 如果中断卡死超过 5.8ms,WWDG 复位 */
/* 如果喂狗太早 (窗口期外),WWDG 复位 */
}
}
IWDG 一旦使能无法软件关闭
IWDG_Enable() 或使用 DBGMCU_ConfigWWDG 窗口期外喂狗也复位
IWDG 预分频和重载寄存器写保护
IWDG_WriteAccessCmd(ENABLE)IWDG 的 LSI 精度差
WWDG 时序依赖 APB1 时钟
早期唤醒中断 (EWI)
调试期间看门狗干扰
DBGMCU_Config(DBGMCU_IWDG_STOP | DBGMCU_WWDG_STOP, ENABLE)IWDG 寄存器更新需要时间
IWDG_GetFlagStatus(IWDG_FLAG_PVU/RVU) 轮询等待