3. 看门狗机制原理
看门狗(Watchdog)是嵌入式与安全关键系统中最基础、也最有效的自恢复机制之一。它的核心思想非常朴素:系统必须在规定时间内主动“报到”,证明自己仍然活着;一旦超时未报到,看门狗就判定系统已经失控,并强制其复位或进入安全状态。理解看门狗,关键要抓住三个要素:定时器、喂狗动作和复位策略。这三者共同决定了看门狗是“救命稻草”还是“形同虚设”。
一、看门狗定时器:机制的心脏
看门狗定时器(WDT,Watchdog Timer)本质上是一个递减或递增的计数器。系统正常运行时,软件周期性地“喂狗”,即重新装载计数值,使计数器永远不会到达溢出点。如果程序跑飞、死循环、任务阻塞或中断被长期关闭,喂狗动作停止,计数器最终溢出,触发复位信号。
从参数角度看,看门狗定时器有几个关键量:
- 超时时间(Timeout):计数器从初值到溢出所需的时间,决定了系统能容忍多长时间的“失联”。
- 喂狗窗口:部分高级看门狗要求喂狗必须落在特定时间窗口内,过早或过晚都视为异常,可防止程序因跑飞而“疯狂喂狗”。
- 时钟源:可来自独立低速振荡器(如 32.768kHz)、内部 RC 或总线时钟。独立时钟源抗干扰能力更强。
- 复位输出:溢出后产生内部复位、外部引脚复位,或仅产生中断。
二、硬件看门狗与软件看门狗
按实现载体,看门狗可分为硬件看门狗和软件看门狗,两者在可靠性、成本和灵活性上差异明显。
硬件看门狗通常由芯片内部独立模块或外部专用芯片实现。它拥有独立时钟源和复位电路,即使 CPU 内核完全死锁,只要时钟仍在运行,它依然能够触发复位。外部看门狗芯片(如 MAX6369、TPS3813 等)还可通过 GPIO 喂狗,具备更强的抗软件失效能力。其缺点是增加成本、占用板级空间,且超时时间通常由硬件固定或有限可调。
软件看门狗则依赖系统中的一个定时器中断或任务来计数。它实现简单、超时时间灵活,但致命弱点是:如果系统关中断、调度器崩溃或该任务本身被阻塞,软件看门狗也会随之失效。因此,软件看门狗更适合作为“任务级健康检查”,而不能替代硬件看门狗承担最终复位职责。
| 对比维度 | 硬件看门狗 | 软件看门狗 |
|---|---|---|
| 时钟独立性 | 独立时钟源,可靠性高 | 依赖系统时钟/中断 |
| CPU 死锁时 | 仍可复位 | 通常失效 |
| 超时时间 | 固定或有限可调 | 灵活可配置 |
| 成本与复杂度 | 较高,需外部芯片或专用模块 | 低,仅需定时器资源 |
| 典型用途 | 最终安全复位 | 任务级健康监测 |
三、喂狗机制:不是“随便喂”
喂狗机制的设计质量,直接决定看门狗能否真正发现故障。常见的喂狗方式包括:
- 单点喂狗:在主循环中直接写寄存器。实现最简单,但若主循环中某段代码死循环,喂狗仍可能被执行,无法发现局部故障。
- 任务级喂狗:每个关键任务维护自己的“存活标志”,由监控任务统一检查后再喂狗。任一任务超时未更新标志,则停止喂狗,触发复位。
- 窗口喂狗:喂狗必须落在允许窗口内,防止程序跑飞后高频喂狗掩盖故障。
- 挑战-应答喂狗:看门狗给出随机数或序列,软件必须正确计算并回写,防止简单写固定值。
下面是一个任务级喂狗的伪代码示例,展示了“先检查、后喂狗”的基本逻辑:
// 各任务周期更新自己的心跳计数
volatile uint32_t task_a_heartbeat = 0;
volatile uint32_t task_b_heartbeat = 0;
void task_a(void) {
while (1) {
// 执行任务逻辑
task_a_heartbeat++;
delay_ms(10);
}
}
void watchdog_monitor(void) {
uint32_t last_a = 0, last_b = 0;
while (1) {
if (task_a_heartbeat == last_a ||
task_b_heartbeat == last_b) {
// 有任务未更新,停止喂狗,等待复位
while (1) { }
}
last_a = task_a_heartbeat;
last_b = task_b_heartbeat;
feed_watchdog(); // 所有任务健康,才喂狗
delay_ms(100);
}
}
四、复位策略:复位之后怎么办
看门狗触发后,系统通常面临几种复位策略选择:
- 整机复位:最彻底,重新初始化所有外设和任务,适合大多数失控场景。
- 部分复位:仅复位异常子系统,保留关键状态,适合高可用系统。
- 降级运行:关闭非关键功能,进入安全模式并告警,适合不能立即停机的场景。
- 复位原因记录:在复位前将看门狗标志、任务状态写入非易失存储,便于事后定位。
一个常见的误区是:看门狗复位后,系统直接重新初始化并继续运行,却不记录复位原因。这样会导致同一故障反复发生却无法定位。正确的做法是在启动时读取复位状态寄存器,区分上电复位、看门狗复位、外部复位等,并写入日志。
if (RSTSRC & WDT_RESET_FLAG) {
log_error("Reset caused by watchdog timeout");
save_crash_context();
}
clear_reset_flags();
五、典型场景与复盘要点
在工业控制器中,常见做法是:外部硬件看门狗负责最终复位,MCU 内部软件看门狗负责任务级监测,关键任务通过心跳标志向监控任务汇报。监控任务只有在所有心跳都正常时才翻转 GPIO 喂外部看门狗。这样,即使 RTOS 调度器崩溃,外部看门狗仍能复位系统。
复盘时建议重点检查以下清单:
- 看门狗时钟是否独立于 CPU 主时钟?
- 喂狗点是否覆盖所有关键任务,而非单一主循环?
- 超时时间是否大于最坏情况下的任务执行时间,又足够短以快速恢复?
- 是否启用了窗口喂狗或挑战-应答机制?
- 复位原因是否被记录并可读?
- 是否存在关中断时间过长导致喂狗被延迟的路径?
只有把定时器、喂狗机制和复位策略作为一个整体来设计,看门狗才能真正成为系统的“最后一道防线”,而不是一个被形式化绕过的摆设。