4. 刷写流程设计:预编程阶段、主编程阶段、后编程阶段
好了,咱们今天聊聊OTA刷写的核心——三个阶段的设计。说白了,就是把新固件安全、可靠地写进ECU里。我做了这么多年,见过太多刷写失败翻车的案例,十有八九都是阶段划分不清、时序没卡好。
整个刷写流程,我习惯分成三大块:预编程、主编程、后编程。每个阶段都有它存在的道理,少一个都不行。
4.1 预编程阶段:准备工作做扎实
这个阶段,说白了就是“打扫屋子再请客”。你想想看,ECU正在跑着应用,你突然要刷它,不先打个招呼,它肯定跟你急。
核心目标:让ECU从正常运行模式切换到可刷写模式。
具体做哪些事?我列一下:
- 诊断会话控制(0x10 03):切换到扩展会话。这是第一步,告诉ECU“我要干活了”。
- 安全访问(0x27):解锁种子密钥。嗯,这里要注意,有些ECU的安全算法很变态,我曾经遇到过一家供应商,种子和密钥的算法居然跟硬件绑定,搞得我排查了整整两天。
- 检查刷写前提条件(0x31):比如电压是否正常、点火开关是否在ON档、车速是否为0。这些条件不满足,刷到一半断电就完蛋了。
- 禁用DTC存储(0x85 02):刷写过程中会产生一堆乱七八糟的故障码,先关掉存储功能,免得刷完回来一看,DTC列表爆满。
- 停止非关键通信(0x28):把那些跟刷写无关的报文停掉,比如网络管理报文、应用报文。减少总线负载,给刷写腾出带宽。
我个人习惯:在预编程阶段最后,我会发一个0x3E(Tester Present)保持会话。不然ECU超时了,自动跳回默认会话,前面的工作全白干。
4.2 主编程阶段:真刀真枪干
这个阶段就是真正的数据传输了。我把它拆成三个子步骤:
4.2.1 下载前准备
- 请求下载(0x34):告诉ECU我要写数据了,数据长度是多少,存在哪个内存地址。
- 传输数据(0x36):把固件分包发过去。每包大小我建议控制在4096字节以内,太大了一出错重传成本高,太小了效率低。
- 请求传输退出(0x37):发完了,告诉ECU“没了”。
避坑指南:我曾经遇到过ECU的Flash擦除时间特别长,超过1秒。而UDS的物理响应超时默认是50ms。怎么办?我用了0x36的“流控帧”机制,让ECU告诉Tester“我忙,你等会儿”。这个细节很多人会忽略。
4.2.2 数据传输中的校验
光发数据不够,还得保证数据没错。我一般会在每包数据后面加个CRC32校验。ECU收到后算一遍,对不上就回NRC 0x22(条件不满足),Tester就得重传这一包。
// 伪代码示例:数据包结构
typedef struct {
uint32_t block_sequence_counter; // 块序列号
uint8_t data[4096]; // 实际数据
uint32_t crc32; // 本包CRC校验
} OtaDataPacket;
4.2.3 完整性检查
所有数据传完后,我会发一个例程控制(0x31 01 FF00),让ECU对整个Flash区域做一次完整性校验。比如对整个固件算个SHA256,跟Tester发过来的比对。这一步不能省,我见过有人跳过这步,结果刷进去的固件是坏的,ECU直接变砖。
4.3 后编程阶段:收尾工作要细致
数据写完了,ECU还不能直接跑。你得帮它把“后事”处理好。
核心任务:恢复ECU的正常运行状态。
| 步骤 | UDS服务 | 说明 |
|---|---|---|
| 恢复通信 | 0x28 03 | 重新开启之前停掉的非关键报文 |
| 启用DTC存储 | 0x85 01 | 恢复故障码记录功能 |
| 清除DTC | 0x14 FF FF FF | 把刷写过程中产生的假故障码清掉 |
| 复位ECU | 0x11 01 | 硬件复位,让新固件跑起来 |
注意:复位后,ECU会重新初始化。这时候Tester要等一段时间再发诊断请求,别ECU还没启动完你就去问它“你好吗”,它理都不理你。我一般等3-5秒。
4.4 三个阶段的时间分配建议
嗯,这里我根据实际项目经验,给个参考时间分配:
- 预编程阶段:约占总时间的5%。主要是握手和配置,很快。
- 主编程阶段:约占总时间的90%。数据传输是重头戏,尤其是大固件(比如几十MB的A/B分区镜像)。
- 后编程阶段:约占总时间的5%。收尾工作,干净利落。
你想想看,如果一个ECU刷写总共需要10分钟,那主编程就占了9分钟。所以优化刷写速度,重点就是优化数据传输效率——比如提高CAN FD的波特率,或者用更大的数据包。
4.5 我踩过的坑
最后分享一个真实案例。有一次做OTA刷写,预编程阶段一切正常,主编程也顺利传完数据,结果后编程阶段复位后,ECU死活连不上。排查了半天,发现是复位后ECU的CAN控制器初始化时间比预期长了200ms,Tester发诊断请求发早了。
从那以后,我强制要求:复位后必须等待ECU发送一个“应用启动完成”的特定报文,Tester收到后才能继续。这个机制,我建议你们也加上。
总结一句话:预编程是“打招呼”,主编程是“干活”,后编程是“善后”。三个阶段环环相扣,任何一个环节出问题,刷写就失败。做设计时,把每个阶段的超时、重试、异常处理都考虑清楚,才能做出稳定的OTA方案。