第4章:DoIP协议:诊断通信、车辆发现、路由激活,OTA诊断会话管理

好,咱们今天聊聊DoIP。说实话,这个协议在OTA升级里太关键了。你想想看,没有它,你连车都发现不了,更别提升级了。

DoIP,全称是Diagnostic over Internet Protocol。说白了,就是把传统的UDS诊断报文,封装到TCP/IP协议栈里传输。为什么要这么做?因为传统CAN总线带宽不够啊。OTA升级动辄几百兆甚至几个G的固件,用CAN传?那得传到猴年马月去。

4.1 车辆发现机制

车辆发现,这是DoIP的第一步。我刚开始做这个功能时,就踩过一个坑——测试设备死活找不到车。后来才发现,是车辆端的DoIP实体没有正确响应广播。

车辆发现的核心流程是这样的:

  1. 测试设备(比如你的诊断仪)发送一个UDP广播报文,目标端口是UDP 3049
  2. 报文内容是一个特定的VIN请求,格式是0x0004(逻辑地址)+ 0x0000(源地址)
  3. 车辆端的DoIP实体收到后,会回复一个车辆公告报文
  4. 公告里包含了VIN、逻辑地址、EID(实体ID)、GID(组ID)等信息

关键点:车辆发现用的是UDP广播,不是TCP。因为此时还没有建立连接。UDP是无连接的,一发一收,效率高。

我个人的习惯是,在测试设备端做一个超时重试机制。因为车辆可能正在休眠,或者网络有延迟。一般我会设置3次重试,每次间隔500ms。如果3次都收不到响应,那就报错。

4.2 路由激活

车辆发现之后,接下来就是路由激活。这一步很多人容易搞混。路由激活不是建立TCP连接,而是告诉车辆:嘿,我要开始跟你通信了,请给我分配一个逻辑地址。

流程是这样的:

  1. 测试设备先跟车辆建立TCP连接,端口是TCP 13400
  2. 然后发送一个路由激活请求报文
  3. 请求里包含测试设备的逻辑地址(比如0x0E80)
  4. 车辆收到后,会检查这个地址是否合法
  5. 如果合法,车辆回复一个路由激活响应,状态码是0x10(成功)
  6. 如果不合法,比如地址冲突,状态码就是0x02(地址冲突)或0x03(地址被占用)

小技巧:我曾经遇到过一个问题,测试设备发送路由激活请求后,车辆一直不响应。后来发现是TCP连接建立后,车辆端没有及时处理。解决方案是在车辆端做一个异步处理,不要阻塞在TCP接收上。

嗯,这里要注意一点。路由激活成功后,车辆会把这个测试设备的信息记录下来。以后这个设备发送的诊断请求,车辆都会优先处理。如果多个设备同时激活,车辆会按照激活顺序排队处理。

4.3 诊断通信

路由激活完成后,就可以进行诊断通信了。DoIP的诊断通信,本质上就是把UDS报文封装在DoIP的Payload里传输。

一个典型的DoIP诊断请求报文格式如下:

| 协议版本 (1字节) | 反向协议版本 (1字节) | Payload类型 (2字节) | Payload长度 (4字节) | Payload (变长) |
| 0x02             | 0xFD                 | 0x8001              | 0x00000008          | UDS请求数据     |

Payload类型0x8001表示诊断请求,0x8002表示诊断响应。这个在ISO 13400-2里有详细定义。

我建议你在实现时,重点关注Payload长度字段。因为这个字段是网络字节序(大端),很多新手容易搞反。我曾经见过一个项目,就是因为字节序搞错了,导致车辆端解析出来的长度是0x01000000,直接崩溃了。

4.4 OTA诊断会话管理

好了,终于到了最核心的部分——OTA诊断会话管理。在OTA升级过程中,我们需要切换诊断会话,才能执行刷写操作。

UDS诊断会话有三种:

会话类型 会话ID 说明
默认会话 0x01 车辆上电后的默认状态,功能受限
编程会话 0x02 用于刷写ECU固件,功能最全
扩展会话 0x03 用于诊断和配置,比默认会话功能多

OTA升级时,我们需要从默认会话切换到编程会话。切换的指令是UDS的10 02服务。但这里有个坑——切换会话需要安全访问认证。

警告:千万不要在编程会话里做其他操作!我曾经见过一个同事,在编程会话里发送了读取故障码的请求,结果导致ECU死机。因为编程会话下,ECU的资源都集中在刷写上,其他功能可能被禁用。

我个人习惯的OTA会话管理流程是这样的:

  1. 先发送10 03切换到扩展会话
  2. 然后发送27 01请求种子,进行安全访问
  3. 收到种子后,计算密钥,发送27 02解锁
  4. 解锁成功后,发送10 02切换到编程会话
  5. 在编程会话下,执行刷写操作(比如34 36 37服务)
  6. 刷写完成后,发送10 01回到默认会话

为什么要先切换到扩展会话?因为安全访问服务(27服务)在默认会话下可能不可用。你想想看,如果谁都能在默认会话下解锁,那安全性就太差了。

还有一个细节——会话超时。每个会话都有一个超时时间,默认会话一般是5秒,编程会话可能更长,比如30秒。如果在超时时间内没有收到任何诊断请求,ECU会自动切回默认会话。所以,在刷写过程中,要定期发送TesterPresent(3E 80)来保持会话。

我曾经在项目里遇到过一个问题:刷写一个大固件,耗时超过30秒,结果会话超时了,ECU自动切回了默认会话,刷写失败。后来我加了一个心跳线程,每2秒发送一次3E 80,问题就解决了。

4.5 实际项目中的避坑指南

最后,我总结几个实际项目中容易踩的坑:

  • TCP连接管理:DoIP的TCP连接是长连接,不要频繁断开重连。每次重连都要重新做路由激活,浪费时间。
  • 并发处理:车辆端要支持多个DoIP连接吗?ISO 13400建议至少支持2个。但实际项目中,我建议只支持1个,避免多个设备同时刷写导致冲突。
  • VIN校验:车辆发现时,测试设备会校验VIN。如果VIN不匹配,车辆不会响应。这个在产线上特别重要,防止刷错车。
  • 报文分片:DoIP的Payload最大支持多少?ISO 13400-2里说最大是4GB。但实际网络环境里,TCP报文会被分片。所以接收端要做好重组处理。

嗯,今天就聊这么多。DoIP协议看起来复杂,但说白了就是一套规则——怎么发现车、怎么建立连接、怎么传数据、怎么管理会话。把这些规则搞清楚了,OTA升级就成功了一半。