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 验证方法
怎么知道你的看门狗配置对了?我教你一个土办法:
- 先配置好IWDG,启动后正常喂狗
- 在某个地方故意不加喂狗函数
- 用示波器测复位引脚,或者看LED闪烁状态
- 如果系统每隔1秒复位一次,说明配置成功
我在消防报警主板上就是这么验证的。LED灯一闪一闪的,看着特别直观。嗯,这个方法虽然土,但管用。
小技巧:你可以把超时时间设短一点(比如200ms)来快速验证。等确认逻辑正确了,再改回1秒。这样不用等那么久。
好了,独立看门狗的核心内容就这些。下一章咱们聊窗口看门狗——那个更智能,也更麻烦。到时候我会讲讲怎么在消防报警系统中同时使用两种看门狗,实现双重保护。