4、产测固件与Bootloader设计:产测模式与正常模式切换、Bootloader跳转逻辑、固件签名与安全校验、看门狗策略

各位工程师朋友,咱们接着聊。上一节我们把产测硬件方案敲定了,这一节轮到固件层面最核心的部分——产测固件与Bootloader设计

说实话,这块儿是量产测试里最容易出幺蛾子的地方。我见过太多团队,硬件设计得漂漂亮亮,结果固件切换逻辑没处理好,产线上刷完机就跑不起来,最后整批返工。嗯,咱们今天就把这些坑一个个填上。

4.1 产测模式与正常模式切换

先说说最基础的问题:设备怎么知道自己该进产测模式,还是正常启动?

我个人习惯用硬件引脚检测 + 标志位组合判断。说白了,就是上电瞬间先读一个GPIO电平,再查一下Flash里有没有预设标志。

推荐方案:

  • GPIO检测:产线治具上拉/下拉一个测试引脚。比如把PA0拉低,Bootloader就进产测模式。
  • Flash标志位:在Flash末尾存一个4字节魔数。如果魔数有效,说明固件已烧录完成,可以正常启动。
  • 组合逻辑:GPIO为低 且 Flash无有效标志 → 进产测模式;GPIO为高 或 Flash有标志 → 正常启动。

你想想看,为什么不用单一判断?我曾经踩过这个坑——只靠GPIO判断,结果产线工人不小心碰了一下治具,设备直接进产测模式,整条线停了半小时。加个Flash标志位做双重保险,稳得多。

代码实现大概长这样:

// 伪代码示例
uint8_t check_boot_mode(void) {
    uint32_t flash_flag = *(volatile uint32_t*)FLAG_ADDR;
    uint8_t gpio_level = HAL_GPIO_ReadPin(TEST_PIN);
    
    if (gpio_level == 0 && flash_flag != MAGIC_NUM) {
        return BOOT_MODE_TEST;   // 进产测
    } else {
        return BOOT_MODE_NORMAL; // 正常启动
    }
}

4.2 Bootloader跳转逻辑

模式判断完了,接下来就是跳转。这里有个关键点:跳转前必须把外设清理干净

为什么?我举个例子。你在Bootloader里初始化了串口、定时器,然后直接跳转到APP。APP启动时也初始化串口,结果发现波特率不对——因为Bootloader没关外设时钟,寄存器状态乱掉了。

注意:跳转前必须做三件事:

  1. 关闭所有外设中断
  2. 恢复外设寄存器到默认值
  3. 失能系统全局中断

跳转后,APP的启动代码会重新初始化一切。

跳转代码其实很简洁,但细节决定成败:

void jump_to_app(uint32_t app_addr) {
    // 1. 关闭中断
    __disable_irq();
    
    // 2. 清理外设(根据芯片不同实现)
    deinit_all_peripherals();
    
    // 3. 设置主堆栈指针
    __set_MSP(*(volatile uint32_t*)app_addr);
    
    // 4. 跳转到复位向量
    void (*app_reset)(void) = (void (*)(void))*(volatile uint32_t*)(app_addr + 4);
    app_reset();
    
    // 5. 永远不会执行到这里
}

这里有个小技巧:跳转前把RCC寄存器全部复位。我遇到过某款国产MCU,不关RCC时钟,APP里PWM死活不工作。查了两天才发现是Bootloader遗留的时钟配置在捣乱。

4.3 固件签名与安全校验

说到安全校验,很多朋友觉得量产阶段没必要。其实不然——产线上烧录器万一出问题,或者固件被篡改,整批设备就废了。

我建议至少做CRC32校验。简单、快、够用。如果产品有安全要求,再上RSA或ECDSA签名。

校验方式 计算时间 安全性 适用场景
CRC32 微秒级 低(防随机错误) 普通消费电子
SHA256 毫秒级 中(防篡改) 工业设备
RSA签名 秒级 高(防伪造) 金融/医疗设备

我个人习惯在产测固件里做双校验

  • 烧录完成后立即校验CRC,确保数据写入正确
  • 每次启动时校验签名,防止Flash被意外改写

经验之谈:CRC多项式别用默认的0x04C11DB7。我建议用0xEDB88320,这个多项式对突发错误更敏感。产线上遇到过一批Flash有坏块,默认CRC没查出来,换了多项式就抓到了。

校验代码示例:

uint8_t verify_firmware(uint32_t start_addr, uint32_t length) {
    uint32_t calc_crc = 0;
    uint32_t stored_crc = *(volatile uint32_t*)(start_addr + length);
    
    // 计算整个固件的CRC
    calc_crc = crc32((uint8_t*)start_addr, length, 0xEDB88320);
    
    if (calc_crc != stored_crc) {
        return 0; // 校验失败
    }
    return 1; // 校验通过
}

4.4 看门狗策略

看门狗这东西,用好了是保护神,用不好就是催命符。产测阶段尤其要注意。

我见过最惨的案例:产测固件里开了看门狗,但没在测试循环里喂狗。结果测到第3个PDA时,看门狗超时复位了,整条产线以为设备坏了,全退了回来。

产测阶段的看门狗策略:

  • Bootloader阶段:关闭看门狗。Bootloader执行时间短,不需要。
  • 产测固件阶段:开启看门狗,但超时时间设长(10秒以上)。每个测试项完成后喂一次狗。
  • 正常模式:APP里按业务需求配置,通常1-5秒超时。

为什么产测阶段要开看门狗?你想啊,产线上如果某个PDA死机了,看门狗能自动复位,让设备继续跑测试。不然工人得手动断电重启,效率太低了。

喂狗代码要放在测试循环的关键节点:

void test_loop(void) {
    while(1) {
        // 喂狗
        HAL_IWDG_Refresh();
        
        // 执行测试项
        if (test_battery() != PASS) {
            report_fail("Battery");
            continue;
        }
        
        HAL_IWDG_Refresh(); // 每个测试项后都喂一次
        
        if (test_wifi() != PASS) {
            report_fail("WiFi");
            continue;
        }
        
        // ... 更多测试项
    }
}

重要提醒:产测固件退出前,一定要关闭看门狗。否则跳转到APP时,APP还没来得及初始化,看门狗就复位了。我早期犯过这个错,后来在跳转函数里加了一行:HAL_IWDG_Stop();

好了,这一节的内容就这些。总结一下:产测固件设计要围绕可靠切换、安全校验、看门狗保护三个核心。下一节咱们聊聊具体的产测项目实现,比如电压校准、射频测试这些实战内容。

记住,产测固件写得好,产线工人笑到老。别让软件成为量产的瓶颈。