第4章:DoIP协议:诊断通信、车辆发现、路由激活,OTA诊断会话管理
好,咱们今天聊聊DoIP。说实话,这个协议在OTA升级里太关键了。你想想看,没有它,你连车都发现不了,更别提升级了。
DoIP,全称是Diagnostic over Internet Protocol。说白了,就是把传统的UDS诊断报文,封装到TCP/IP协议栈里传输。为什么要这么做?因为传统CAN总线带宽不够啊。OTA升级动辄几百兆甚至几个G的固件,用CAN传?那得传到猴年马月去。
4.1 车辆发现机制
车辆发现,这是DoIP的第一步。我刚开始做这个功能时,就踩过一个坑——测试设备死活找不到车。后来才发现,是车辆端的DoIP实体没有正确响应广播。
车辆发现的核心流程是这样的:
- 测试设备(比如你的诊断仪)发送一个UDP广播报文,目标端口是UDP 3049
- 报文内容是一个特定的VIN请求,格式是0x0004(逻辑地址)+ 0x0000(源地址)
- 车辆端的DoIP实体收到后,会回复一个车辆公告报文
- 公告里包含了VIN、逻辑地址、EID(实体ID)、GID(组ID)等信息
关键点:车辆发现用的是UDP广播,不是TCP。因为此时还没有建立连接。UDP是无连接的,一发一收,效率高。
我个人的习惯是,在测试设备端做一个超时重试机制。因为车辆可能正在休眠,或者网络有延迟。一般我会设置3次重试,每次间隔500ms。如果3次都收不到响应,那就报错。
4.2 路由激活
车辆发现之后,接下来就是路由激活。这一步很多人容易搞混。路由激活不是建立TCP连接,而是告诉车辆:嘿,我要开始跟你通信了,请给我分配一个逻辑地址。
流程是这样的:
- 测试设备先跟车辆建立TCP连接,端口是TCP 13400
- 然后发送一个路由激活请求报文
- 请求里包含测试设备的逻辑地址(比如0x0E80)
- 车辆收到后,会检查这个地址是否合法
- 如果合法,车辆回复一个路由激活响应,状态码是0x10(成功)
- 如果不合法,比如地址冲突,状态码就是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会话管理流程是这样的:
- 先发送10 03切换到扩展会话
- 然后发送27 01请求种子,进行安全访问
- 收到种子后,计算密钥,发送27 02解锁
- 解锁成功后,发送10 02切换到编程会话
- 在编程会话下,执行刷写操作(比如34 36 37服务)
- 刷写完成后,发送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升级就成功了一半。