嵌入式实战 可靠性设计
看门狗双层策略:IWDG + 通信看门狗组合
一个常见问题:MCU 在跑、FreeRTOS 在调度、但通信已经断了。设备没有死机,但不响应任何命令。IWDG 不会触发(主循环还在喂狗),操作员以为设备坏了。
这就是双层看门狗的必要性:一层保 MCU 不死,一层保通信不断。
第一层:IWDG(硬件独立看门狗)
STM32F103 内置独立看门狗,使用独立的 40kHz LSI 时钟,即使主时钟失效也能触发复位。
// 初始化:4 秒超时
void MX_IWDG_Init(void) {
IWDG->KR = 0x5555; // 解锁写保护
IWDG->PR = IWDG_PRESCALER_64; // 64 分频
IWDG->RLR = 2500; // 重装载: 4 秒 (64*2500/40kHz)
IWDG->KR = 0xCCCC; // 启动
}
// Service 层包装,APP 层通过此接口喂狗
void packer_watchdog_kick(void) {
IWDG->KR = 0xAAAA; // 喂狗
}
喂狗位置:MonitorTask 每 500ms 周期喂一次。如果 MonitorTask 卡死(被高优先级任务饿死、死锁等),IWDG 在 4 秒后触发 MCU 复位。
第二层:通信看门狗(软件)
IWDG 只能检测 MCU 级故障——程序完全卡死或跑飞。它检测不到"程序正常运转但通信链路断开"的情况。
// MonitorTask 每 500ms 检查一次
void monitor_comm_watchdog(void) {
uint32_t now = xTaskGetTickCount();
uint32_t last_rx = packer_serial_frame_get_last_rx_tick();
if ((now - last_rx) > APP_CFG_COMM_WATCHDOG_TIMEOUT_MS) {
// 通信超时 → 锁存报警,但不复位 MCU
packer_fault_latch_set(APP_ALARM_COMM_TIMEOUT);
}
}
通信看门狗的参数通过运行时配置管理:
// app_runtime_config_defaults.h
#define RCFG_DEFAULT_COMM_WATCHDOG_TIMEOUT_MS 5000u // 5 秒
层级对比
| 维度 | IWDG(硬件) | 通信看门狗(软件) |
|---|---|---|
| 检测范围 | MCU 级卡死、时钟失效 | 通信链路中断 |
| 超时值 | 4 秒(硬件固定) | 5 秒(运行时可调) |
| 触发动作 | MCU 硬件复位 | 锁存报警码 + 状态快照标记 |
| 恢复方式 | 复位后重新初始化 | 收到新命令后自动清除 |
| 喂狗者 | MonitorTask(500ms 周期) | ProtoTask 收到合法帧时自动更新 |
| 依赖 | 独立 LSI 时钟 | FreeRTOS tick |
为什么是两层不是一层
单层看门狗(无论硬件还是软件)都无法覆盖全部故障场景:
- 只有 IWDG:通信断了 30 秒 MCU 也不会复位——主循环还在正常喂狗。设备变成"活死人"。
- 只有通信看门狗:FreeRTOS tick 失效或高优先级任务死循环,通信看门狗自己都跑不了。
两层互补:一层保系统不彻底死亡,一层保通信不静默断开。
实现细节
通信看门狗的时间戳更新位置:
// ProtoTask 解码一帧合法协议后:
static void on_valid_frame(void) {
s_last_rx_tick = xTaskGetTickCount(); // 更新时间戳
// ... 正常协议处理
}
// MonitorTask 检查:
uint32_t elapsed = now - s_last_rx_tick;
if (elapsed > comm_watchdog_timeout_ms) {
// 通信超时报警
}
喂狗路径的层级隔离:
MonitorTask
→ packer_watchdog_kick() // Service 层
→ bsp_watchdog_kick() // BSP 层
→ IWDG->KR = 0xAAAA // 硬件寄存器
APP 层不直接写 IWDG 寄存器,通过 Service 层包装调用。这是分层约束的一个具体体现。
IWDG 保证死机后能重启。通信看门狗保证"没死但聋了"时能报警。两个一起用才能覆盖嵌入式设备最常见的两类无声故障。
Comments NOTHING