4、STM32窗口看门狗(WWDG)详解:窗口上限与下限的概念,如何配置窗口值防止程序跑飞,与IWDG的配合使用

独立看门狗(IWDG)像个倔脾气的保安——你必须在规定时间内踹它一脚,否则它就重启。但窗口看门狗(WWDG)不一样,它更像个精明的监工:你来得太早不行,来得太晚也不行,必须在它划定的“窗口期”内喂狗。

我第一次用WWDG时,就被这个“窗口”概念绕晕了。后来在调试一个消防报警主控板时,程序明明没死,却总被WWDG复位。查了三天,才发现是喂狗时间落在了窗口之外。嗯,从那以后,我对WWDG的上下限有了刻骨铭心的理解。

4.1 窗口看门狗的本质:为什么需要上下限?

IWDG只检查“是否超时”,但WWDG检查的是“是否在正确的时间段内喂狗”。

你想想看,如果程序跑飞了,它可能还在某个循环里乱转,碰巧在超时前喂了IWDG。但WWDG不一样——它要求喂狗时间必须落在[下限, 上限]这个区间内。程序跑飞后,喂狗时机大概率会偏离这个窗口,从而触发复位。

说白了,WWDG比IWDG更“敏感”,能捕捉到那些“看似活着,实则跑偏”的异常状态。

4.2 窗口上限与下限的概念

WWDG的核心是一个7位递减计数器(CNT),从0x7F往下减到0x40。这个递减过程,就是看门狗的“生命倒计时”。

关键来了:

  • 窗口上限(Upper Window):由配置寄存器WWDG_CFR的W[6:0]位设定。喂狗必须在CNT值大于W[6:0]时进行。如果CNT已经减到小于等于W[6:0],你再喂狗,就会触发复位。
  • 窗口下限(Lower Window):固定为0x40。当CNT减到0x3F时,WWDG会产生早期唤醒中断(EWI),如果中断没处理,再减到0x3F以下就会复位。但喂狗必须在CNT≥0x40时进行,否则复位。

核心公式:

喂狗允许区间:[窗口上限值, 0x7F]

喂狗禁止区间:[0x40, 窗口上限值-1]

复位触发条件:CNT < 0x40 或 在禁止区间内喂狗

举个例子:假设你设置窗口上限为0x60。那么:

  • 当CNT在0x60~0x7F之间时,喂狗是安全的。
  • 当CNT减到0x5F及以下时,喂狗会立即触发复位。
  • 当CNT减到0x3F时,自动复位(除非使能了EWI中断)。

我在项目中遇到过一个问题:把窗口上限设得太接近0x7F,结果主循环稍微抖动一下,喂狗就落在了窗口外。后来我把窗口上限调到0x70左右,留出余量,问题就解决了。

4.3 如何配置窗口值防止程序跑飞

配置WWDG的步骤其实不复杂,但参数选型有讲究。我个人习惯按以下流程来:

  1. 确定时钟源:WWDG使用PCLK1(APB1时钟),通常为36MHz或42MHz(取决于你的系统配置)。
  2. 计算递减周期:递减频率 = PCLK1 / 4096 / 分频系数。分频系数可选1、2、4、8,对应WDGTB[1:0]的00、01、10、11。
  3. 设定窗口上限:根据你的主循环执行时间,留出20%~30%的余量。
  4. 使能WWDG:设置WDGA位启动。

下面是一个典型的配置代码:

void WWDG_Config(void)
{
    // 使能WWDG时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
    
    // 设置分频系数为8,递减频率 = 36MHz / 4096 / 8 ≈ 1098Hz
    WWDG_SetPrescaler(WWDG_Prescaler_8);
    
    // 设置窗口上限为0x60
    // 喂狗必须在CNT > 0x60时进行
    WWDG_SetWindowValue(0x60);
    
    // 设置计数器初始值为0x7F
    WWDG_Enable(0x7F);
    
    // 使能早期唤醒中断(可选)
    WWDG_EnableIT();
    NVIC_EnableIRQ(WWDG_IRQn);
}

避坑指南:

我曾经把窗口上限设成0x7F,结果程序一启动就复位。为什么?因为计数器从0x7F开始递减,第一次喂狗时CNT还是0x7F,但窗口上限也是0x7F,喂狗条件要求CNT 大于窗口上限——0x7F不大于0x7F,所以直接复位。记住:窗口上限必须小于0x7F!

4.4 喂狗策略:在正确的时间做正确的事

WWDG的喂狗时机,需要精确控制。我一般会在主循环的固定位置喂狗,而不是在中断里。为什么?

如果在中断里喂狗,主循环跑飞了,中断可能还在正常触发,WWDG就形同虚设了。你想想看,是不是这个道理?

喂狗代码很简单:

void WWDG_Feed(void)
{
    // 读取当前计数器值
    uint8_t cnt = WWDG->CR & 0x7F;
    
    // 检查是否在窗口内
    if (cnt > WWDG->CFR & 0x7F)
    {
        // 喂狗:写入0x7F到计数器
        WWDG_SetCounter(0x7F);
    }
    else
    {
        // 窗口已关闭,此时喂狗会复位
        // 这里可以做一些错误处理
    }
}

重要提醒:

WWDG的计数器是只读的!你不能直接写任意值,只能通过WWDG_SetCounter()函数将计数器重置为0x7F。而且,这个函数必须在窗口内调用,否则触发复位。

4.5 与IWDG的配合使用:双保险策略

在实际的消防报警系统中,我经常把IWDG和WWDG搭配使用,形成“双保险”。

具体策略是这样的:

看门狗类型 作用 超时时间 喂狗位置
IWDG 防止程序完全死锁 较长(如4秒) 主循环末尾
WWDG 检测程序时序异常 较短(如100ms) 主循环中关键节点

我做过一个项目,主循环里有三个关键任务:传感器采集、数据处理、通信上报。我在每个任务完成后都喂一次WWDG,但只在主循环末尾喂IWDG。这样,如果某个任务卡住了,WWDG会先复位;如果主循环整体卡死,IWDG兜底。

配置示例:

void WatchDog_Init(void)
{
    // 配置IWDG:4秒超时
    IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
    IWDG_SetPrescaler(IWDG_Prescaler_64);   // 40kHz / 64 = 625Hz
    IWDG_SetReload(2500);                   // 2500 / 625 = 4秒
    IWDG_Enable();
    
    // 配置WWDG:100ms窗口
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
    WWDG_SetPrescaler(WWDG_Prescaler_8);
    WWDG_SetWindowValue(0x50);              // 窗口上限
    WWDG_Enable(0x7F);                      // 启动
}

void MainLoop(void)
{
    while(1)
    {
        // 任务1:传感器采集
        Sensor_Collect();
        WWDG_Feed();    // 任务1完成,喂WWDG
        
        // 任务2:数据处理
        Data_Process();
        WWDG_Feed();    // 任务2完成,喂WWDG
        
        // 任务3:通信上报
        Comm_Report();
        WWDG_Feed();    // 任务3完成,喂WWDG
        
        // 所有任务完成,喂IWDG
        IWDG_ReloadCounter();
    }
}

经验之谈:

WWDG的窗口上限不要设得太紧。我见过有人把窗口上限设成0x70,结果系统在低温环境下时钟漂移,喂狗时间偏移了几毫秒,就触发了复位。建议留出至少20%的余量。

4.6 调试WWDG的实用技巧

调试WWDG比IWDG麻烦,因为一旦配置错误,系统会不停复位。我有几个小技巧:

  • 先关掉WWDG,用逻辑分析仪观察喂狗时序:在调试阶段,先注释掉WWDG使能代码,用GPIO翻转来观察喂狗的时间点。
  • 利用早期唤醒中断(EWI):在EWI中断里记录错误信息,然后执行喂狗。这样既能知道程序哪里出了问题,又不会导致复位。
  • 分步调试:先让WWDG超时时间设得很长(比如1秒),确认基本功能正常后,再逐步缩短。
void WWDG_IRQHandler(void)
{
    if(WWDG_GetFlagStatus())
    {
        // 记录错误:计数器即将减到0x3F
        Error_Log("WWDG Early Wakeup!");
        
        // 喂狗,防止复位
        WWDG_SetCounter(0x7F);
        
        // 清除中断标志
        WWDG_ClearFlag();
    }
}

我的习惯:

在产品最终发布前,我会把EWI中断关掉,让WWDG直接复位。因为生产环境中,我不希望程序在异常状态下“苟延残喘”,而是希望它立即复位,恢复到安全状态。消防系统,安全第一嘛。

好了,关于WWDG的窗口概念和配置方法,就讲到这里。下一章我们会深入探讨看门狗在低功耗模式下的行为,以及如何设计一个既省电又可靠的看门狗策略。