3. 刷写流程核心服务:0x34、0x36、0x37、0x31 在刷写中的应用
好,咱们今天聊点硬核的。刷写流程里,有四个服务是绕不开的——0x34(请求下载)、0x36(数据传输)、0x37(请求退出传输),还有0x31(例程控制)。说白了,它们就是刷写的“四驾马车”。
我个人习惯把这四个服务分成两组:传输组和控制组。0x34、0x36、0x37 负责把数据从诊断仪搬到 ECU 里,0x31 则负责在刷写前后做一些“脏活累活”,比如擦除、校验、复位。嗯,咱们一个一个来拆解。
3.1 请求下载(0x34)——刷写的“敲门砖”
0x34 是刷写流程的起点。它的作用很简单:告诉 ECU “我要开始写数据了,你准备好没?”
我记得刚入行时,有个同事没发 0x34 就直接发 0x36,结果 ECU 直接不理他。你想想看,ECU 都不知道你要写什么、写到哪里、写多大,它怎么敢收数据?
0x34 的请求报文长这样:
| Byte | 名称 | 值示例 | 说明 |
|------|----------------|--------------|--------------------------|
| 0 | SID | 0x34 | 请求下载服务标识 |
| 1 | dataFormatIdentifier | 0x00 | 数据格式,通常为无压缩 |
| 2-3 | addressAndLengthFormat | 0x44 | 地址和长度格式 |
| 4-7 | memoryAddress | 0x08000000 | 起始地址(4字节) |
| 8-11 | memorySize | 0x00010000 | 数据长度(4字节) |
这里有个坑,我必须要说。地址和长度格式(Byte 2-3)很容易搞错。0x44 表示地址占 4 字节,长度占 4 字节。但有些 ECU 用 0x24(地址 2 字节,长度 4 字节),你如果写错了,ECU 会回一个 NRC 0x13(消息长度错误)。
ECU 收到 0x34 后,如果一切正常,会回复一个肯定响应:
| Byte | 名称 | 值示例 | 说明 |
|------|----------------|--------------|--------------------------|
| 0 | SID + 0x40 | 0x74 | 肯定响应 |
| 1 | maxNumberOfBlockLength | 0x1000 | 每包最大数据长度(4096字节) |
这个 maxNumberOfBlockLength 很关键。它告诉诊断仪:你每次发 0x36 时,数据包不要超过这个大小。我见过有人不管这个值,直接发 8KB 的数据包,结果 ECU 直接丢包。
3.2 数据传输(0x36)——真正的“搬砖”环节
0x34 握手成功后,就该 0x36 上场了。它的任务就是把数据一块一块地传给 ECU。
0x36 的请求报文:
| Byte | 名称 | 值示例 | 说明 |
|------|----------------|--------------|--------------------------|
| 0 | SID | 0x36 | 数据传输服务标识 |
| 1 | blockSequenceCounter | 0x01 | 块序列计数器,从1开始 |
| 2-N | data | ... | 实际数据 |
这里有个细节:blockSequenceCounter 是从 1 开始的,每发一包加 1。ECU 会用这个值来检测是否有丢包。如果 ECU 发现序列号跳了(比如从 1 直接跳到 3),它会认为中间那包丢了,然后回复 NRC 0x13。
我在项目中遇到过一种情况:诊断仪和 ECU 之间的 CAN 总线负载很高,偶尔会丢帧。结果刷写到一半,ECU 突然报错,说序列号不对。后来我加了一个 重传机制:如果收到 NRC 0x13,就重新发送当前包,序列号不变。嗯,这个问题就解决了。
ECU 收到 0x36 后,会回复一个肯定响应:
| Byte | 名称 | 值示例 | 说明 |
|------|----------------|--------------|--------------------------|
| 0 | SID + 0x40 | 0x76 | 肯定响应 |
| 1 | blockSequenceCounter | 0x01 | 当前接收的块序列号 |
注意,ECU 回复的序列号和你发的一样。如果不一样,说明 ECU 可能处理了其他包,这时候要小心了。
3.3 请求退出传输(0x37)——优雅地结束
数据发完了,不能直接断电走人。你需要告诉 ECU:“数据发完了,你可以处理了。” 这就是 0x37 的作用。
0x37 的请求报文很简单:
| Byte | 名称 | 值示例 | 说明 |
|------|----------------|--------------|--------------------------|
| 0 | SID | 0x37 | 请求退出传输服务标识 |
没有多余的数据,就一个字节。ECU 收到后,会回复一个肯定响应:
| Byte | 名称 | 值示例 | 说明 |
|------|----------------|--------------|--------------------------|
| 0 | SID + 0x40 | 0x77 | 肯定响应 |
你以为这就完了?不,0x37 之后,ECU 通常会进入一个 内部处理阶段。比如把收到的数据从临时缓冲区搬到 Flash 里。这个过程可能耗时几秒甚至几十秒。
3.4 例程控制(0x31)——刷写的“瑞士军刀”
0x31 不是刷写的专属服务,但它在刷写流程里用得特别多。说白了,0x31 就是让 ECU 执行一段预定义的“小程序”。
在刷写中,0x31 通常用于三个场景:
- 擦除 Flash:刷写前,把目标区域擦干净。
- 校验数据:刷写后,检查数据是否正确。
- 复位 ECU:刷写完成后,让 ECU 重启。
0x31 的请求报文:
| Byte | 名称 | 值示例 | 说明 |
|------|----------------|--------------|--------------------------|
| 0 | SID | 0x31 | 例程控制服务标识 |
| 1 | subFunction | 0x01 | 01=启动, 02=停止, 03=查询 |
| 2-3 | routineIdentifier | 0xFF00 | 例程ID |
| 4-N | routineData | ... | 例程参数 |
举个例子,擦除 Flash 的例程:
请求: 31 01 FF 00 08 00 00 00
| | | |________ 起始地址 (0x08000000)
| | |______________ 例程ID (0xFF00, 擦除例程)
| |_________________ 启动例程
|____________________ SID
响应: 71 01 FF 00
| | |
| | |______________ 例程ID
| |_________________ 启动成功
|____________________ 肯定响应
这里要注意,擦除例程通常需要时间。ECU 可能不会立即回复,而是先回一个 NRC 0x78(待处理),等擦除完了再回肯定响应。你的脚本要能处理这种情况。
3.5 四个服务的配合——一个完整的刷写片段
说了这么多,咱们看看这四个服务怎么配合。这是一个典型的刷写流程:
1. 诊断仪 -> ECU: 31 01 FF 00 08 00 00 00 (擦除 Flash)
2. ECU -> 诊断仪: 71 01 FF 00 (擦除成功)
3. 诊断仪 -> ECU: 34 00 44 08 00 00 00 00 01 00 00 (请求下载)
4. ECU -> 诊断仪: 74 00 10 00 (允许下载,每包4096字节)
5. 诊断仪 -> ECU: 36 01 [数据...] (发送第1包)
6. ECU -> 诊断仪: 76 01 (接收成功)
7. 诊断仪 -> ECU: 36 02 [数据...] (发送第2包)
8. ECU -> 诊断仪: 76 02 (接收成功)
... (重复直到数据发完)
9. 诊断仪 -> ECU: 37 (请求退出传输)
10. ECU -> 诊断仪: 77 (退出成功)
11. 诊断仪 -> ECU: 31 01 FF 01 [校验参数] (启动校验)
12. ECU -> 诊断仪: 71 01 FF 01 (校验通过)
你看,0x31 负责擦除和校验,0x34/0x36/0x37 负责数据传输。各司其职,缺一不可。
嗯,这里有个经验之谈:擦除和校验的例程 ID 不是固定的。不同厂商、不同 ECU 用的 ID 都不一样。我见过用 0xFF00 的,也见过用 0x0202 的。所以,写脚本前一定要看 ECU 的 诊断规范,别想当然。
3.6 避坑指南
最后,我总结几个常见的坑,你们写脚本时注意一下:
- 0x34 的地址和长度格式:一定要和 ECU 匹配,否则 NRC 0x13 伺候。
- 0x36 的序列号:从 1 开始,连续递增,不能跳号。
- 0x37 后的等待:ECU 可能还在内部处理,别急着发下一步。
- 0x31 的例程 ID:不同 ECU 不一样,别写死。
- 超时处理:每个服务都有超时时间,比如 0x31 的擦除可能耗时 5 秒,你的脚本要能等。
好了,这一章就到这里。下一章咱们聊聊 刷写流程的状态机设计,看看怎么把这些服务串成一个完整的自动化脚本。