引言:为什么需要“窗口”看门狗?
在单片机应用中,看门狗定时器是保障系统稳定运行的最后一道防线。STM32F103内置了两种看门狗:独立看门狗(IWDG)和窗口看门狗(WWDG)。如果说独立看门狗是一只只要定期投喂就不会咬人的“宠物狗”,那么窗口看门狗就是一只要求极为严格的“警犬”——它规定了你必须在特定的时间段内(既不能太早,也不能太晚)进行“投喂”(喂狗),否则就会触发系统复位。
这种严格的机制使得窗口看门狗特别适用于检测那些程序“跑飞”后又意外回到正常轨道的复杂故障情况,为高安全要求的应用提供了更可靠的保障。
1. 窗口看门狗(WWDG)核心原理剖析
什么是“窗口”?之所以称为窗口就是因为其喂狗时间是一个有上下限的范围内(窗口),它包含一个7位的递减计数器(T[6:0]),这个计数器不断从预设值(最大0x7F)向下递减。你可以通过设定相关寄存器,设定其上限时间(下限固定)。喂狗的时间不能过早也不能过晚。而独立看门狗限制喂狗时间在0-x内,x由相关寄存器决定。喂狗的时间不能过晚。
下窗口:固定值0x40。当计数器值递减到0x40(即64)再减一变为0x3F时,会产生复位。这意味着计数器值绝对不能小于0x40。
上窗口:由用户配置的窗口值(W[6:0]) 决定。这个值必须大于0x40而小于0x7F。
所谓的“窗口”,就是指从“上窗口值”到“下窗口值(0x40)”之间的这段时间区域。
窗口看门狗时序图:

窗口看门狗框图:

工作原理:过早或过晚都不行
窗口看门狗的工作逻辑可以用一句话概括:必须在窗口期内刷新计数器,否则系统复位。
复位条件1:刷新过早(窗口外)
如果程序在计数器值大于上窗口值(即还没进入窗口)的时候就进行了喂狗操作,系统会立即产生复位。这能有效防止程序在错误的执行阶段(比如初始化阶段)意外喂狗,掩盖了真正的逻辑错误。
复位条件2:刷新过晚/不刷新
如果计数器值递减到0x3F(即小于下窗口值0x40)时,程序仍未进行喂狗,系统也会产生复位。这是传统的“超时复位”。
正常操作
程序必须在计数器值 ≤ 上窗口值,并且 ≥ 0x40 的这个时间区间内,进行一次喂狗操作,将计数器重置为初始值,如此循环往复。
提前唤醒中断(EWI)
如果启动了看门狗并且允许中断,在计数器值达到0x40时,会触发一个提前唤醒中断(EWI)。这个中断被称为“死前中断”,是系统给你的最后一次机会。在这个中断服务函数中,你可以执行一些紧急操作,比如保存关键数据、清理状态或发出报警信号,然后再进行喂狗,以避免复位。但请注意,这个窗口非常短暂,必须快速处理。
2. 关键参数与超时计算
窗口看门狗的时钟源来自于APB1总线时钟(PCLK1,通常为36MHz或72MHz)。它经过了固定的4096分频后再通过一个可编程的预分频器到达计数器 
计算公式
计数器时钟频率:
其中,预分频器(Prescaler)可以是1、2、4、8,对应配置寄存器CFR的WDGTB[1:0]位。
超时时间计算公式(从初始计数值递减到0x40的时间)。
TWWDG=CNT_CK1×(Counter0x40)
更常用的公式为:
Tmax=FPCLK14096×2WDGTB×(Counter0x40+1)

参数配置策略
假设PCLK1 = 36MHz:
Prescaler(预分频器):决定了计数器的递减速度。
Counter(计数器初值):决定了窗口期的起始点。通常设置为最大值0x7F,以获得最长的监控时间。
Window(窗口值):决定了窗口期的结束点。
应用示例:如果你想监控一段正常运行时间为50ms的程序,你可以将超时时间设置为略大于50ms,比如60ms。通过调整预分频器和计数器值,你可以算出窗口值,确保程序在50ms结束时刚好落在窗口期内。
控制寄存器(WWDG_CR):

void WWDG_Enable(uint8_t Counter);//启动并设置初始值
void WWDG_SetCounter(uint8_t Counter);//喂狗
配置寄存器(WWDG_CFR):

void WWDG_EnableIT(void);//使能提前唤醒中断
void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);
void WWDG_SetWindowValue(uint8_t WindowValue);
状态寄存器(WWDG_SR):

FlagStatus WWDG_GetFlagStatus(void);
void WWDG_ClearFlag(void);

窗口看门狗配置过程
① 使能看门狗时钟:
     RCC_APB1PeriphClockCmd();
② 设置分频系数:
     WWDG_SetPrescaler();
③ 设置上窗口值:
     WWDG_SetWindowValue();
④ 开启提前唤醒中断并分组(可选):
     WWDG_EnableIT();   
     NVIC_Init();
⑤ 使能看门狗:
     WWDG_Enable();
⑥ 喂狗:
    WWDG_SetCounter();
⑦编写中断服务函数
   WWDG_IRQHandler();

代码实现
在生成的代码基础上,我们添加用户逻辑。
void IWDG_Init(u8 prer,u16 rlr) 
{	
 	IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);  //使能对寄存器IWDG_PR和IWDG_RLR的写操作
	IWDG_SetPrescaler(prer);  //设置IWDG预分频值:设置IWDG预分频值为64
	IWDG_SetReload(rlr);  //设置IWDG重装载值
	IWDG_ReloadCounter();  //按照IWDG重装载寄存器的值重装载IWDG计数器
	IWDG_Enable();  //使能IWDG
}
//喂独立看门狗
void IWDG_Feed(void)
{   
 	IWDG_ReloadCounter();	//重载计数值									   
}

//保存WWDG计数器的设置值,默认为最大. 
u8 WWDG_CNT=0x7f; 
//初始化窗口看门狗 	
//tr   :T[6:0],计数器值 
//wr   :W[6:0],窗口值 
//fprer:分频系数(WDGTB),仅最低2位有效 
//Fwwdg=PCLK1/(4096*2^fprer). 

void WWDG_Init(u8 tr,u8 wr,u32 fprer)
{ 
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);  //   WWDG时钟使能
	WWDG_CNT=tr&WWDG_CNT;   //初始化WWDG_CNT.
	WWDG_SetPrescaler(fprer);////设置IWDG预分频值
	WWDG_SetWindowValue(wr);//设置窗口值
	WWDG_Enable(WWDG_CNT);	 //使能看门狗 ,	设置 counter .
	WWDG_ClearFlag();//清除提前唤醒中断标志位
	WWDG_NVIC_Init();//初始化窗口看门狗 NVIC
	WWDG_EnableIT(); //开启窗口看门狗中断
} 
//重设置WWDG计数器的值
void WWDG_Set_Counter(u8 cnt)
{
    WWDG_Enable(cnt);//使能看门狗 ,	设置 counter .	 
}
//窗口看门狗中断服务程序
void WWDG_NVIC_Init()
{
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn;    //WWDG中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;   //抢占2,子优先级3,组2	
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;	 //抢占2,子优先级3,组2	
    NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; 
	NVIC_Init(&NVIC_InitStructure);//NVIC初始化
}

void WWDG_IRQHandler(void)
{
	WWDG_SetCounter(WWDG_CNT);	  //当禁掉此句后,窗口看门狗将产生复位
	WWDG_ClearFlag();	  //清除提前唤醒中断标志位
	LED1=!LED1;		 //LED状态翻转
}
主要代码逻辑 (main.c):
int main(void)
 {		
	delay_init();	    	 //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 //串口初始化为115200
 	LED_Init();
	KEY_Init();          //按键初始化	 
	LED0=0;
	delay_ms(300);	  
	WWDG_Init(0X7F,0X5F,WWDG_Prescaler_8);//计数器值为7f,窗口寄存器为5f,分频数为8	   
 	while(1)
	{
		LED0=1;			  	   
	}   
}

4. 调试技巧与注意事项
时间计算是关键:务必根据你的PCLK1频率,仔细计算并验证窗口时间。可以使用逻辑分析仪或示波器在喂狗函数前后翻转一个IO口来测量实际喂狗间隔。
中断服务程序要精简:必须非常短小精悍,因为留给你的时间仅为一个计数周期(微秒级)。复杂操作会导致复位不可避免。
不能禁用:窗口看门狗一旦启动,在系统复位前无法被停止。
区分复位源:在程序启动时,可以通过检查RCC_CSR寄存器中的WWDGRSTF标志位来判断本次复位是否由窗口看门狗引起。
总结
STM32F103的窗口看门狗(WWDG)提供了一种比独立看门狗更精确、更严格的程序监控机制。通过强制规定喂狗的“时间窗口”,它能有效检测出那些传统看门狗无法捕捉的软件逻辑故障。虽然配置稍显复杂,但通过对超时时间的精确计算和合理的窗口值设置,我们能够为嵌入式系统构建起一道坚固的可靠性防线。希望本文能帮助你彻底理解并掌握WWDG的使用。