Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

2.16 上传下载(0x34/35/36/37)——手术四步曲

4MB固件,8字节帧——怎么拆

这是UDS里最复杂、最严谨的服务链——固件下载。一个ECU的新固件可能是2MB到16MB。经典CAN帧的数据区只有8字节(CAN FD可达64字节)。本书示例以经典CAN为例。你需要把一个大文件切成几千块,每一块编码上一个blockSequenceCounter以保证不丢、不重、不乱序——并且在最后验证完整性。

ISO 14229-1:2013用四条SID完成了这个任务:

  • 0x34 RequestDownload —— 请求下载授权
  • 0x36 TransferData —— 传输数据块
  • 0x37 RequestTransferExit —— 终止传输并验证
  • 0x35 RequestUpload —— 上传(数据方向相反)

第一步:请求下载(0x34)——手术授权

请求

Byte 0: 0x34 (SID)
Byte 1: dataFormatIdentifier (1字节)
        高4位=compressionType(0x0=无压缩, 0x1=...)
        低4位=encryptingType(0x0=无加密, 0x1=...)
Byte 2: addressAndLengthFormatIdentifier (1字节)
        高4位=memorySizeLength (内存长度占多少字节)
        低4位=memoryAddressLength (内存地址占多少字节)
Byte 3..N: memoryAddress[] (内存首地址, 长度=低4位决定的字节数)
Byte   N+1..M: memorySize[] (要下载的总长度, 长度=高4位决定的字节数)

正响应

Byte 0: 0x74 (SID+0x40)
Byte 1: lengthFormatIdentifier (1字节)
        高4位= 回显 maxNumberOfBlockLength 占多少字节(通常2)
        低4位= 保留 (0x00)
Byte 2..N: maxNumberOfBlockLength (ECU允许的最大单块传输字节数, 大端序)

blockSequenceCounter的起点在正响应的瞬间被隐式重置为0。


第二步:传输数据(0x36)——手术进行

请求(下载方向)

Byte 0: 0x36 (SID)
Byte 1: blockSequenceCounter (1字节, 从0x01开始)
Byte 2..N: transferRequestParameterRecord[] (实际数据块)

正响应(下载)

Byte 0: 0x76 (SID+0x40)
Byte 1: blockSequenceCounter (回声)
Byte 2..N: transferResponseParameterRecord[] (下载可返回的响应参数记录)

blockSequenceCounter的规则:

  • 第一条0x36的blockSequenceCounter必须是0x01
  • 每一条后续0x36的blockSequenceCounter = 上一条+1
  • blockSequenceCounter为0xFF时,下一个是0x00(回绕发生在同一传输会话内,ECU通过0x37收到的校验和验证整体完整性,因此0x00重复不会导致歧义)
  • ECU在收到每一条时做校验:==当前预期的计数器?不等于→NRC 0x73(wrongBlockSequenceCounter)

第三步:传输终止(0x37)——手术结束

请求

Byte 0: 0x37 (SID)
Byte 1..N: transferRequestParameterRecord[] (可选的, 例如校验和数据)

正响应

Byte 0: 0x77 (SID+0x40)
Byte 1..N: transferResponseParameterRecord[]

ECU在这一步验证全部下载的数据完整性并关闭传输——此后不能再发0x36。


上传流程(0x35)——数据方向相反

上传把角色反转——ECU向诊断仪发送数据。流程相同:

0x35 RequestUpload → 请求上传, 声明地址/长度
0x36 TransferData → 每次传送: ECU发数据、诊断仪接收
0x37 RequestTransferExit → 上传完成

典型上传场景:刷写前备份当前固件(回滚用)、读取故障发生时的内存镜像做离线分析、产线读出某块存储区域的标定数据做质量抽检。


完整下载序列示例

诊断仪 → ECU:  0x34 0x00 0x44 0x00 0x04 0x00 0x00 0x00 0x00 0x04 0x00
解读: dataFormatIdentifier=0x00 (无压缩), addressFmtId=0x44 (4字节地址+4字节长度)
      memoryAddress=0x00040000 (Flash起始地址)
      memorySize=0x00000400 (1024字节要下载)

ECU → 诊断仪:   0x74 0x20 0x00 0xC8
解读: lengthFmtId=0x20 (2字节最大块长度)  maxBlockLen=0x00C8=200字节
      → 每次传输≤200字节

诊断仪 → ECU:  0x36 0x01 [block1: 200字节固件数据]
ECU → 诊断仪:   0x76 0x01 [确认block 1 OK]

诊断仪 → ECU:  0x36 0x02 [block 2: 200字节]
ECU → 诊断仪:   0x76 0x02 [确认block 2 OK]

... 继续直到最后一个块 ...

诊断仪 → ECU:  0x36 0x06 [block 6: 24字节 (最后一块)]
ECU → 诊断仪:   0x76 0x06 [确认最后一块OK]

诊断仪 → ECU:  0x37
ECU → 诊断仪:   0x77 [确认传输终止, 固件完整]

为什么需要四步而不是一步

这是UDS里最漂亮的“分形约束“设计——每一步解决一个独立的约束:

  1. 0x34 = 协商:告诉ECU我要写哪个区域、总共多少。ECU告诉我每次发多大的块。
  2. 0x36 = 传输:实际搬运数据——分段、校验序号、累积进度。
  3. 0x37 = 关闭:校验完整性——我传完了,没问题,完成。

三个步骤是被CAN总线的8字节MTU和Flash存储的分块擦写粒度逼出来的——不是协议设计者的审美偏好。


本篇小结

  1. 下载(0x34→0x36→0x37)和上传(0x35→0x36→0x37)是UDS中数据操作的完整序列。
  2. blockSequenceCounter是传输可靠性的核心——保证不丢帧、不重帧、不乱序。
  3. addressAndLengthFormatIdentifier用半字节编码传达地址和长度字节数——实现可变宽度寻址。
  4. 下载序列的每一步都可以被NRC打断——如果blockSequenceCounter不连续→0x73(WBSC);如果ECU正忙→0x71(传输已暂停)。

【下集预告】:下载的通道已经建立——但有时候你不需要通过文件传输协议下载——你只需要直接写几个字节到特定的内存地址。0x3D WriteMemoryByAddress——极简直接的地址写入——在诊断生产中用于快速标定注入。