2、刷写协议详解:UDS协议中的34/36/37服务

各位同学,今天我们聊点硬核的——UDS刷写三剑客:34、36、37服务。

说实话,我刚入行那会儿,看到这三个服务号就头大。34、36、37,长得跟双胞胎似的,搞不清楚谁先谁后。后来被师傅骂了几次,才真正搞明白它们的逻辑关系。

说白了,这三个服务就是刷写的标准三步走:请求下载 → 传输数据 → 结束传输。你想想看,这不就跟我们下载文件一样吗?先跟服务器打个招呼说我要下载了,然后一段一段收数据,最后说声拜拜。

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

34服务是刷写的第一步。它的作用就是告诉ECU:「嘿,我要开始写数据了,你准备好了吗?」

我个人习惯把这个服务叫做「握手服务」。为什么?因为它确实是在握手——客户端和服务端互相确认一下,看看能不能开始干活。

请求格式:

字节0: 34 (服务ID)
字节1: 数据格式 (bit7-4: 压缩方法, bit3-0: 加密方法)
字节2-3: 地址和长度格式信息
字节4-7: 内存起始地址 (根据格式信息长度可变)
字节8-11: 内存大小 (根据格式信息长度可变)

这里有个坑,我必须要提醒大家。字节1的数据格式,很多人容易搞混。0x00表示不压缩不加密,0x01表示只压缩不加密,0x11表示既压缩又加密。嗯,这里要注意,实际项目中绝大多数情况都是0x00,因为刷写文件本身就已经是压缩好的了。

响应格式:

字节0: 74 (肯定响应)
字节1: 最大数据块长度 (MaxNumberOfBlockLength)

这个最大数据块长度很关键。它告诉客户端:你每次最多能发多少字节。我在项目中遇到过,有的ECU只支持256字节,有的能支持4096字节。如果你发的数据块超过了这个限制,ECU会直接给你个否定响应。

避坑指南:我曾经在一个项目里,因为没仔细看这个最大数据块长度,直接发了2KB的数据块,结果ECU一直报错。排查了半天才发现是这个问题。所以,拿到34服务的响应后,一定要先解析这个值。

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

36服务是刷写的主体。说白了,就是真正干活的部分。

它的格式很简单:

字节0: 36 (服务ID)
字节1: 块序列号 (BlockSequenceCounter)
字节2-N: 数据块内容

块序列号从1开始,每发一包就加1。为什么要这个序列号?你想想看,如果网络不好,数据包丢了怎么办?ECU可以通过序列号判断有没有丢包。

我记得有一次调试,发现ECU刷写到一半就停了。查了半天,原来是序列号从0x01跳到了0x03,中间丢了0x02。ECU检测到序列号不连续,直接罢工了。

36服务的响应:

字节0: 76 (肯定响应)
字节1: 块序列号 (与请求中的序列号相同)

这里有个小技巧:每次收到76响应后,再发下一包。不要一口气发太多,否则ECU处理不过来。我建议的做法是:发一包,等响应,再发下一包。虽然慢一点,但稳定。

个人经验:实际项目中,我一般会把数据块大小设置成ECU支持的最大值。比如ECU支持4096字节,我就用4096。这样能减少交互次数,刷写速度会快很多。但要注意,有些ECU虽然支持大块传输,但处理速度慢,这时候就要适当减小数据块大小。

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

37服务是刷写的收尾工作。它的作用就是告诉ECU:「数据发完了,你可以开始处理了。」

格式超级简单:

字节0: 37 (服务ID)

没错,就一个字节。不需要任何参数。

响应格式:

字节0: 77 (肯定响应)

收到77响应后,ECU就会开始处理接收到的数据。比如校验完整性、写入Flash等。这个过程可能需要几秒钟,具体看数据量大小。

重要提醒:发送37服务后,千万不要立即断电!我曾经有个同事,刷写完成后直接拔了电源,结果ECU变砖了。因为ECU还在处理数据,突然断电会导致数据不完整。正确的做法是:等ECU处理完成,再断电。

2.4 刷写流程与状态机

好了,三个服务都讲完了。现在我们把它们串起来,看看完整的刷写流程。

标准刷写状态机:

IDLE → 发送34服务 → WAIT_DOWNLOAD
WAIT_DOWNLOAD → 收到74响应 → 发送36服务 → DATA_TRANSFER
DATA_TRANSFER → 收到76响应 → 继续发送36服务 → DATA_TRANSFER
DATA_TRANSFER → 数据发送完毕 → 发送37服务 → WAIT_EXIT
WAIT_EXIT → 收到77响应 → IDLE (刷写完成)

这个状态机看起来简单,但实际项目中会有各种异常情况。比如:

  • 发送34服务后,ECU返回否定响应怎么办?
  • 数据传输过程中,ECU超时了怎么办?
  • 发送37服务后,ECU一直不响应怎么办?

我个人的做法是:每个状态都设置超时时间。比如34服务超时设为5秒,36服务超时设为2秒,37服务超时设为10秒。超时后重试3次,如果还不行,就报错退出。

核心要点:
  • 34服务:建立连接,获取最大数据块长度
  • 36服务:分段传输数据,注意序列号连续性
  • 37服务:结束传输,等待ECU处理完成
  • 状态机:严格按顺序执行,处理异常情况

最后说一句,刷写协议看着简单,但实际调试起来坑很多。我建议大家在开发阶段,多用CANoe或者PCAN的日志功能,把每次交互的数据都记录下来。出了问题,翻翻日志,基本都能找到原因。

好了,这一章就到这里。下一章我们聊聊刷写中的安全机制——安全访问和校验和。这些东西在量产项目中特别重要,搞不好就会出大问题。