18、看门狗与低功耗模式:启动阶段的特殊处理

看门狗和低功耗模式,这两个东西在启动阶段特别容易出问题。我见过不少工程师,芯片刚上电就跑飞了,或者进低功耗就醒不过来——说白了,都是启动阶段的初始化没处理好。

今天咱们就聊聊,在系统刚启动那会儿,怎么伺候好看门狗,怎么安全地进入和退出低功耗模式。

18.1 看门狗:启动阶段的“定时炸弹”

看门狗(Watchdog Timer)是个好东西,能防止程序跑飞。但它在启动阶段,反而可能成为一颗定时炸弹。

为什么会这样?因为很多芯片上电后,看门狗默认就是开启的。你还没来得及喂狗,它就先把你复位了。

⚠️ 注意: 启动代码中,第一件事往往不是初始化时钟,而是先关掉看门狗,或者给它一个足够长的超时时间。

我个人习惯的做法是:在复位向量指向的第一条指令,就立刻禁用看门狗。比如在 ARM Cortex-M 上,启动文件里通常这样写:

; 启动文件 startup.s 片段
Reset_Handler:
    ; 第一步:关掉看门狗
    LDR     R0, =IWDG_BASE_ADDR
    LDR     R1, [R0, #IWDG_KR_OFFSET]
    MOV     R2, #0xCCCC          ; 解锁键值
    STR     R2, [R0, #IWDG_KR_OFFSET]
    MOV     R2, #0x0000          ; 禁用看门狗
    STR     R2, [R0, #IWDG_KR_OFFSET]

    ; 然后才初始化时钟、堆栈等
    BL      SystemInit
    BL      main

嗯,这里要注意:不同厂家的看门狗,解锁和禁用的方式完全不同。有的需要写特定序列,有的需要写寄存器。我建议你仔细看芯片手册,别想当然。

💡 小技巧: 如果芯片允许,可以在启动阶段把看门狗的超时时间设到最大(比如几秒),等系统初始化完成后再改回正常值。这样既不会误复位,又能保证安全。

18.2 低功耗模式:启动时的“沉睡陷阱”

低功耗模式,说白了就是让芯片睡觉。但启动阶段,芯片刚醒来,很多外设还没准备好,这时候如果贸然操作,很容易出问题。

我在项目中遇到过一件事:某次做物联网终端,芯片从深度睡眠中唤醒后,直接去读传感器数据,结果读回来全是0。查了半天才发现,是 ADC 还没稳定。

启动阶段处理低功耗模式,核心就三点:

  1. 判断唤醒源:是谁把芯片叫醒的?定时器?外部中断?还是复位?
  2. 恢复时钟和外设:低功耗模式下,很多时钟和外设都被关了,需要重新初始化。
  3. 清理唤醒标志:不清除的话,下次可能误判。

看个例子,这是我从深度睡眠中唤醒后的处理流程:

void SystemWakeupHandler(void)
{
    uint32_t wakeup_source;

    // 1. 读取唤醒源寄存器
    wakeup_source = RTC->ISR & RTC_ISR_WUTF;

    // 2. 恢复系统时钟
    SystemClock_Config();   // 重新配置PLL、HSE等

    // 3. 重新初始化外设
    UART_Init();
    I2C_Init();
    ADC_Init();

    // 4. 等待外设稳定
    Delay_ms(10);           // 给ADC、晶振一点时间

    // 5. 清除唤醒标志
    RTC->ISR &= ~RTC_ISR_WUTF;

    // 6. 根据唤醒源做不同处理
    if (wakeup_source & RTC_ISR_WUTF) {
        // 定时唤醒:采集数据
        Sensor_Read();
    } else {
        // 外部中断唤醒:处理事件
        Event_Process();
    }
}
🔑 关键点: 唤醒后,不要假设任何外设还处于可用状态。时钟、GPIO、DMA 等,统统重新初始化一遍,才是最稳妥的做法。

18.3 看门狗与低功耗的“相爱相杀”

看门狗和低功耗模式,有时候会互相打架。你想想看:芯片进入低功耗后,CPU 停了,看门狗还在跑吗?

这取决于芯片设计。有些芯片的看门狗在低功耗模式下会自动暂停,有些则不会。如果看门狗还在跑,而你又没喂狗,那它就会在芯片睡着的时候,把芯片复位。

我曾经踩过这个坑:一个电池供电的设备,每隔几分钟唤醒一次采集数据。结果发现设备总是莫名其妙重启。查了三天,才发现是看门狗在睡眠期间超时了。

解决方案有两种:

方案 做法 适用场景
方案一:睡眠前禁用看门狗 进入低功耗前关掉看门狗,唤醒后再重新开启 睡眠时间不确定,或芯片不支持看门狗自动暂停
方案二:使用独立看门狗(IWDG) IWDG 使用独立时钟,不受系统时钟影响,可在睡眠中继续运行 需要高可靠性,防止睡眠期间死机

我个人更倾向于方案二。因为方案一有个风险:如果你忘了在唤醒后重新开启看门狗,那系统就处于无保护状态了。而 IWDG 只要配置好,它自己会一直跑,你只需要在唤醒后及时喂狗就行。

18.4 启动阶段的“黄金窗口”

启动阶段,从复位到 main 函数执行,这段时间我称之为“黄金窗口”。

为什么叫黄金窗口?因为这段时间里,系统还没完全初始化,很多中断还没开启,外设还没配置。你可以在此时做一些特殊操作,比如:

  • 检查复位原因(是上电复位、看门狗复位,还是低功耗唤醒?)
  • 根据复位原因,决定是否跳过某些初始化步骤
  • 记录复位次数,用于故障诊断

看个实际例子:

void CheckResetCause(void)
{
    uint32_t reset_cause = RCC->CSR;

    if (reset_cause & RCC_CSR_WDGRSTF) {
        // 看门狗复位:记录错误,进入安全模式
        ErrorLog_Write("Watchdog reset occurred");
        EnterSafeMode();
    } else if (reset_cause & RCC_CSR_SFTRSTF) {
        // 软件复位:正常启动
        NormalBoot();
    } else if (reset_cause & RCC_CSR_PORRSTF) {
        // 上电复位:完整初始化
        FullInit();
    } else if (reset_cause & RCC_CSR_PINRSTF) {
        // 外部复位引脚:可能是低功耗唤醒
        WakeupFromSleep();
    }

    // 清除复位标志,避免下次误判
    RCC->CSR |= RCC_CSR_RMVF;
}
💡 经验之谈: 我习惯在启动代码里加一个“复位计数器”。每次复位都加1,存到备份寄存器里。这样如果设备频繁复位,就能通过这个计数器快速定位问题。

18.5 知识体系总览

说了这么多,咱们用一张图来总结一下启动阶段看门狗和低功耗模式的处理逻辑:

启动阶段:看门狗与低功耗处理流程 系统复位 / 唤醒 第一步:禁用看门狗 或 设置超长超时 第二步:读取复位原因寄存器 唤醒源? 低功耗唤醒 恢复时钟 & 外设 上电/看门狗复位 完整初始化 进入 main(),开启看门狗

这张图把整个流程串起来了。你想想看,从复位开始,先关看门狗,再判断唤醒源,然后根据情况做不同处理,最后才进入 main 函数。每一步都有它的道理。

📌 总结一下:
  • 看门狗在启动阶段要优先处理,别让它成为绊脚石
  • 低功耗唤醒后,不要假设任何外设还活着,全部重新初始化
  • 看门狗和低功耗要协调好,避免睡眠期间被复位
  • 利用启动阶段的“黄金窗口”,做复位原因分析和故障诊断

嗯,这些经验都是我用实际项目换来的。希望你能少走一些弯路。


公众号:蓝海资料掘金营,微信 deep3321