4、UDS诊断服务详解(上):请求下载(0x34)、数据传输(0x36)、请求退出传输(0x37)、ECU复位(0x11)
各位同学,今天我们正式进入UDS诊断服务的核心环节。说实话,这部分内容在OTA升级里是真正的硬骨头。你想想看,没有这些诊断服务,ECU就是个封闭的黑盒子,你根本没法把新固件塞进去。
我个人习惯把UDS诊断服务比作「ECU的通信语言」。你要让ECU干活,就得用它能听懂的话。今天讲的这四个服务,正好覆盖了OTA升级中最关键的几个动作:下载、传输、结束、复位。
4.1 请求下载(0x34)—— 升级的起跑线
0x34服务,说白了就是告诉ECU:「嘿,我要给你发新东西了,你准备好了吗?」
我在项目中遇到过好几次,有些同事上来就直接发数据,结果ECU根本不响应。为什么?因为你没先发0x34请求,ECU根本不知道你要干嘛。
0x34的请求报文格式是这样的:
请求: 0x34 + 数据格式标识符 + 地址信息 + 内存大小信息
响应: 0x74 + 数据格式标识符 + 最大数据块长度
这里有个关键点——最大数据块长度。ECU会告诉你它一次能接收多少数据。我见过有些ECU只能一次收4KB,有的能收64KB。你要是发多了,ECU直接给你丢回来。
重要提示:0x34服务必须在编程会话(Programming Session)下才能执行。如果你在默认会话下发0x34,ECU会回复否定响应码0x7F + 0x34 + 0x78(错误会话)。
实际项目中,我建议你在发0x34之前,先确认一下ECU当前处于什么会话状态。嗯,这里要注意,有些ECU在扩展会话下也能接受0x34,但这不是标准做法,别依赖这个。
4.2 数据传输(0x36)—— 真正的搬砖活
0x36服务,就是实际的数据搬运工。你想想看,固件文件动辄几MB甚至几十MB,不可能一次性发完。所以UDS设计了这个分块传输的机制。
0x36的报文格式:
请求: 0x36 + 块序列号 + 数据块
响应: 0x76 + 块序列号
块序列号从1开始,每发一块就加1。ECU收到后会回复同样的序列号,表示确认收到。
我曾经踩过一个坑:块序列号溢出。有些ECU的块序列号只有1个字节,最大255。如果你要传的文件太大,块数超过255怎么办?答案是——别让它超过。要么加大每块的数据量,要么在0x34阶段就规划好。
我的经验:每块数据大小建议设为ECU返回的最大数据块长度的80%。为什么留20%余量?因为有些ECU在处理数据时会有内部缓冲限制,你卡着上限发,容易触发溢出。
数据传输过程中,ECU可能会回复否定响应。常见的否定码有:
| 否定码 | 含义 | 常见原因 |
|---|---|---|
| 0x13 | 报文长度错误 | 你发的数据块大小超过了ECU声明的最大长度 |
| 0x31 | 请求超出范围 | 块序列号不对,或者数据内容有问题 |
| 0x72 | Flash编程失败 | ECU在写入Flash时出错了,比如校验失败 |
遇到0x72错误,基本意味着你得重新开始整个下载流程。所以,数据传输过程中的校验机制非常重要。我个人习惯在每块数据末尾加一个CRC16校验,虽然UDS标准没强制要求,但能省掉很多麻烦。
4.3 请求退出传输(0x37)—— 优雅地结束
0x37服务,就是告诉ECU:「数据发完了,你可以开始处理了。」
这个服务看起来简单,但很多人会忽略它的重要性。你想想看,如果你不发0x37,ECU会一直处于等待数据的状态。有些ECU有超时机制,等几分钟没收到新数据就自动退出;有些ECU则傻等着,直到你发0x37或者ECU断电。
0x37的报文格式:
请求: 0x37
响应: 0x77
就这么简单,没有额外参数。但ECU收到0x37后,内部会做几件事:
- 检查所有数据是否完整接收
- 验证数据的完整性(比如CRC校验)
- 把数据从临时缓冲区搬到正式存储区
我在项目中遇到过一个问题:发了0x37后,ECU回复了0x77,但紧接着又发了一个0x7F + 0x37 + 0x72。这说明什么?说明ECU在内部处理时发现数据有问题。所以,0x77响应不代表升级成功,只代表ECU收到了你的退出请求。
注意:有些ECU在收到0x37后,会进入一个不可中断的内部处理流程。这时候你发任何诊断请求,ECU都可能不响应。所以,发完0x37后,建议等待至少2秒再发下一个请求。
4.4 ECU复位(0x11)—— 让新固件生效
0x11服务,就是让ECU重启。为什么OTA升级需要这个?因为新固件刷进去后,ECU需要重新初始化才能运行新代码。
0x11的报文格式:
请求: 0x11 + 复位类型
响应: 0x51
复位类型常见的有三种:
- 0x01:硬复位 —— 相当于断电重启,ECU完全重新上电
- 0x02:软复位 —— 相当于软件重启,不涉及硬件断电
- 0x03:快速启动 —— 部分ECU支持,跳过一些初始化步骤
我个人建议,OTA升级完成后使用硬复位(0x01)。为什么?因为软复位可能不会清除所有内部状态,有些老数据还留在内存里,可能导致新固件运行异常。
我曾经遇到过一个案例:某ECU刷完固件后,用软复位启动,结果新固件跑不起来。排查了半天,发现是软复位没清掉一个全局变量,那个变量在新固件里已经被移除了,但旧值还在,导致程序崩溃。换成硬复位后,问题就解决了。
关键点:发完0x11复位请求后,ECU会立即执行复位,不会给你发响应。所以,你发完0x11后,需要等待ECU重新上线。这个等待时间因ECU而异,有的几百毫秒,有的好几秒。
实际项目中,我一般会在发完0x11后,循环发送0x3E(Tester Present)请求,直到ECU响应为止。这样能准确知道ECU什么时候复位完成。
4.5 四个服务的配合流程
好了,四个服务都讲完了。它们在实际OTA升级中是怎么配合的?我给你画个流程图:
1. 进入编程会话(0x10 + 0x02)
2. 请求下载(0x34)—— 告诉ECU我要发数据了
3. 数据传输(0x36)—— 循环发送数据块
4. 请求退出传输(0x37)—— 数据发完了
5. ECU复位(0x11)—— 让新固件生效
6. 等待ECU重新上线
7. 验证新固件版本
这个流程看起来简单,但每个环节都可能出问题。我建议你在开发阶段,把每个服务的否定响应都记录下来,分析一下ECU为什么拒绝你的请求。很多时候,问题出在时序上——比如你发0x34太快,ECU还没准备好。
我的习惯:每个诊断请求之间至少间隔50ms。别小看这50ms,它能避免很多莫名其妙的通信问题。有些ECU的处理速度慢,你发得太快,它根本来不及处理。
好了,今天的内容就到这里。这四个服务是OTA升级的基石,你理解了它们,后面的内容就好办了。下节课我们讲剩下的几个诊断服务,包括安全访问、例程控制等,这些都是升级过程中必不可少的环节。
记住一句话:诊断服务不是写代码,是写协议。你写的每一行代码,最终都要转化成ECU能理解的诊断报文。所以,把协议吃透,比什么都重要。