3、独立看门狗(IWDG)寄存器详解:STM32平台下IWDG的关键寄存器配置(KR、PR、RLR、SR)

好,咱们接着聊。上一节我们把IWDG的整体框架搭起来了,这一节要深入到底层——寄存器。

说实话,很多工程师写看门狗代码,都是直接抄库函数。我以前也这么干,直到有一次产品在现场频繁复位,查了三天才发现是预分频器配置错了。从那以后,我养成了一个习惯:用寄存器之前,一定先看数据手册

STM32的IWDG,说白了就四个关键寄存器:KR(键寄存器)、PR(预分频寄存器)、RLR(重装载寄存器)、SR(状态寄存器)。掌握了它们,你就掌握了IWDG的命脉。

3.1 键寄存器(KR)—— 看门狗的“钥匙”

这个寄存器很有意思。它就像一个保险柜的密码锁,你不能随便往里写数据。只有特定的“钥匙”才能开门。

KR寄存器是16位只写寄存器,地址偏移0x00。它的作用有三个:

  • 写入0x5555:解锁PR和RLR寄存器,允许配置
  • 写入0xAAAA:喂狗(刷新计数器)
  • 写入0xCCCC:启动看门狗(硬件启动后,这个操作由硬件自动完成)

重点来了:一旦你写了0xCCCC启动了IWDG,它就再也停不下来了。除非你复位整个芯片。嗯,这就是“独立”二字的含义——它独立于主程序运行,连你自己都关不掉它。

我在项目中遇到过一个问题:调试阶段为了方便,想临时关闭看门狗。结果发现IWDG一旦使能,软件根本关不掉。后来只能在调试器里把IWDG的时钟源给断了。所以我的建议是:产品开发阶段,先用软件看门狗(WWDG)调试,最后再换成IWDG

3.2 预分频寄存器(PR)—— 决定“滴答”的速度

PR寄存器,地址偏移0x04,用来设置看门狗计数器的时钟分频系数。

为什么需要分频?因为IWDG的时钟源是LSI(低速内部振荡器),典型频率是40kHz(不同芯片有差异,一般在30~60kHz之间)。这个频率太快了,直接计数的话,溢出时间只有零点几毫秒。所以我们需要分频,把时钟降下来。

PR寄存器的低3位有效,对应关系如下:

PR[2:0] 分频系数 时钟频率(LSI=40kHz时)
000 4 10 kHz
001 8 5 kHz
010 16 2.5 kHz
011 32 1.25 kHz
100 64 625 Hz
101 128 312.5 Hz
110 256 156.25 Hz
111 256 156.25 Hz

个人经验:我一般选PR=4(分频4)或PR=6(分频256)。分频太小,计数器跑得太快,喂狗频率太高,浪费CPU;分频太大,溢出时间太长,系统死机了要等半天才复位。制氧机这种场景,我习惯用分频128或256,配合RLR设置一个1~4秒的溢出时间,既安全又省心。

3.3 重装载寄存器(RLR)—— 设定“倒计时”时长

RLR寄存器,地址偏移0x08,12位有效(0~4095)。它决定了计数器从哪个值开始往下减。

溢出时间的计算公式很简单:

T_out = (RLR + 1) × T_ck

其中 T_ck = 1 / (f_LSI / PR分频系数)

举个例子:LSI=40kHz,PR=6(分频256),RLR=4000。

T_ck = 1 / (40000 / 256) = 6.4 ms
T_out = (4000 + 1) × 6.4 ms ≈ 25.6 秒

嗯,25秒,对于制氧机来说太长了。一般我们设1~4秒就够了。

注意:RLR寄存器必须在写入KR=0x5555之后才能修改。而且修改后,新的值不会立即生效——要等到计数器溢出复位,或者你手动喂一次狗(写0xAAAA),才会把RLR的值加载到计数器里。我曾经在这里栽过跟头:改了RLR,以为生效了,结果看门狗还是按旧值在跑。

3.4 状态寄存器(SR)—— 看看“门”关好了没

SR寄存器,地址偏移0x0C,只读。它有两个标志位:

  • PVU(Prescaler Update):预分频器更新标志。0表示PR寄存器更新完成,1表示正在更新。
  • RVU(Reload Update):重装载值更新标志。0表示RLR寄存器更新完成,1表示正在更新。

为什么要检查这两个位?因为IWDG的寄存器写操作是异步的。你写了PR或RLR,硬件需要几个LSI时钟周期才能完成更新。如果你紧接着就去喂狗或启动看门狗,可能会出问题。

所以标准的配置流程是:

// 1. 解锁PR和RLR
IWDG->KR = 0x5555;

// 2. 配置预分频器(分频256)
IWDG->PR = 0x06;

// 3. 等待PVU位清零
while (IWDG->SR & IWDG_SR_PVU);

// 4. 配置重装载值(4000)
IWDG->RLR = 4000;

// 5. 等待RVU位清零
while (IWDG->SR & IWDG_SR_RVU);

// 6. 喂狗,使新配置生效
IWDG->KR = 0xAAAA;

// 7. 启动看门狗(如果之前没启动)
IWDG->KR = 0xCCCC;

避坑指南:我曾经在等待PVU和RVU标志时,忘了加超时保护。结果有一次LSI时钟异常变慢,程序卡死在while循环里,看门狗反而没起作用。后来我加了一个超时计数器——如果等待超过一定时间,就强制复位。你想想看,看门狗自己把自己给看死了,这多尴尬。

3.5 寄存器配置实战要点

好了,四个寄存器讲完了。我总结几个实战中容易踩的坑:

  1. LSI频率不准:不同芯片、不同温度下,LSI可能在30~60kHz之间波动。所以算出来的溢出时间只是个大概值。量产前一定要实测。
  2. 喂狗位置要选对:别放在中断里喂狗。万一主循环卡死了,中断还能跑,看门狗就形同虚设了。我习惯在主循环的末尾喂狗,确保整个循环都跑通了。
  3. 初始化顺序不能乱:先解锁,再配PR和RLR,等标志位清零,最后喂狗启动。顺序错了,配置可能不生效。
  4. 调试时注意:用调试器单步执行时,IWDG还在跑。如果你停在断点上太久,芯片就复位了。可以在调试前先关闭IWDG,或者把调试器的“调试时停止看门狗”选项打开。

嗯,这一节的内容就到这儿。寄存器这东西,光看不行,得动手写代码。下一节我们讲代码实现的时候,我会把完整的初始化函数和喂狗函数贴出来,到时候你照着写一遍,印象就深了。