几年前我调试一台工业控制器,现场反馈设备偶尔死机,必须断电重启才能恢复。程序逻辑翻来覆去查了好几遍,最后才发现是主循环里一个阻塞延时写得过长,加上外部干扰触发了异常,系统又没有自恢复机制。那次之后,我养成一个习惯:正式项目的第一版固件,就把看门狗加上。
看门狗(WDT,Watchdog Timer)在嵌入式开发里属于“平时没人想起来,出事时救命”的模块。这篇文章想用一篇的篇幅,把看门狗的原理、类型、应用场景,以及新手最容易踩的配置坑讲清楚。同时我会结合最近被问得很多的 NSUC1612E 看门狗配置为例,给出一套可以直接落地的操作思路。无论你是刚接触 MCU 开发,还是已经写过几年固件但一直没认真研究过看门狗,这篇都值得花十分钟读完。
1. 看门狗到底管什么用:一次“跑飞”事故让我决定认真对待它
1.1 从 MCU 跑飞说起:为什么主循环卡住比崩溃更可怕
很多新手会把“死机”想象成程序直接崩掉、什么都不跑了。但实际嵌入式系统里更常见的“死机”分为好几种,最坑的是“假死”:
- 主循环里进入了一个死循环,但中断还在正常响应;
- 某段代码因为栈溢出或指针错乱,跳到一个随机地址反复执行;
- 外设初始化失败,程序卡在 while 等待标志位,而这个标志位永远不会置位;
- 硬件受到电磁干扰,程序计数器被改写,跑飞后不知在哪个代码段转悠。
这些情况的共同点是:系统不是完全没电,也不是所有外设都停摆,而是“该干活的逻辑已经不干了”。如果一个人得盯着设备手动重启,那就完全违背了嵌入式系统应该 7×24 小时自主工作的意义。
看门狗解决的就是这个问题:它像一个不近人情的监工,要求主程序每隔一段时间去“打卡报到”,如果超过期限没来,就直接把系统复位。
注意:看门狗不是用来“防止”死机的,而是用来“检测到死机后自动恢复”的。它治标不治本,但能让设备在无人干预的情况下爬起来继续干活。
1.2 看门狗的工作逻辑:一个倒计时器加一根“狗绳”
看门狗本质上就是一个递减计数器。它正常工作的逻辑可以用三句话概括:
- 计数器从某个初值开始,一直往下数,数到 0 就触发系统复位;
- 程序在计数器还没数到 0 的时候,主动“喂狗”(重新装载初值),计数器会回到初值重新开始数;
- 如果程序跑飞或卡死,没人去喂狗,计数器就会数到 0,系统被强制复位。
这里的“喂狗”动作,专业术语叫“清狗”或者“重装载”(Reload)。不同芯片叫法不一样,但本质都一样:往一个寄存器写入特定值,让计数器重新从头开始计时。
如果你用过微波炉,可以这么理解:看门狗就像微波炉定时器,你拧到 30 秒,如果 30 秒内不再拧一下,它就“叮”一声触发动作。程序就是那个每隔几秒去拧一下的人。
1.3 喂狗动作的实质:告诉系统“我还活着”
喂狗这个动作看似简单,但在工程上有一个容易被忽略的深层含义:喂狗不仅是“刷新计数器”,更是“向上报平安”。
这句话往深了讲,其实是喂狗策略的设计核心。理想情况下,喂狗代码应该放在主循环里,这样只要主循环还能正常跑完一圈,就说明大部分核心逻辑是健康的。如果把喂狗放在一个最高优先级的中断里,那主循环哪怕已经卡死,中断照样能触发喂狗,看门狗就形同虚设。
所以新手入门看门狗,第一件事不是急着写代码,而是建立起一个观念:看门狗检查的不是“某个中断有没有跑”,而是“你的核心业务逻辑还正不正常”。后面我讲 NSUC1612E 配置和喂狗策略时,都会反复回到这个观念上。
2. 三种看门狗形态:内部独立、窗口、外置芯片怎么选
2.1 内部独立看门狗(IWDG):最省事但不够精细
大多数 MCU 内部都会集成独立看门狗,常见叫法有 IWDG(Independent Watchdog)、WDT、WWDG 等。这里先讲“独立看门狗”。
所谓“独立”,指的是它拥有独立的时钟源,通常是一个内部的低速 RC 振荡器(比如 40kHz 左右),不依赖主时钟。这样设计的好处是:即使主时钟因为外部晶振失效而挂了,独立看门狗依然能在跑,依然能超时复位系统。
独立看门狗的特点:
- 配置简单,通常就是设置分频系数、重装载值、使能,三步;
- 超时时间范围大,可以从几十微秒到几秒甚至更长;
- 一旦开启,很多芯片上无法软件关闭,必须复位后才能停止;
- 喂狗精度要求不高,只要在超时之前喂一次就行。
它适合大多数“不需要太精细”的场景,比如普通消费电子、家电控制板、工业采集节点。我自己的习惯是:只要项目没有特殊需求,默认就用内部独立看门狗,简单可靠。
2.2 窗口看门狗(WWDG):既怕死机也怕“疯跑”
窗口看门狗和独立看门狗最大的区别是:它不仅要求“不能太晚喂狗”,还要求“不能太早喂狗”。喂狗必须落在某个时间窗口内,太早和太晚都会复位。
为什么会有这种设计?因为独立看门狗存在一个漏洞:如果程序跑飞后,恰好在一个循环里不断执行“喂狗”指令,计数器永远到不了 0,看门狗就失效了。窗口看门狗则要求喂狗必须发生在窗口期内,程序如果跑飞后乱喂,很容易提前喂,同样触发复位,保护效果更强。
窗口看门狗的特点:
- 喂狗窗口的上限、下限都需要配置;
- 对喂狗时序要求更严格,对程序实时性要求高;
- 适合汽车电子、医疗设备等对安全要求更高的场景。
但新手要注意:窗口看门狗配置稍微复杂,而且如果在中断里或主循环里喂狗时机不对,反而会频繁复位。所以我的建议是,入门阶段先掌握独立看门狗,等理解了喂狗时序再上窗口看门狗。
2.3 外置硬件看门狗:主控挂了它还在
除了 MCU 内部看门狗,还有一些项目会用外置看门狗芯片,比如 MAX6369、CAT823 这类专用芯片,或者利用外部定时器电路实现。它们和 MCU 的关系是:MCU 通过一个 GPIO 引脚定期“喂”它,如果超时,它就拉一下 MCU 的复位引脚,或者直接切断、重启供电。
外置硬件看门狗的价值在于:
- MCU 完全死掉、电源异常时,它依然独立工作;
- 可以检测 MCU 的供电是否正常,电源跌落时主动复位;
- 喂狗失败时,可以直接控制电源通断,实现“断电重启”,比单纯拉复位引脚更彻底。
它也带来额外成本:多一颗芯片、多一个 GPIO、多一份驱动代码,PCB 布局也要考虑。所以一般工业控制、通信设备、户外设备用得比较多,普通小家电反而不太需要。
2.4 纯软件看门狗:能用但别太依赖
还有一种“软件看门狗”,严格来说不是硬件模块,而是利用一个定时器中断和一个计数变量模拟出来的:定时器中断每次给某个变量加一,主程序每次清掉这个变量,如果变量超过阈值就复位。
它的优点是灵活,可以同时监控多个任务;缺点是如果 MCU 本身死机、时钟停摆,软件看门狗也跟着失效。所以它只能作为辅助手段,不能在关键安全场合替代硬件看门狗。
总结一下几种形态的适用场景,我做了一个表:
| 看门狗形态 | 保护对象 | 优点 | 缺点 | 典型场景 |
|---|---|---|---|---|
| 内部独立看门狗 | MCU 跑飞/死循环 | 配置简单,独立时钟 | 无法防“故意乱喂” | 消费电子、家电、工业控制 |
| 窗口看门狗 | MCU 跑飞/乱执行 | 防提前喂狗 | 配置复杂,时序要求高 | 汽车、医疗、安全关键设备 |
| 外置硬件看门狗 | MCU 完全失效/电源异常 | 彻底断电重启 | 成本高,占用 GPIO | 通信设备、户外设备 |
| 软件看门狗 | 任务级健康检查 | 灵活,可多任务 | 依赖 MCU 正常运行 | 复杂 RTOS 系统辅助监控 |
3. 新手最该搞懂的 4 个参数与选型误区
3.1 超时时间:系统响应最坏情况说了算
看门狗的超时时间怎么定?很多新手喜欢拍脑袋定一个 1 秒,结果产品一出问题就疯狂复位。正确做法是先分析系统“最长多久必须被喂一次狗”。
公式其实很简单:
超时时间 = 计数器初值 × 分频系数 / 看门狗时钟频率
举个例子,假设看门狗时钟频率是 40kHz,分频系数是 32,重装载值是 1000,那超时时间就是:
1000 × 32 / 40000 = 0.8 秒
这意味着如果 0.8 秒内没有任何喂狗动作,系统复位。
但工程上不能直接把这个计算值当成“喂狗周期”。你要留出足够的余量,因为系统在最坏情况下可能一段时间内确实无法喂狗。常见的阻赛场景包括:
- 往外部 EEPROM/Flash 里写一页数据,如果写保护没处理好,擦写时间可能几百毫秒;
- 通信模块重发、等待 ACK,最坏情况下可能卡一个完整的超时周期;
- RTOS 中有低优先级任务运行,高优先级任务持续占用 CPU,低优先级喂狗任务迟迟得不到调度;
- 进入低功耗模式后,某些 MCU 的 RC 振荡器精度会下降,时间可能偏长。
我通常的做法是:先找出所有可能阻塞喂狗的场景,统计最长的阻塞时间,然后乘以 2 到 3 作为超时时间。太少容易误复位,太多则失去及时恢复的意义。
3.2 喂狗窗口与时钟精度:余量不是越大越好
窗口看门狗有一个“窗口上限”,也就是最早允许喂狗的时间点。如果你过早喂狗,同样会触发复位。它的本质是防止系统在异常状态下“意外”喂狗。
这里新手最容易犯的错是:为了安全,把窗口开得特别宽,结果窗口的上限和下限之间几乎覆盖了所有时间,那窗口看门狗和普通看门狗就没区别了。
设计喂狗窗口时要考虑两个因素:
- 主循环最差情况下的循环周期;
- 时钟源的精度。
大多数 MCU 的内部 RC 振荡器精度在室温下能到正负百分之几,但在高低温下误差会增大。你按典型值算出来的窗口,在高温下可能整体偏移,导致喂狗点落在窗口外。所以窗口的两边至少留 20% 以上余量,不能卡着边界设计。
3.3 掉电与低功耗模式下的看门狗行为
低功耗场景是新手最容易忽略的坑。很多 MCU 进入 Stop/Standby 模式后,看门狗可能继续运行,也可能停止,具体要看手册。如果看门狗继续运行,而你进入低功耗的时间又超过超时时间,那每次进低功耗都会被复位一次,表现为“一睡觉就重启”。
处理方式通常有三种:
- 进入低功耗之前先禁狗(如果芯片支持);
- 进入低功耗之前,把超时时间临时调到大于低功耗持续时长;
- 从低功耗唤醒后立刻喂狗,但要注意:如果低功耗时长大于超时时间,唤醒喂狗也来不及,需要在唤醒中断里第一件事就是喂狗。
还有一种更稳妥的做法:使用外置看门狗芯片,它本身就支持“low power”模式,进入休眠前通过外部引脚暂停喂狗,或者把看门狗超时时间调得很长。这个在设计阶段就要想好,等 PCB 做出来再改就麻烦了。
3.4 应用场景参考表与常见误区
不同行业对看门狗的需求差异很大,我根据自己的经验整理了一个参考表:
| 场景 | 建议形式 | 超时时间参考 | 喂狗方式 |
|---|---|---|---|
| 简单小家电 | 内部独立看门狗 | 1-2 秒 | 主循环喂 |
| 工业控制板 | 内部独立看门狗 + 软件任务监控 | 200-500ms | 主循环+关键任务标志 |
| 汽车电子 | 窗口看门狗 | 10-100ms | 中断喂狗+窗口策略 |
| 通信基站/户外设备 | 外置硬件看门狗 | 1-10 秒 | GPIO 翻转 |
| 低功耗传感节点 | 内部看门狗+休眠管理 | 1-30 秒 | 唤醒后补充喂狗 |
新手常见的几个误区,我一起列出来:
- 误区一:把看门狗当 debug 工具,调试时忘了关,结果程序一直复位;
- 误区二:在主循环开头喂狗,万一主循环后面卡死,这一次喂狗撑不到复位,系统保护就失效;
- 误区三:在最高优先级定时器中断里喂狗,主循环卡死也不复位;
- 误区四:看门狗超时时间设得太短,导致正常运行时偶尔复位,最后为了“解决问题”直接把看门狗关掉。
这些误区我都会在第 6 章展开讲排查方法,先记住一句话:看门狗是系统安全机制,不是普通外设,配置前一定要想清楚保护边界。
4. 以 NSUC1612E 为例,从零配置看门狗全流程
4.1 先把时钟树摸清楚:超时时间计算的起点
最近不少人在问 NSUC1612E 的看门狗配置。我不清楚具体是哪个系列,但这类 MCU 看门狗的使用逻辑基本一致:先选时钟源,再分频,再装载重载值,最后使能。
NSUC1612E 这类芯片的看门狗通常支持独立时钟源或者来自系统时钟的分频时钟。配置前第一件事是打开参考手册,找到 WDT(看门狗)部分的“Clock Source”小节,确认你用的时钟频率是多少。内部 RC 在 25℃ 下可能是 40kHz,但温度变化后频率会漂,这个值直接参与超时时间计算。
我这里用示意代码来说明,寄存器名采用常见命名方式,实际项目里请以 NSUC1612E 参考手册为准:
#define WDT_FCLK 40000u // 看门狗内部时钟 40kHz,假设值 #define WDT_PRESCALER 32u // 分频系数 #define WDT_TARGET_MS 800u // 目标超时时间 800ms // 计算重装载值:reload = (target_s * fclk) / prescaler - 1 uint32_t reload = (WDT_TARGET_MS * (WDT_FCLK / 1000u)) / WDT_PRESCALER - 1u;注意这里为什么要减 1:大部分看门狗计数器是从装载值递减到 0,需要再过一个时钟周期才产生复位,所以实际超时时间会比理论值多一个周期。虽然影响很小,但新手如果能养成这个习惯,说明你真的理解计数器的行为。
4.2 初始化寄存器:一次配置,别中途乱改
NSUC1612E 这类芯片的看门狗初始化流程,一般可以归结为下面几步:
- 如果芯片有写保护,先解锁 WDT 相关寄存器;
- 设置看门狗模式:复位模式或中断模式(大多数场景用复位模式);
- 配置分频系数;
- 写入重装载值;
- 使能看门狗;
- 使用后立刻喂一次狗,确保装载值生效。
示意代码:
void wdt_init(uint32_t reload) { wdt_unlock(); // 1. 解锁写保护,具体函数看库 wdt_set_mode(WDT_MODE_RESET); // 2. 复位模式 wdt_set_prescaler(WDT_PRESCALER); // 3. 分频 wdt_set_reload(reload); // 4. 写重装载值 wdt_enable(); // 5. 使能 wdt_feed(); // 6. 先喂一次,确保启动后从完整值开始计数 } void wdt_feed(void) { wdt_clear_flag(); // 清中断/超时标志 wdt_reload(); // 执行重装载 }这里有个容易被忽略的细节:很多芯片在“初始化之前”和“使能之后”都需要喂狗,尤其有些看门狗使能后会在极短时间内复位,比如分频还没生效,计数器已经开始跑了。所以初始化函数的最后一步往往要立刻喂一次,这样保证系统从已知状态开始。
另外,看门狗寄存器一旦锁定,运行中就不要频繁去改。真要改超时时间,先确认芯片支持“窗口内修改”还是“必须先禁狗再改”,否则可能触发意外复位。
4.3 喂狗代码该放在哪里:前后台与中断的博弈
配置完看门狗之后,新手问得最多的问题就是:喂狗代码到底写哪?
我们先分两种情况看。
情况一:前后台架构(裸机)
最简单的做法是在主循环 while(1) 里喂狗:
int main(void) { sys_init(); wdt_init(...); while (1) { task_a(); task_b(); wdt_feed(); // 主循环所有必要任务执行完后喂狗 } }这种写法有一个隐含要求:主循环里每个任务的单次最大执行时间必须小于超时时间。如果 task_a 里有一个阻塞等待外部设备响应的函数,最坏情况下等了 600ms,而超时时间是 800ms,那勉强能过;但如果两个任务叠加超过 800ms,系统就会误复位。
所以复杂一点的项目,我会把喂狗放到主循环中段,并且在喂狗之前检查几个关键模块的健康状态。
情况二:RTOS 架构
RTOS 下我一般不提倡单独开一个最高优先级“喂狗任务”,因为这样会产生“影子保护”问题——其他任务全卡死了,喂狗任务照样跑。更合理的做法是:
- 每个关键任务周期内更新自己的“心跳标志”;
- 一个中等优先级的监控任务统一检查所有心跳,如果都正常,则喂狗;
- 一旦某个任务心跳超时,不喂狗,让看门狗复位。
这样做的好处是看门狗保护的粒度更细,坏处是逻辑复杂度上升。新手如果第一次在 RTOS 里加看门狗,可以先从“主循环任务喂狗”开始,跑稳了再升级成多任务心跳。
4.4 示波器验证复位行为:配置完必须做的检查
看门狗配置完不是“感觉差不多就行”,一定要实测复位行为。我在验证时通常会做这几件事:
- 初始配置后,先不喂狗,观察复位时间是否接近设定值;
- 用示波器同时抓复位引脚和一个 GPIO 翻转信号,GPIO 每 200ms 翻转一次,看复位周期是否与理论一致;
- 人为卡死主循环(比如加一个 while(1);),确认系统能在预期时间内复位;
- 正常运行时长期通电跑,确认没有误复位。
如果是 NSUC1612E 这类 MCU,复位引脚输出低电平的时间一般在几百微秒到几毫秒,用示波器抓一下就能看到。如果发现复位周期和设定值偏差很大,优先检查分频配置和时钟源是否选对。
注意:验证看门狗时,最好用独立的电源和下载器,不要把调试器和被测板共地混乱。否则复位瞬间可能会把调试器也带崩。
5. 实际项目里喂狗策略怎么设计才不容易翻车
5.1 主循环喂狗是默认方案,但别忽视耦合风险
对很多小项目来说,主循环末尾喂狗是性价比最高的方案。它最简单的逻辑是:“这一圈代码能跑到末尾,说明前面所有代码都通过了”。
但这个方案有个隐藏风险:主循环里的代码和喂狗动作耦合太深。只要有人往主循环里加了一个阻塞等待,或者一个长时间运算,整个系统的看门狗时序就被破坏了。我见过不少产品,量产之后出现随机重启,最后定位到是某个传感器在异常情况下读数据会阻塞几十毫秒,把看门狗超时时间顶爆了。
所以用主循环喂狗时,我有几个原则:
- 所有可能长时间阻塞的操作,统一封装成带超时的等待函数;
- 主循环中尽量不要放超过超时时间 1/3 的阻塞逻辑;
- 在喂狗函数里顺便做一个“系统负载”统计,比如记录两次喂狗的最大时间间隔,调试时通过串口打出来。
这样即使以后有人改了主循环,也能尽早发现问题。
5.2 RTOS 场景:喂狗任务优先级与调度抖动
RTOS 里喂狗最大的挑战是“调度抖动”。你以为喂狗任务每隔 100ms 跑一次,但在高优先级任务持续占用 CPU 的情况下,低优先级任务可能被饿死,导致看门狗误复位。
我有一次在 FreeRTOS 项目里遇到看门狗不断复位,查了很久才发现是有个高优先级任务里做了 Flash 擦写,期间禁止了调度器,擦写时间超过了看门狗超时时间的一半。解决方法不是调高喂狗任务优先级,而是把 Flash 擦写做成更小的分片,并且在不影响功能的情况下允许调度器切走。
如果你非要用一个独立任务喂狗,我建议:
- 喂狗任务优先级不要设成最高;
- 喂狗任务里检查其他关键任务的心跳标志;
- 喂狗间隔不要卡着超时时间,至少留 50% 余量;
- 如果系统支持,在进入临界区(关中断)之前先喂一次狗,防止长时间关中断导致超时。
5.3 低功耗唤醒后的喂狗时序:来不及喂怎么办
低功耗设备和看门狗结合时,最容易出现的问题是唤醒后时钟还没稳定、程序还没来得及喂狗,看门狗就已经复位了。尤其是在使用外部晶振的高精度场景里,唤醒到时钟稳定可能需要几个毫秒,如果看门狗在低功耗模式下持续运行,这几个毫秒可能就决定了生死。
建议的做法是:
- 在进入低功耗之前,先喂狗,并且把看门狗的重装载值调大;
- 唤醒中断里,优先级最高的一件事就是喂狗,然后再做其他初始化;
- 如果芯片支持“调试模式/低功耗模式下暂停看门狗”,确认这个配置在生产固件里是关闭的。
我之前做过一个 NB-IoT 终端,每 10 分钟休眠一次,休眠时长 300 秒,但看门狗最大只有 128 秒。最后方案是在休眠前关闭看门狗,唤醒后重新初始化。这里有个前提:芯片允许在运行中关闭看门狗。如果你的芯片不支持,那只能选外置看门狗,或者把休眠拆成多段,每段都短于看门狗超时时间。
5.4 多模块协同:把“健康检查”做进喂狗流程
当系统模块比较多时,单纯在主循环里喂狗已经不够了。比如一个设备有通信模块、传感器采集模块、存储模块,如果通信模块卡死,但主循环其他代码还能跑,那喂狗照样正常,问题永远不会被发现。
这时候我习惯在喂狗前做一次“健康检查表”,大概的逻辑是这样的:
bool check_all_tasks(void) { if (!sensor_task_alive()) return false; if (!comm_task_alive()) return false; if (!storage_task_alive())return false; return true; } void main_loop_feed(void) { if (check_all_tasks()) { wdt_feed(); } }每个任务需要在一个周期内更新自己的 alive 标志,比如把“最近一次运行的时间戳”记下来。检查时如果发现时间戳超过阈值,说明该任务异常,看门狗就会被“故意饿死”,系统复位。
这种方式的优势是保护粒度细;劣势是需要维护一张心跳表。但如果你做的是带多个功能模块的产品,这个成本很值得。我在实际项目中通常把心跳表和调试信息绑定:每个任务写完心跳后,顺手把一个全局变量加一,串口异常时能直接看到是哪个任务卡了,对排错非常帮助。
6. 调试现场复盘:几个坑,每个都是真实踩出来的
6.1 调试器暂停导致看门狗复位,程序永远停在复位向量
这是新手最常遇到的第一个坑:程序烧进去能跑,但一连接调试器,设了断点,一暂停程序就复位,甚至复位后 PC 停在复位向量,怎么都调不好。
原因很简单:调试器暂停目标 CPU 后,计数器照样在跑,一旦超过超时时间,看门狗就复位了。如果是单步调试,由于步进时间可能超过超时时间,同样会触发。
解决办法有几个:
- 在调试配置里关闭“硬件看门狗”,只保留软件看门狗逻辑;
- 在初始化看门狗处加一个编译宏,只有 release 版本才真正使能;
- 使用支持“调试时暂停外设”的调试器特性,但需要在代码里配合设置调试寄存器;
- 如果必须在线调试,把超时时间临时改大到 30 秒,调试完再改回来。
我自己的习惯是:看门狗初始化代码放在一个单独的函数里,函数开头判断一个宏,比如#ifndef DEBUG_DISABLE_WDT,调试构建直接屏蔽掉。这样不会影响 release 的安全性。
6.2 中断里喂狗:表面正常,实际已失去保护
我之前接手过一个项目,现象是“看门狗没起效,程序卡死不复位”。打开代码一看,喂狗动作写在 SysTick 中断里,每 1ms 一次。这样就算主循环完全卡死,SysTick 中断依然会触发,依然会喂狗,看门狗永远等不到超时。
解决方案是把喂狗逻辑从 SysTick 中移除,改成在主循环或任务心跳中喂狗。如果担心主循环卡死时系统没有任何提示,可以在中断里维护一个“主循环运行计数”,主循环每次运行将计数递增。喂狗函数只有在计数有变化时才执行。
这类问题最大的难点不是技术,而是“看不出毛病”:代码看起来一直在跑,看门狗也配了,但实际上保护已经失效。所以我在代码评审时,看到中断里喂狗基本会直接标红。
6.3 超时时间与启动时间不匹配:上电就疯狂重启
另一种常见现象是上电后系统不断重启,看门狗复位周期极其规律,但程序逻辑看着也没问题。这种情况十有八九是看门狗在系统初始化早期就已经被使能,而初始化完成需要的时间超过了超时时间,导致还没跑到喂狗代码,看门狗就复位了。
比如有些芯片在复位后看门狗默认就是开启的,你还没来得及配置,它已经用默认值开始计数。如果默认超时时间是几百毫秒,而你的初始化流程需要几百毫秒甚至一秒,那程序永远起不来。
处理办法:
- 在启动代码最前面尽快喂一次狗,让系统先“续命”;
- 先把超时时间配置成较长的值,等所有初始化完成后再重配成最终值;
- 查看芯片手册,如果支持,在初始化开始前先禁用看门狗,最后再开启。
这个坑在换新芯片时特别容易踩,因为不同芯片复位后看门狗的默认状态不一样,有的关,有的开。
6.4 排查链路:从复位标志位到喂狗点,一步步来
如果遇到“看门狗引起的异常复位”,我一般按下面的链路排查:
查复位标志位:很多 MCU 都有 RCC_CSR 或者 RST_STAT 寄存器,记录上一次复位原因。如果是看门狗复位,标志位会置位。这一步最快,能确认问题方向。
量复位引脚波形:用示波器抓复位引脚,看是不是周期性低电平。如果是,基本确认看门狗在不停复位。
区分“初始化阶段复位”还是“运行阶段复位”:在串口启动日志里加几个关键打印点,看系统能跑到哪一步。如果卡在初始化早期,说明超时时间太短;如果能跑到主循环但过一会复位,说明喂狗策略问题。
临时屏蔽喂狗:屏蔽所有喂狗代码,看复位周期是否变成确定性的超时周期。如果变成固定周期,说明看门狗配置是生效的,问题在喂狗逻辑。
检查喂狗路径:梳理喂狗代码的调用路径,确认没有在中断里喂狗,确认主循环中不存在超长阻塞,确认低功耗唤醒后第一时间补喂。
使用 GPIO 记录时间戳:在喂狗函数入口翻转一个 GPIO,用示波器测量两次翻转的时间间隔。如果大部分时间间隔稳定,但偶尔有一次接近或超过超时时间,那问题大概率出在那个异常点所在的任务里。
这套链路我用了很多年,基本能覆盖 90% 的看门狗问题。新手遇到“莫名重启”,别急着怀疑硬件,先把复位标志位打出来,往往能少走很多弯路。
最后分享一个我自己的习惯:每次建项目时,我都会在启动代码里提前把看门狗的“喂狗预留位”留好,哪怕是空函数,也先留一个 wdt_feed() 的接口。后续每个模块开发时都会看到这个函数,慢慢就形成了“喂狗是系统能力的一部分”的意识,而不是等出了问题才想起来补。看门狗这东西,配置本身五分钟就能搞定,但喂狗策略和调试经验,是靠一个个现场问题喂出来的。希望这篇文章能帮你把基础打牢,少踩几个我当年踩过的坑。