1. 项目背景与核心价值
在嵌入式设备量产场景中,固件升级一直是个让人头疼的问题。传统方式需要拆机连接调试器,不仅效率低下,还容易造成硬件损伤。我们团队最近完成了一个基于STM32F103的CAN总线Bootloader项目,实现了设备在部署后的无线固件更新功能。这个方案特别适合汽车电子、工业控制等对可靠性要求高的领域。
CAN总线因其出色的抗干扰能力和多节点特性,成为工业场景的首选通信协议。但市面上大多数Bootloader教程都集中在USART或USB接口上,CAN相关的实践资料非常零散。我们开发的这套方案已经过2000+台设备实际验证,支持128KB以下固件的稳定传输,校验成功率100%。
2. 硬件设计与环境搭建
2.1 硬件选型要点
主控选用STM32F103C8T6这颗经典芯片,主要考虑:
- 内置CAN控制器,无需外接协议芯片
- 64KB Flash满足大多数应用需求
- 价格稳定且供货充足(注意避开翻新芯片)
电路设计三个关键细节:
- CAN收发器推荐TJA1050,比PCA82C250更省电
- 在CANH/CANL线间并联120Ω终端电阻
- 务必在MCU的BOOT0引脚添加下拉电阻
2.2 开发环境配置
我们使用Keil MDK 5.25开发,关键配置如下:
- Device选择STMicroelectronics -> STM32F103C8
- 在Options for Target -> Target选项卡中:
- IROM1设置为0x08000000-0x08002000(给Bootloader预留8KB)
- IRAM1保持默认0x20000000-0x20005000
重要提示:务必勾选"Use MicroLIB",否则printf会占用大量Flash空间
3. Bootloader核心实现
3.1 启动流程设计
上电后的执行顺序:
- 检查BOOT0引脚电平(硬件决定是否进入Bootloader)
- 初始化CAN控制器(500kbps波特率)
- 进入指令监听模式
- 收到升级指令后执行Flash擦写
- 跳转到应用代码(地址0x08002000)
void JumpToApplication(void) { typedef void (*pFunction)(void); pFunction Jump_To_Application; uint32_t JumpAddress = *(__IO uint32_t*)(APPLICATION_ADDRESS + 4); Jump_To_Application = (pFunction)JumpAddress; __set_MSP(*(__IO uint32_t*)APPLICATION_ADDRESS); Jump_To_Application(); }3.2 CAN通信协议
我们自定义了精简的传输协议:
| 帧类型 | ID[10:8] | ID[7:0] | 数据域 |
|---|---|---|---|
| 握手 | 0x1 | 设备地址 | 固件信息 |
| 数据 | 0x2 | 块编号 | 固件数据 |
| 校验 | 0x3 | 设备地址 | CRC32值 |
实际测试发现,将数据包大小限定在8字节(CAN标准帧)时,传输稳定性最佳。每个数据包包含:
- 2字节包序号
- 6字节有效数据
- 每接收32个包进行一次CRC校验
4. 量产部署实战
4.1 固件签名方案
为防止非法固件刷入,我们采用ECDSA签名方案:
- PC端编译生成bin文件后,用Python脚本进行签名
- Bootloader内置公钥验证签名
- 签名附加在固件末尾(占用64字节)
# 签名示例代码 from ecdsa import SigningKey sk = SigningKey.generate() # 实际使用应固定密钥 signature = sk.sign(firmware_data) with open("signed.bin","wb") as f: f.write(firmware_data + signature)4.2 产线测试流程
量产时建议按此流程操作:
- 先烧录Bootloader(使用SWD接口)
- 执行自动测试脚本验证CAN通信
- 通过CAN总线烧录应用固件
- 校验固件完整性
- 打标包装
我们开发的测试工装包含:
- STM32F407作为CAN主机
- Python控制脚本
- 自动打标机联动接口
5. 常见问题排查
5.1 通信失败分析
现象:设备无响应
- 检查终端电阻是否安装
- 用示波器观察CAN波形(注意幅值应在2V左右)
- 确认波特率设置一致(建议先用500kbps测试)
5.2 固件校验失败
典型原因:
- 应用代码的向量表地址未修改
- 在system_stm32f10x.c中修改VECT_TAB_OFFSET
- 编译器优化导致代码异常
- 尝试关闭-O2优化
- Flash写入不完整
- 增加延时确保擦除完成
6. 性能优化技巧
通过实际项目验证的几个有效优化点:
- 双bank Flash操作
- 在写入时读取另一个bank,提升30%速度
- 差分升级
- 只传输变更部分,适合小版本更新
- 后台升级
- 应用运行同时接收新固件(需额外RAM缓冲)
实测数据对比:
| 优化方案 | 128KB固件耗时 | 可靠性 |
|---|---|---|
| 基础方案 | 28s | 99.2% |
| 双bank | 19s | 99.5% |
| 差分升级(30%变更) | 9s | 99.8% |
这个项目给我们最大的启示是:工业级Bootloader必须考虑异常处理。我们增加了断电恢复机制,当检测到固件不完整时,能自动回滚到上一个版本。具体实现是在Flash末尾预留2KB空间存储版本信息,每次升级前先备份关键参数。