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来不及处理,导致块序号错乱。解决方案是在每帧之间加一个小的延时,或者等收到响应再发下一帧。
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服务就是干这个的。
安全访问的流程很简单:
- 请求种子(Seed):诊断仪发27 01(请求种子),ECU返回一个随机数(种子)。
- 发送密钥(Key):诊断仪用种子和算法计算出密钥,发27 02(发送密钥)。
- 验证结果: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 // 解锁成功
为什么要有安全访问?说白了,就是防止误刷写或恶意刷写。你想想看,如果谁都能随便刷写ECU,那底盘域控的安全就无从谈起了。27服务就是第一道防线。
3.4 知识体系总览
下面这张图把本章的核心逻辑串起来了。从物理层到服务层,再到安全机制,每一步都环环相扣。
3.5 避坑指南与实战经验
最后,分享几个我在项目中踩过的坑,希望能帮你少走弯路。
- 超时处理:UDS协议里每个服务都有超时时间。34服务通常要求ECU在500ms内响应,36服务是200ms。如果超时,工具必须重发或中止。我曾经遇到过ECU在刷写过程中因为Flash写入慢,导致36服务超时。解决方案是在工具里增加一个“等待响应”的机制,而不是死等超时。
- 数据对齐:34服务里的地址和长度必须对齐到ECU的Flash扇区大小。如果不对齐,ECU会返回0x31(请求超出范围)。我建议在工具里自动做对齐处理,避免手动计算出错。
- 安全访问失败:27服务失败后,ECU通常会锁定一段时间(比如10秒)。如果连续失败多次,锁定时间会指数级增加。我曾经在调试时连续输错密钥,结果ECU锁了30分钟。嗯,那段时间只能干等着。