3、刷写协议基础:UDS on CAN/DoIP基础、34/36/37服务详解、安全访问(27服务)

聊到底盘域控的OTA刷写,绕不开的就是UDS协议。说白了,UDS就是ECU和诊断仪之间沟通的“行话”。你发个请求,它回个响应,一来一回,刷写、标定、诊断就全干了。

我个人习惯把UDS理解成一套“指令集”。刷写时用的最多的是34/36/37这三个服务,再加上安全访问27服务。今天咱们就把这几个核心服务掰开揉碎讲清楚。

3.1 UDS on CAN 与 DoIP:两种物理层的差异

UDS本身不挑物理层。CAN和DoIP是两种最常见的载体。我在项目中遇到过不少同事,在CAN上写得好好的,一换DoIP就抓瞎。其实没那么玄乎。

对比项 UDS on CAN UDS over DoIP
物理层 CAN总线(CAN 2.0 / CAN FD) 以太网(TCP/IP + UDP)
传输速率 500kbps ~ 8Mbps(CAN FD) 100Mbps / 1Gbps
数据长度 8字节(CAN)/ 64字节(CAN FD) 最大 65535 字节(分片传输)
连接方式 广播式,所有节点都能收到 点对点,TCP连接
典型场景 传统ECU刷写、标定 域控、高性能计算平台刷写

为什么DoIP越来越火?你想想看,一个底盘域控的固件动辄几十兆甚至上百兆。用CAN刷,一帧8字节,加上协议开销,刷一次得半小时。DoIP一帧能发几千字节,几分钟搞定。嗯,这里要注意:DoIP虽然快,但网络稳定性要求更高。我曾经在车间调试时,网线接触不良导致刷写中断,查了半天才发现是物理层问题。

3.2 34/36/37 服务:刷写的“三步走”

刷写数据,本质上就是“请求下载 → 传输数据 → 请求退出传输”这三个步骤。对应的就是34、36、37服务。

3.2.1 34服务:请求下载(Request Download)

34服务的作用是告诉ECU:“我要开始写数据了,你准备好。” 请求报文里要指定数据格式(DFI,Data Format Identifier)和内存地址/大小。

// 34服务请求示例(CAN)
// 请求:0x34 + 子功能 + DFI + 地址 + 长度
// 假设:DFI = 0x00(无压缩/无加密),地址 = 0x80010000,长度 = 0x00100000(1MB)
Tx: 34 00 00 80 01 00 00 00 10 00 00
// 响应:0x74 + 子功能 + 最大数据块长度
Rx: 74 00 00 00 10 00  // 每次最多发0x1000字节

我个人习惯在发送34服务前,先确认ECU当前会话模式。如果不在扩展会话或编程会话里,34服务会被拒绝。避坑指南:我曾经遇到过ECU返回0x22(条件不满足),查了半天发现是没先发10 02(编程会话)。

3.2.2 36服务:传输数据(Transfer Data)

36服务是刷写的核心。它负责把实际数据一块一块地发给ECU。每发一块,ECU都会回复一个响应,告诉你这块数据是否接收成功。

// 36服务请求示例
// 请求:0x36 + 块序号 + 数据
// 块序号从0x01开始,每次递增
Tx: 36 01 [256字节数据...]
Rx: 76 01  // 块序号0x01接收成功

Tx: 36 02 [256字节数据...]
Rx: 76 02  // 块序号0x02接收成功

这里有个关键点:块序号。ECU会检查块序号是否连续。如果中间丢了一帧,ECU会回复0x13(不正确报文长度)或0x31(请求超出范围)。我在项目中遇到过一种情况:刷写工具发送太快,ECU来不及处理,导致块序号错乱。解决方案是在每帧之间加一个小的延时,或者等收到响应再发下一帧。

提示:36服务的数据块大小不是随便定的。它受限于34服务响应中返回的“最大数据块长度”。如果超过这个长度,ECU会直接拒绝。我建议在工具里做一次校验,避免发送超长数据块。

3.2.3 37服务:请求退出传输(Request Transfer Exit)

数据全部发完后,用37服务告诉ECU:“数据发完了,你可以开始处理了。” ECU收到后,通常会开始校验完整性、写入Flash等操作。

// 37服务请求示例
// 请求:0x37 + 0x00(无额外参数)
Tx: 37 00
Rx: 77 00  // 退出成功

注意:37服务不代表刷写完成。它只是告诉ECU“传输阶段结束”。ECU可能还需要几秒甚至几十秒来写入Flash。我建议在发送37服务后,等待ECU主动发送一个“编程完成”的响应,或者通过22服务读取状态来确认。

3.3 安全访问(27服务):刷写的“门禁”

刷写不是谁都能干的。ECU会要求你先通过安全访问验证,才能执行34/36/37等敏感服务。27服务就是干这个的。

安全访问的流程很简单:

  1. 请求种子(Seed):诊断仪发27 01(请求种子),ECU返回一个随机数(种子)。
  2. 发送密钥(Key):诊断仪用种子和算法计算出密钥,发27 02(发送密钥)。
  3. 验证结果:ECU比对密钥,如果正确,返回0x67 02,表示解锁成功。
// 安全访问示例(假设算法为简单异或)
// 步骤1:请求种子
Tx: 27 01
Rx: 67 01 12 34 56 78  // 种子 = 0x12345678

// 步骤2:计算密钥(种子 ^ 0xAAAAAAAA)
// 密钥 = 0x12345678 ^ 0xAAAAAAAA = 0xB89EFCD2

// 步骤3:发送密钥
Tx: 27 02 B8 9E FC D2
Rx: 67 02  // 解锁成功
警告:安全访问算法是OEM的核心机密。千万不要在生产工具里硬编码密钥。我曾经见过一个项目,密钥被逆向出来,导致刷写工具被滥用。建议使用HSM(硬件安全模块)或动态密钥生成机制。

为什么要有安全访问?说白了,就是防止误刷写或恶意刷写。你想想看,如果谁都能随便刷写ECU,那底盘域控的安全就无从谈起了。27服务就是第一道防线。

3.4 知识体系总览

下面这张图把本章的核心逻辑串起来了。从物理层到服务层,再到安全机制,每一步都环环相扣。

UDS刷写协议知识体系 物理层 CAN DoIP 刷写服务 34 36 37 安全访问 27服务 Seed & Key 刷写流程:安全访问(27) → 请求下载(34) → 传输数据(36) → 请求退出(37) 每一步都必须按顺序执行,跳步会导致ECU拒绝服务 物理层决定传输速率和可靠性 服务层定义刷写的数据交互逻辑 安全层确保只有授权设备才能刷写

3.5 避坑指南与实战经验

最后,分享几个我在项目中踩过的坑,希望能帮你少走弯路。

  • 超时处理:UDS协议里每个服务都有超时时间。34服务通常要求ECU在500ms内响应,36服务是200ms。如果超时,工具必须重发或中止。我曾经遇到过ECU在刷写过程中因为Flash写入慢,导致36服务超时。解决方案是在工具里增加一个“等待响应”的机制,而不是死等超时。
  • 数据对齐:34服务里的地址和长度必须对齐到ECU的Flash扇区大小。如果不对齐,ECU会返回0x31(请求超出范围)。我建议在工具里自动做对齐处理,避免手动计算出错。
  • 安全访问失败:27服务失败后,ECU通常会锁定一段时间(比如10秒)。如果连续失败多次,锁定时间会指数级增加。我曾经在调试时连续输错密钥,结果ECU锁了30分钟。嗯,那段时间只能干等着。
核心要点:UDS刷写协议的核心就是“安全访问 + 34/36/37”的组合。理解了这个组合,你就掌握了OTA刷写的基础。剩下的就是根据具体ECU的协议规范,调整参数和流程。