3、刷写核心服务:34h/36h/37h服务详解、数据传输流程、块大小与时序控制
好,咱们进入刷写最核心的部分了。34h、36h、37h这三个服务,说白了就是UDS刷写的三驾马车。你搞懂了它们,远程刷写就通了八成。我个人习惯把这仨服务叫做“开门、搬货、关门”。
3.1 34h服务:请求下载(开门)
34h服务,全称“Request Download”。它的任务就是告诉ECU:“嘿,我要开始给你传数据了,你准备好没?”
这个服务的请求报文长这样:
| Byte | 参数名 | 描述 |
|------|-----------------|-------------------------------|
| 0 | 34h | 服务ID |
| 1 | 数据格式 | bit0-3: 压缩方法, bit4-7: 加密方法 |
| 2-3 | 地址和长度格式信息 | 指定后续地址和长度的字节数 |
| 4-n | 内存地址 | 刷写起始地址 |
| n+1-m| 内存大小 | 要刷写的数据总长度 |
嗯,这里要注意一个坑。地址和长度格式信息这个字节,很多人会搞错。它决定了后面地址和长度各占几个字节。比如0x24,表示地址占2字节,长度占4字节。
ECU收到34h后,如果一切正常,会回复一个肯定响应:
| Byte | 参数名 | 描述 |
|------|-----------------|-------------------------------|
| 0 | 74h | 肯定响应ID |
| 1 | 最大块大小 | ECU一次能接收的最大数据长度 |
这个“最大块大小”非常关键。它决定了你后面36h服务每次能传多少数据。说白了,ECU告诉你:“我胃口就这么大,你一次别喂太多。”
3.2 36h服务:传输数据(搬货)
36h服务,全称“Transfer Data”。这就是真正搬砖的活了。你拿着34h拿到的“最大块大小”,把数据切成一块一块,用36h服务发过去。
请求报文结构:
| Byte | 参数名 | 描述 |
|------|-----------------|-------------------------------|
| 0 | 36h | 服务ID |
| 1 | 块序列号 | 从1开始递增 |
| 2-n | 数据 | 实际要写入的数据 |
块序列号从1开始,每次加1。ECU会检查这个序列号,防止丢包或乱序。我见过有些工程师图省事,序列号随便填,结果ECU直接拒绝写入。
ECU的肯定响应很简单:
| Byte | 参数名 | 描述 |
|------|-----------------|-------------------------------|
| 0 | 76h | 肯定响应ID |
| 1 | 块序列号 | 当前成功接收的块序列号 |
这里有个小细节。ECU返回的序列号,是你发过去的那个。如果ECU返回的序列号和你预期的不一样,说明出问题了。我建议你在上位机里加个校验,每次收到响应后比对一下序列号。
3.3 37h服务:请求退出传输(关门)
37h服务,全称“Request Transfer Exit”。数据传完了,你得告诉ECU一声:“活干完了,你收尾吧。”
请求报文:
| Byte | 参数名 | 描述 |
|------|-----------------|-------------------------------|
| 0 | 37h | 服务ID |
就这么简单,没有额外参数。ECU收到后,会做两件事:
- 检查接收到的数据完整性(比如CRC校验)
- 把数据从临时缓冲区写入真正的Flash区域
肯定响应:
| Byte | 参数名 | 描述 |
|------|-----------------|-------------------------------|
| 0 | 77h | 肯定响应ID |
3.4 数据传输流程:完整链路
好了,三个服务都讲完了。咱们串起来看看完整流程:
- 诊断会话控制:先切到扩展会话(10 03)
- 安全访问:解锁ECU(27 01/27 02)
- 请求下载:发送34h,拿到最大块大小
- 传输数据:循环发送36h,每次不超过最大块大小
- 请求退出传输:发送37h,结束传输
- 检查状态:用22h服务读取刷写状态
你想想看,这个流程其实和咱们平时下载文件很像。先建立连接(34h),然后分块下载(36h),最后关闭连接(37h)。
3.5 块大小与时序控制
块大小,就是34h响应里那个“最大块大小”。它受什么影响?
- ECU的RAM大小:缓冲区越大,块大小可以越大
- CAN总线负载:块太大,会占用总线太久
- Flash写入速度:ECU写入Flash需要时间,块太大可能超时
我建议的块大小范围:
| 总线类型 | 建议块大小 | 说明 |
|---|---|---|
| CAN 2.0 | 4-8字节 | CAN帧数据场最大8字节 |
| CAN FD | 64字节 | CAN FD数据场最大64字节 |
| 以太网 | 512-4096字节 | 取决于ECU的TCP/IP栈 |
时序控制这块,说白了就是“别太快,也别太慢”。
- 发送间隔:我一般设10-20ms。太快了ECU处理不过来,太慢了刷写时间太长
- 超时时间:ECU处理36h服务,一般要求在50ms内响应。超过这个时间,上位机应该重发
- 重试次数:我习惯设3次。超过3次还失败,直接报错,别死循环
嗯,这里还要提一下流控制。在CAN总线上,如果ECU处理不过来,它会发一个流控制帧(FC),告诉上位机“慢点”。上位机必须遵守这个流控制,否则ECU会丢包。
我个人习惯在代码里加一个动态调整机制。如果连续收到3次流控制,就自动降低发送速率。如果连续10次没有流控制,就适当提高速率。这样既能保证稳定性,又能提高刷写效率。
好了,34h/36h/37h这三个服务,加上块大小和时序控制,就是刷写的核心。你把这些搞明白了,剩下的就是具体实现细节了。下一章咱们聊聊刷写中的错误处理和诊断,那才是真正考验工程师水平的地方。