3、STM32独立看门狗(IWDG)详解:寄存器配置、预分频器设置、重装载值计算,实现一个1秒超时的独立看门狗

看门狗这东西,说白了就是系统的一个「保安」。程序跑飞了?死循环了?它帮你一巴掌拍醒。我在消防报警系统里吃过这个亏——有一次现场调试,主机死机了,蜂鸣器一直响,值班人员只能拔电源。从那以后,我的每个项目里,看门狗都是标配。

今天咱们聊STM32的独立看门狗,简称IWDG。它独立于主时钟,自己有个32kHz的LSI振荡器。就算主时钟挂了,它照样能工作。嗯,这一点在消防系统里特别重要——你想想看,火灾报警器要是因为主时钟故障就罢工了,那后果可不敢想。

3.1 IWDG的核心寄存器

要玩转IWDG,你只需要记住三个寄存器。我个人习惯把它们叫做「三兄弟」:

  • IWDG_KR(键寄存器):写0x5555解锁,写0xAAAA喂狗,写0xCCCC启动看门狗
  • IWDG_PR(预分频寄存器):设置分频系数,决定计数时钟频率
  • IWDG_RLR(重装载寄存器):设置计数值,决定超时时间

重要提醒:IWDG的寄存器有写保护。必须先往KR写0x5555,才能修改PR和RLR。这个设计是为了防止程序跑飞时误改配置。我在项目中见过有人忘了这一步,折腾了半天才发现。

3.2 预分频器设置

IWDG的时钟源是LSI,典型频率是32kHz(实际在30~60kHz之间,手册上写的是40kHz典型值)。预分频器的作用就是把时钟频率降下来,让计数周期变长。

预分频值通过PR寄存器的低3位设置:

PR[2:0] 预分频系数 计数时钟频率(LSI=32kHz) 计数周期
000 4 8 kHz 0.125 ms
001 8 4 kHz 0.25 ms
010 16 2 kHz 0.5 ms
011 32 1 kHz 1 ms
100 64 500 Hz 2 ms
101 128 250 Hz 4 ms
110 256 125 Hz 8 ms
111 256 125 Hz 8 ms

经验之谈:我一般选预分频系数32或64。这样计数周期在1~2ms,重装载值好算,精度也够。预分频太大,计数周期太长,喂狗时机不好把握;太小了,重装载值得设很大,浪费寄存器位数。

3.3 重装载值计算

超时时间的计算公式很简单:

超时时间 = 重装载值 × 计数周期
         = RLR × (4 × 2^PR) / LSI频率

咱们要实现1秒超时,选预分频系数32(PR=011),计数周期1ms。那么:

RLR = 超时时间 / 计数周期
    = 1000 ms / 1 ms
    = 1000

嗯,1000这个值刚好在12位寄存器范围内(最大4095)。完美。

注意:RLR寄存器只有12位有效,最大值4095。如果你算出来的值超过4095,就得加大预分频系数。我曾经在项目里算错了,设了个5000,结果看门狗根本不按预期工作——因为高位被截断了。

3.4 代码实现:1秒超时IWDG

下面是我在实际项目中用的配置代码。注释写得很详细,你直接抄就能用:

#include "stm32f10x.h"   // 以STM32F1为例

void IWDG_Config(void)
{
    // 第一步:解锁寄存器
    // 往键寄存器写0x5555,允许修改PR和RLR
    IWDG->KR = 0x5555;
    
    // 第二步:设置预分频系数为32
    // PR[2:0] = 011,对应分频系数32
    // 计数时钟频率 = 32kHz / 32 = 1kHz
    // 计数周期 = 1ms
    IWDG->PR = 0x03;   // 注意:只用了低3位
    
    // 第三步:设置重装载值为1000
    // 超时时间 = 1000 × 1ms = 1000ms = 1秒
    IWDG->RLR = 1000;
    
    // 第四步:重新加载计数器
    // 写0xAAAA,把RLR的值加载到计数器
    IWDG->KR = 0xAAAA;
    
    // 第五步:启动看门狗
    // 写0xCCCC,启动IWDG
    IWDG->KR = 0xCCCC;
    
    // 注意:启动后,必须在1秒内喂狗一次
    // 否则系统会复位
}

// 喂狗函数
void IWDG_Feed(void)
{
    // 写0xAAAA,重新加载计数器
    IWDG->KR = 0xAAAA;
}

3.5 避坑指南

我踩过的坑,你千万别再踩了:

  • 喂狗时机不对:喂狗太早或太晚都不行。太早,看门狗形同虚设;太晚,系统已经复位了。我一般把喂狗放在主循环的固定位置,比如每500ms喂一次,留一半余量。
  • 中断里喂狗:千万别在中断服务函数里喂狗!如果主程序死循环了,但中断还能响应,看门狗就被骗过去了。我曾经在项目里犯过这个错,查了两天才发现。
  • LSI频率不准:LSI的精度只有±10%左右。你算的1秒,实际可能是0.9秒或1.1秒。所以设计超时时间时,要留够余量。我一般按最差情况算,确保在最慢的LSI下也能正常工作。
  • 调试时注意:用调试器单步执行时,看门狗还在跑。如果你停在某个断点超过1秒,系统就复位了。我习惯在调试时先禁用看门狗,等程序调通了再打开。

3.6 验证方法

怎么知道你的看门狗配置对了?我教你一个土办法:

  1. 先配置好IWDG,启动后正常喂狗
  2. 在某个地方故意不加喂狗函数
  3. 用示波器测复位引脚,或者看LED闪烁状态
  4. 如果系统每隔1秒复位一次,说明配置成功

我在消防报警主板上就是这么验证的。LED灯一闪一闪的,看着特别直观。嗯,这个方法虽然土,但管用。

小技巧:你可以把超时时间设短一点(比如200ms)来快速验证。等确认逻辑正确了,再改回1秒。这样不用等那么久。

好了,独立看门狗的核心内容就这些。下一章咱们聊窗口看门狗——那个更智能,也更麻烦。到时候我会讲讲怎么在消防报警系统中同时使用两种看门狗,实现双重保护。