4. IWDG实战:CubeMX配置与HAL库喂狗函数
好,咱们直接进入正题。看门狗这东西,说白了就是给单片机请了个「监工」。你程序跑得好好的,它不吭声;一旦你死机了、卡死了,它二话不说直接给你来个硬件复位。
在医疗制氧机里,这个「监工」是必须的。你想啊,病人正吸着氧呢,主控芯片突然死机了——电磁阀不动作了,氧浓度失控了,这可不是闹着玩的。所以独立看门狗(IWDG)是底线中的底线。
4.1 为什么选IWDG而不是WWDG?
STM32里其实有两个看门狗:IWDG(独立看门狗)和WWDG(窗口看门狗)。我个人习惯,在医疗设备里首选IWDG。原因很简单:它用的是独立的40kHz内部RC振荡器,就算主时钟挂了,它照样能工作。
WWDG虽然也能防死锁,但它依赖系统时钟。万一主时钟出问题,WWDG也跟着罢工了。这在医疗设备里是不可接受的。
核心原则:IWDG是最后一道防线,它的时钟源必须独立于主系统。
4.2 CubeMX配置步骤
咱们一步步来。打开CubeMX,选好你的芯片型号(我以STM32F103为例)。
- 找到IWDG外设:在Pinout & Configuration标签页里,左侧列表找到「IWDG」。
- 激活它:勾选「Activated」。就这么简单,硬件模块就使能了。
- 配置预分频器和重装载值:这是关键。
看下面这个表格,是我常用的配置组合:
| 预分频器(PR) | 分频系数 | LSI时钟(40kHz)下的计数频率 | 最大超时时间(12位计数器) |
|---|---|---|---|
| 0 | 4 | 10kHz | 409.6ms |
| 1 | 8 | 5kHz | 819.2ms |
| 2 | 16 | 2.5kHz | 1.638s |
| 3 | 32 | 1.25kHz | 3.276s |
| 4 | 64 | 625Hz | 6.553s |
| 5 | 128 | 312.5Hz | 13.107s |
| 6 | 256 | 156.25Hz | 26.214s |
我在制氧机项目里,一般选预分频器为4(PR=2),重装载值设为4095。这样超时时间大约是1.638秒。为什么选这个值?
嗯,这里有个经验:超时时间要大于主循环最长执行时间的2倍。制氧机的主循环里要处理传感器采集、PID运算、电磁阀控制、通信等任务,最坏情况下大概600ms。1.638秒的窗口,留了足够的余量,又不会太长导致死机后复位太慢。
小技巧:重装载值最大是4095(12位计数器)。如果你需要更长的超时时间,就加大预分频器。但注意,预分频器越大,计数精度越低。我一般不超过PR=4。
4.3 HAL库喂狗函数:HAL_IWDG_Refresh
配置好之后,代码里怎么用?HAL库已经封装好了,就一个函数:
/* 喂狗操作 */
HAL_IWDG_Refresh(&hiwdg);
就这么一行。每次调用,就把计数器重置为初始值,防止看门狗超时复位。
但问题来了:在哪里喂狗?
很多新手喜欢在main函数的while(1)循环开头就喂狗。这其实是个坏习惯。你想想看,如果程序在循环中间某个地方卡死了,但每次循环开头都喂了狗,那看门狗永远不会复位——因为它每次都被重置了。
我曾经在一个项目里就吃过这个亏。当时调试一个传感器驱动,I2C总线偶尔会锁死。但我在循环开头喂狗,导致锁死后看门狗不动作,设备一直卡在I2C等待里。后来花了整整两天才定位到问题。
正确的做法是:在关键任务完成后喂狗。比如:
void main_loop(void)
{
while(1)
{
/* 任务1:读取传感器数据 */
if(read_sensors() != HAL_OK)
{
/* 错误处理,但不喂狗 */
error_handler();
}
/* 任务2:PID运算 */
pid_calculate();
/* 任务3:控制电磁阀 */
control_valves();
/* 所有关键任务完成,喂狗 */
HAL_IWDG_Refresh(&hiwdg);
/* 任务4:通信(非关键,即使卡住也不影响核心功能) */
communication_task();
}
}
看到了吗?我把喂狗放在三个核心任务之后。这样,只要任何一个核心任务卡死,喂狗就不会执行,看门狗就会在1.638秒后复位系统。
重要警告:不要在中断服务函数里喂狗!中断里喂狗会掩盖主循环的死锁问题。我曾经见过一个工程师在定时器中断里喂狗,结果主循环死锁了,中断还在跑,看门狗永远不复位——这等于把看门狗废了。
4.4 实战中的避坑指南
说几个我踩过的坑:
- 坑一:初始化顺序。IWDG必须在系统时钟初始化之后、外设初始化之前配置。因为IWDG一旦使能,就不能再修改预分频器和重装载值了。我习惯在MX_IWDG_Init()里配置好,后面不动它。
- 坑二:调试时关掉IWDG。调试模式下,如果你单步执行,看门狗会超时复位。可以在CubeMX里勾选「IWDG in Debug mode」选项,或者调试时暂时注释掉喂狗代码。我一般会在调试版本里加个宏控制:
#ifndef DEBUG_MODE
HAL_IWDG_Refresh(&hiwdg);
#endif
- 坑三:低功耗模式。如果制氧机需要进入待机模式省电,IWDG在待机模式下仍然工作。进入待机前要确保最后一次喂狗的时间点,避免待机期间被复位。我一般会在进入待机前先喂一次狗,然后立即进入待机。
4.5 验证看门狗是否生效
配置完了,怎么验证?很简单:写个死循环,看它会不会复位。
void test_watchdog(void)
{
uint32_t count = 0;
while(1)
{
count++;
if(count < 1000000)
{
/* 正常喂狗 */
HAL_IWDG_Refresh(&hiwdg);
}
else
{
/* 模拟死锁:停止喂狗 */
/* 什么也不做,等待看门狗复位 */
while(1);
}
}
}
用示波器测量复位引脚,你会看到一个低脉冲——那就是看门狗在干活。如果没看到,检查你的配置和喂狗位置。
好了,IWDG的实战配置就这些。下一节咱们聊聊软件防死锁的设计思路,那才是真正考验架构能力的地方。