4. 通信协议基础

刷写数据流设计,说白了就是解决一个问题:怎么把几十兆的二进制文件,安全、可靠、高效地塞进ECU里。

我刚开始做刷写时,以为就是把文件一股脑发过去。结果第一次台架测试就挂了——CAN总线直接爆满,ECU那边也报缓冲区溢出。嗯,从那以后我才真正重视起数据流设计。

这一章,咱们就聊聊数据分段、块大小权衡,还有CRC校验这些实战细节。

4.1 数据分段:为什么不能一把梭?

ECU的RAM有限,Flash写入也有最小单位。你想想看,一个2MB的刷写文件,如果一次性发过去,ECU根本接不住。

所以,刷写数据必须分段。常见的分段策略分三层:

层级 名称 典型大小 说明
L1 刷写文件 1~64 MB 完整的二进制镜像,如 .hex、.s19、.bin
L2 数据块 (Block) 1~256 KB 按Flash扇区或逻辑分区切分,每个块独立校验
L3 传输段 (Segment) 4~4096 字节 单次CAN/UDS消息承载的数据量,受MTU限制

我个人习惯,在UDS 14229-1标准下,用0x34 (RequestDownload) 和 0x36 (TransferData) 服务来实现分段传输。每个块对应一次0x34请求,块内的每个段对应一次0x36请求。

核心原则: 块的大小 = Flash擦除单位(Sector)的整数倍。这样能避免跨扇区校验带来的复杂度。

4.2 传输机制:从块到段的拆解

数据分段之后,怎么传?我画了一张流程图,你看一眼就明白了。

刷写数据流分段传输流程 刷写数据流分段传输流程 刷写文件 (2MB) 切块 (Block 1~N) 0x34 RequestDownload (块头) 0x36 TransferData (段1~M) 0x37 RequestTransferExit (CRC校验) 每个块独立走完 0x34 → 0x36×M → 0x37 流程,块之间串行执行

流程其实不复杂:

  1. 先发0x34:告诉ECU我要写一个块,块的大小、起始地址、CRC算法都在这条消息里。
  2. 循环发0x36:把块切成M个段,一段一段发。每发一段,ECU回一个响应,确认接收成功。
  3. 最后发0x37:块传完了,ECU做一次CRC校验,确认完整性。

实战技巧: 我建议每个0x36消息里带一个段序号(BlockSequenceCounter)。有一次我在项目里遇到丢包,ECU靠这个序号发现中间少了一段,直接要求重传,省了整块重来的时间。

4.3 块大小与传输时间:一个权衡游戏

块大小怎么定?这其实是个权衡。我列个表,你一看就懂。

块大小 优点 缺点 适用场景
小 (1~4 KB) 出错重传代价低,RAM占用小 握手次数多,总传输时间长 低速CAN (125 kbps),小RAM ECU
中 (16~64 KB) 平衡传输效率和重传代价 需要中等RAM缓冲区 CAN FD (500 kbps),主流ECU
大 (128~256 KB) 握手次数少,总传输时间短 出错重传代价高,RAM压力大 高速CAN FD / DoIP,大RAM ECU

我做过一个实际测算:

  • CAN 2.0,500 kbps,每帧8字节有效数据,帧间隔+协议开销约1.2ms。
  • 一个64 KB的块,需要发8192帧,纯传输时间约9.8秒。
  • 如果块大小改成4 KB,同样数据量要发16个块,握手次数多了15次,总时间变成11.2秒。

你看,块越大,传输越快。但代价是什么?

注意: 块越大,ECU需要的RAM缓冲区就越大。我曾经遇到一个项目,ECU的RAM只有8 KB,我硬塞了一个16 KB的块,结果ECU直接报内存溢出。后来改成4 KB块,问题解决。

所以,块大小的上限 = ECU的可用RAM / 2。留一半给协议栈和中断处理。

4.4 CRC校验:完整性保护的最后一关

数据传完了,怎么保证没出错?CRC校验就是干这个的。

UDS标准里,0x37服务会带一个CRC值。ECU收到所有段后,自己算一遍CRC,跟发过来的比对。一致,就写Flash;不一致,就报错,要求重传整个块。

常用的CRC算法有几种:

算法 位宽 多项式 检测能力 我推荐
CRC-8 8 bit 0x07 一般,适合小数据块 不推荐,碰撞概率高
CRC-16 16 bit 0x8005 较好,适合CAN刷写 常用,性价比高
CRC-32 32 bit 0x04C11DB7 极强,适合DoIP/大文件 高速刷写首选

我个人习惯,在CAN刷写场景下用CRC-16。为什么?

  • CRC-8太弱,我曾经在测试中发现,随机翻转2个bit,CRC-8有1/256的概率检测不出来。
  • CRC-32虽然强,但计算开销大,在低速MCU上会拖慢刷写速度。
  • CRC-16在检测能力和计算速度之间取得了很好的平衡。

避坑指南: 我曾经遇到一个坑——刷写工具和ECU用的CRC初始值不一样。工具用0xFFFF,ECU用0x0000,结果每次校验都失败。排查了两天才发现。所以,CRC的初始值、多项式、输出异或值,必须在刷写规范里写死。

4.5 实战中的完整性保护策略

除了CRC,我还会在刷写流程里加几道保险:

  1. 块级CRC:每个块传完后,0x37里带CRC,ECU校验。
  2. 文件级CRC:整个刷写文件在工具端算一个CRC,放在刷写文件的末尾或头部。ECU刷完后,再算一次,双重确认。
  3. Flash写入后回读:ECU写完一个扇区后,读出来跟原始数据比对。虽然慢,但最可靠。

你想想看,如果只靠块级CRC,万一ECU在写Flash时掉电,数据写到一半,CRC校验通过了但数据不完整,怎么办?文件级CRC和回读机制就是兜底的。

我的经验: 在量产刷写中,我一般用块级CRC + 文件级CRC的组合。回读太耗时,只在研发阶段用。量产时,文件级CRC已经能覆盖掉电场景了。

4.6 小结一下

刷写数据流设计,核心就三件事:

  • 分段:文件→块→段,三层结构,适配ECU的RAM和Flash特性。
  • 权衡:块大小影响传输时间和重传代价,根据总线速度和ECU RAM来定。
  • 校验:CRC-16是性价比之选,块级+文件级双重校验保平安。