第四章:TPMS终端软件架构:Bootloader设计

好,咱们进入正题。TPMS终端软件架构这块,我个人觉得最核心的就是Bootloader。你想想看,一个装在轮胎里的传感器,装车之后基本就不拆了。如果固件有bug或者需要升级,全靠Bootloader来救命。所以这部分设计,我建议你花点心思。

4.1 启动流程:从复位到跑起来

芯片上电复位后,第一件事就是跑Bootloader。这个流程我拆成三步来讲:

  1. 硬件初始化:关看门狗、配时钟、初始化RAM。这一步要快,我一般控制在5ms以内。
  2. 自检与跳转:检查应用固件的完整性,如果OK就跳转到应用区;如果不OK,就留在Bootloader里等升级指令。
  3. 升级监听:如果检测到升级请求(比如GPIO电平或者RF信号),就进入升级模式。

关键点:启动流程里最怕的是死循环。我曾经遇到过一版代码,因为初始化顺序不对,导致跳转前就把外设配置乱了。结果应用固件跑起来直接死机。后来我加了个“软复位计数器”,如果连续三次启动失败,就强制进入Bootloader。

这里给个简化的启动流程图,用伪代码表示:

void main(void) {
    // 1. 关中断,初始化堆栈
    DisableInterrupts();
    InitStack();
    
    // 2. 硬件初始化(最小系统)
    InitClock();      // 配置内部RC振荡器
    InitWatchdog();   // 先关掉,后面再开
    InitRAM();        // 清零或校验
    
    // 3. 检查升级标志
    if (CheckUpdateFlag() == TRUE) {
        EnterUpdateMode();  // 进入升级模式
        while(1);           // 等待升级完成
    }
    
    // 4. 校验应用固件
    if (VerifyAppFirmware() == PASS) {
        JumpToApp();        // 跳转到0x4000地址
    } else {
        // 固件损坏,等待升级
        EnterUpdateMode();
    }
}

4.2 分区管理:内存怎么分才合理

TPMS的Flash通常不大,32KB到128KB不等。分区设计直接影响升级的可靠性。我个人的习惯是分成四个区:

分区名称 起始地址 大小 用途
Bootloader区 0x0000 8KB 存放Bootloader代码,只读
参数区 0x2000 2KB 存放校准参数、ID、升级标志
应用区A 0x2800 32KB 主应用固件
应用区B 0xA800 32KB 备份固件,用于回滚

避坑指南:我曾经把参数区和Bootloader区挨得太近,结果一次擦写操作把Bootloader的向量表给覆盖了。从那以后,我强制要求参数区必须单独一个扇区,并且和Bootloader区之间留出至少4KB的空白。

分区管理还有个细节:升级标志位。我习惯在参数区里放一个字节,0xAA表示“需要升级”,0x55表示“升级完成”。这样即使升级过程中断电,下次上电Bootloader也能识别到未完成的升级。

4.3 应用固件:采集、处理、通信

应用固件是TPMS真正干活的部分。我把它拆成三个模块:

  • 采集模块:读取压力、温度、加速度传感器。注意采样频率,我一般设成每3秒一次,太频繁费电。
  • 处理模块:滤波、阈值判断、数据打包。这里有个坑——传感器数据会有毛刺,不加滤波的话,误报率会很高。
  • 通信模块:通过RF发射数据。TPMS常用的是315MHz或433MHz,发射功率要控制在10dBm以内,不然会干扰其他设备。

嗯,这里要注意:应用固件和Bootloader是独立的两个工程。编译时,应用固件的起始地址要跟分区表对齐。我见过有人忘了改链接脚本,结果应用固件编译出来地址从0x0000开始,直接把Bootloader覆盖了。

4.4 安全启动链:信任根与签名验证

安全启动链,说白了就是防止有人往你的TPMS里刷恶意固件。TPMS虽然小,但它是车联网的一部分,一旦被攻破,后果很严重。

我设计的方案分三步:

  1. 信任根:芯片出厂时,在OTP(一次性可编程)区域烧录一个公钥哈希。这个哈希就是信任的起点,不可更改。
  2. 签名验证:固件发布前,用私钥对固件进行签名。Bootloader启动时,用OTP里的公钥哈希验证签名。
  3. 链式验证:Bootloader验证应用固件,应用固件再验证参数区。每一级都签名,形成一条信任链。

重要提醒:签名算法别用MD5,已经被破解了。我建议用SHA-256 + ECDSA。虽然计算量大了点,但TPMS的MCU通常有硬件加速模块,性能不是问题。

签名验证的代码片段,我贴一个简化版:

bool VerifyFirmware(uint32_t appAddr, uint32_t appSize) {
    // 1. 读取固件哈希
    uint8_t hash[32];
    CalculateSHA256(appAddr, appSize, hash);
    
    // 2. 读取签名(存储在固件末尾)
    uint8_t signature[64];
    ReadFlash(appAddr + appSize, signature, 64);
    
    // 3. 用公钥验证签名
    if (ECDSA_Verify(publicKey, hash, signature) == PASS) {
        return TRUE;
    } else {
        return FALSE;
    }
}

我个人习惯在Bootloader里加一个“安全计数器”。如果连续三次签名验证失败,就锁定芯片,不再接受任何升级请求。这样可以防止暴力破解。

4.5 总结与避坑

好了,这一章的内容差不多就这些。我最后再唠叨几句:

  • 启动流程:一定要加看门狗,防止Bootloader卡死。
  • 分区管理:留足冗余空间,别把Flash塞得太满。
  • 安全启动:信任根一定要硬件保护,别放在可擦写的Flash里。

我记得有一次,客户反馈TPMS升级后频繁死机。排查了半天,发现是Bootloader跳转到应用区时,忘了关中断。结果应用固件的中断向量表还没初始化,一个中断进来直接跑飞。从那以后,我每次跳转前都会加一句DisableInterrupts(),跳转后再由应用固件自己开中断。

嗯,细节决定成败。TPMS的Bootloader虽然小,但每一行代码都关系到行车安全。希望这些经验能帮你少走弯路。