第四章:TPMS终端软件架构:Bootloader设计
好,咱们进入正题。TPMS终端软件架构这块,我个人觉得最核心的就是Bootloader。你想想看,一个装在轮胎里的传感器,装车之后基本就不拆了。如果固件有bug或者需要升级,全靠Bootloader来救命。所以这部分设计,我建议你花点心思。
4.1 启动流程:从复位到跑起来
芯片上电复位后,第一件事就是跑Bootloader。这个流程我拆成三步来讲:
- 硬件初始化:关看门狗、配时钟、初始化RAM。这一步要快,我一般控制在5ms以内。
- 自检与跳转:检查应用固件的完整性,如果OK就跳转到应用区;如果不OK,就留在Bootloader里等升级指令。
- 升级监听:如果检测到升级请求(比如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虽然小,但它是车联网的一部分,一旦被攻破,后果很严重。
我设计的方案分三步:
- 信任根:芯片出厂时,在OTP(一次性可编程)区域烧录一个公钥哈希。这个哈希就是信任的起点,不可更改。
- 签名验证:固件发布前,用私钥对固件进行签名。Bootloader启动时,用OTP里的公钥哈希验证签名。
- 链式验证: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虽然小,但每一行代码都关系到行车安全。希望这些经验能帮你少走弯路。