4、Bootloader启动阶段的看门狗策略:上电复位后的看门狗状态、初始化阶段喂狗时机、配置寄存器保护

好,咱们今天聊点实在的。Bootloader启动阶段,看门狗怎么伺候?这个问题,我当年刚入行时踩过不少坑。说白了,看门狗就是个脾气暴躁的保安——你如果不按时给它“打卡”,它就帮你重启整个系统。但在Bootloader启动这个节骨眼上,你既要依赖它防止程序跑飞,又不能让它过早地把你给“咬”了。

4.1 上电复位后的看门狗状态

芯片一上电,看门狗是什么状态?嗯,这里要注意:绝大多数MCU在上电复位后,看门狗默认是开启的。为什么?因为芯片厂商比你更怕死。万一你的代码在启动初期就跑飞了,没有看门狗兜底,系统就彻底挂了。

我记得有一次,我在一个STM32F4的项目上,刚焊好板子,下载了程序,结果发现系统每隔几百毫秒就重启一次。查了半天,原来是默认的独立看门狗(IWDG)在作祟。我当时那个郁闷啊——代码还没跑起来,就被狗咬了。

核心要点:上电复位后,看门狗通常处于以下状态:

  • 独立看门狗(IWDG):默认开启,由内部低速时钟(LSI)驱动,无法被软件关闭(除非硬件复位)
  • 窗口看门狗(WWDG):默认关闭,需要手动使能
  • 定时器模拟看门狗:默认关闭,由用户自行配置

你想想看,如果IWDG默认开启,而你的Bootloader初始化代码需要跑几百毫秒,那你就必须在超时之前喂狗。否则,系统就会陷入“启动→复位→再启动→再复位”的死循环。我见过一个同事,调试了整整一天,最后发现是看门狗超时时间设得太短,连初始化都跑不完。

4.2 初始化阶段喂狗时机

喂狗时机,说白了就是“什么时候该打卡”。我个人习惯把Bootloader的初始化阶段分成几个关键节点,每个节点之间都要考虑喂狗。

为什么会这样?因为初始化阶段最容易出问题。时钟配置、外设初始化、内存检测,每一步都可能卡住。如果看门狗超时时间设得紧,你还没跑到喂狗代码,系统就复位了。

我的建议:在初始化阶段,采用“分段喂狗”策略。

  1. 第一段:复位后立即喂狗——清除复位标志,确认看门狗状态
  2. 第二段:时钟初始化后喂狗——系统时钟稳定后,喂一次
  3. 第三段:外设初始化后喂狗——关键外设(如串口、Flash)配置完成后,再喂一次
  4. 第四段:跳转到主程序前喂狗——确保进入主循环前狗是饱的

我曾经在一个汽车电子项目中,Bootloader需要做完整的CRC校验,耗时可能超过2秒。如果看门狗超时只有1秒,那就麻烦了。我的做法是:在CRC校验循环中,每处理一个扇区就喂一次狗。这样既保证了校验的完整性,又不会被狗咬。

// 分段喂狗示例代码
void Bootloader_Init(void)
{
    // 1. 复位后立即喂狗
    IWDG_ReloadCounter();
    
    // 2. 初始化系统时钟
    SystemClock_Config();
    IWDG_ReloadCounter();  // 时钟稳定后喂狗
    
    // 3. 初始化串口和Flash
    UART_Init();
    Flash_Init();
    IWDG_ReloadCounter();  // 外设初始化后喂狗
    
    // 4. 执行CRC校验(耗时操作)
    for(uint32_t i = 0; i < SECTOR_COUNT; i++)
    {
        CRC_CheckSector(i);
        IWDG_ReloadCounter();  // 每处理一个扇区喂一次狗
    }
    
    // 5. 跳转到主程序前最后一次喂狗
    IWDG_ReloadCounter();
    JumpToApplication();
}

警告:不要在中断服务函数中喂狗!我曾经见过有人把喂狗放在SysTick中断里,结果主循环卡死了,中断还在跑,看门狗永远不超时。这种“假活”状态比死机更可怕。

4.3 配置寄存器保护

看门狗的配置寄存器,可不是你想改就能改的。芯片厂商为了防止程序跑飞后误操作看门狗,设置了重重保护。嗯,这里要讲点硬核的东西了。

以STM32的IWDG为例,它的配置寄存器(PR和RLR)是有写保护功能的。你需要先向密钥寄存器(IWDG_KR)写入0x5555,才能解锁这些寄存器。写入其他值,寄存器就锁死了。

寄存器 地址 功能 保护机制
IWDG_KR 0x40003000 密钥寄存器 写入0xCCCC启动看门狗,0xAAAA喂狗,0x5555解锁
IWDG_PR 0x40003004 预分频器配置 需要先解锁(IWDG_KR=0x5555)才能写入
IWDG_RLR 0x40003008 重装载值配置 需要先解锁(IWDG_KR=0x5555)才能写入
IWDG_SR 0x4000300C 状态寄存器 只读,指示PVU和RVU位

我个人习惯在配置看门狗时,遵循以下步骤:

  1. 先解锁:向IWDG_KR写入0x5555
  2. 等待就绪:检查IWDG_SR的PVU和RVU位,确保前一次操作完成
  3. 配置参数:写入PR和RLR寄存器
  4. 重新锁定:向IWDG_KR写入任意非0x5555的值(如0x0000)

避坑指南:我曾经在一个低功耗项目中,为了省电把LSI时钟关了,结果IWDG直接罢工。后来查手册才发现,IWDG的时钟源是独立的LSI,不能随便关。所以,配置寄存器之前,一定要确认时钟源是活的。

还有一个容易忽略的点:配置寄存器保护不仅仅是防止误写,更是为了防止程序跑飞后看门狗被恶意关闭。你想想看,如果看门狗配置寄存器可以被随意修改,那黑客或者程序错误就能轻松关掉看门狗,系统安全就无从谈起了。

所以,我的建议是:在Bootloader初始化完成后,主动锁定看门狗配置寄存器。这样即使后续代码跑飞,也无法修改看门狗的配置。嗯,说白了,就是“上了锁再把钥匙扔掉”。

// 配置寄存器保护示例
void IWDG_Config(uint32_t prescaler, uint32_t reload)
{
    // 1. 解锁寄存器
    IWDG->KR = 0x5555;
    
    // 2. 等待前一次操作完成
    while(IWDG->SR & (IWDG_SR_PVU | IWDG_SR_RVU));
    
    // 3. 配置预分频器和重装载值
    IWDG->PR = prescaler;
    IWDG->RLR = reload;
    
    // 4. 等待配置生效
    while(IWDG->SR & (IWDG_SR_PVU | IWDG_SR_RVU));
    
    // 5. 重新锁定寄存器(写入非0x5555的值)
    IWDG->KR = 0x0000;
    
    // 6. 启动看门狗
    IWDG->KR = 0xCCCC;
}

小技巧:如果你用的是窗口看门狗(WWDG),它的配置寄存器保护机制略有不同。WWDG的配置寄存器(CFR)在写入后会自动锁定,需要重新使能WWDG才能再次修改。所以,配置WWDG时,最好一次性把所有参数写对,别想着“先试试,不行再改”。

最后,总结一下Bootloader启动阶段的看门狗策略:

  • 上电复位后:确认看门狗状态,默认开启的IWDG要特别小心
  • 初始化阶段:分段喂狗,关键节点各喂一次,耗时操作循环喂
  • 配置寄存器:先解锁、再配置、后锁定,确保安全

嗯,这一章的内容就到这里。下一章我们会聊聊“看门狗超时时间的计算与选择”,到时候我会分享一个我实际项目中用过的计算公式,保证实用。