1. DoIP协议基础:ISO 13400标准概述
各位同学好,我是老张。今天咱们聊聊DoIP协议的基础。说实话,我刚接触车载诊断时,用的还是CAN诊断那一套。后来上了DoIP项目,第一反应是——这不就是把CAN诊断报文塞进以太网里吗?
嗯,后来发现事情没那么简单。DoIP(Diagnostic over Internet Protocol)背后有一套完整的标准体系,核心就是ISO 13400。我个人习惯把ISO 13400拆成三块来看:物理层怎么连、传输层怎么传、应用层怎么解析。咱们一个一个说。
1.1 ISO 13400标准体系
ISO 13400其实是个系列标准,分了好几个部分。我列个表,大家看着更清楚:
| 标准编号 | 内容 | 我的理解 |
|---|---|---|
| ISO 13400-1 | 通用信息和用例定义 | 说白了就是告诉你DoIP能干啥 |
| ISO 13400-2 | 传输层和网络层要求 | 这是最核心的部分,报文怎么发、怎么收 |
| ISO 13400-3 | 基于IEEE 802.3的物理层 | 就是以太网物理层,100BASE-TX或1000BASE-T |
| ISO 13400-4 | 基于IEEE 802.11的物理层 | 无线诊断,这个用得少,但趋势明显 |
| ISO 13400-5 | 一致性测试 | 你做的DoIP能不能过认证,就看这个 |
我记得第一次看ISO 13400-2时,被里面各种定时器搞得头大。什么T_TCP_EST、T_TCP_TERM、T_CR... 后来做项目才发现,这些定时器一个都不能错,错了ECU就掉线。
1.2 物理层与传输层
物理层这块,ISO 13400-3规定得很清楚。车载环境用的是100BASE-TX,也就是百兆以太网。为什么不用千兆?你想想看,诊断数据量再大,也大不到哪去。百兆完全够用,而且成本低、功耗小。
我在项目中遇到过一个问题:某款车用1000BASE-T做DoIP,结果EMC测试死活过不去。后来换成100BASE-TX,加了个共模扼流圈,一次通过。嗯,这里要注意——车载以太网的物理层设计,EMC是头等大事。
传输层用的是TCP和UDP。TCP用于诊断数据,UDP用于发现和通告。为什么这么分?
- TCP:可靠传输,诊断报文一个都不能丢。丢了ECU可能就进入错误状态了。
- UDP:广播/组播,用于车辆发现。你想想,诊断仪刚连上车载网络,怎么知道有哪些ECU?靠UDP广播。
重要概念:DoIP的端口号
UDP 13400 —— 用于车辆发现和通告
TCP 13400 —— 用于诊断数据交换
这两个端口号是固定的,ISO 13400规定的。我建议你在设计网关时,把这两个端口写死在代码里,别搞成可配置的。为什么?我曾经见过一个项目,把端口号配错了,结果诊断仪死活连不上车,查了两天才发现是端口号问题。
1.3 DoIP报文结构解析
好,到了最核心的部分。DoIP报文长什么样?我直接上代码:
// DoIP报文头部结构
typedef struct {
uint8_t protocol_version; // 协议版本,当前为0x02
uint8_t inverse_version; // 版本取反,用于校验
uint16_t payload_type; // 载荷类型
uint16_t payload_length; // 载荷长度
uint8_t payload[]; // 实际数据
} DoIP_Header_t;
这个结构很简单,就4个字段。但每个字段都有讲究:
- protocol_version:当前是0x02。ISO 13400-2:2012版本。以后可能会变。
- inverse_version:0xFD。就是0x02取反。接收端会校验这个字段,不匹配直接丢弃。
- payload_type:这个最重要,决定了报文干什么用。下面我列个表。
- payload_length:4字节,最大支持2^32-1字节的数据。但实际中谁会发这么大?
payload_type是关键。ISO 13400-2定义了十几种类型,我挑几个常用的:
| 类型值 | 名称 | 方向 | 说明 |
|---|---|---|---|
| 0x0001 | Generic DoIP header negative acknowledge | 双向 | 通用否定应答,报文格式错了就回这个 |
| 0x0002 | Vehicle Identification Request | 诊断仪→车辆 | 谁在线?报上名来 |
| 0x0004 | Vehicle Identification Response | 车辆→诊断仪 | 我在这!这是我的VIN、IP地址 |
| 0x0005 | Routing Activation Request | 诊断仪→ECU | 我要激活诊断路由 |
| 0x0006 | Routing Activation Response | ECU→诊断仪 | 激活成功/失败,给你个socket |
| 0x8001 | Diagnostic Message | 双向 | 真正的UDS诊断数据 |
个人经验:调试DoIP时,我最先看的就是payload_type。如果报文类型不对,后面的数据解析全是错的。我习惯在代码里加个switch-case,把每种类型都打印出来。调试阶段特别有用。
1.4 一个完整的DoIP报文示例
咱们看个实际的例子。假设诊断仪发送一个车辆识别请求:
// 车辆识别请求报文(十六进制)
02 FD 00 02 00 00 00 00
// 解析:
// 02 —— 协议版本
// FD —— 版本取反
// 00 02 —— payload_type = 0x0002 (Vehicle Identification Request)
// 00 00 00 00 —— payload_length = 0 (没有额外数据)
这个报文发出去后,支持DoIP的ECU会回复:
// 车辆识别响应报文(十六进制)
02 FD 00 04 00 00 00 14
// 后面跟着20字节的VIN码 + 其他信息
注意:DoIP报文头部是8字节固定长度。但payload_length字段的值不包括头部本身。也就是说,如果payload_length=0,整个报文就是8字节。这个细节我在项目中踩过坑——有个同事把payload_length算成了包含头部,结果接收端一直报格式错误。
1.5 路由激活:建立诊断通道的关键
车辆识别之后,下一步就是路由激活。这个过程说白了就是诊断仪告诉ECU:「我要用你来做诊断,给我开个通道。」
路由激活请求报文:
// 路由激活请求
02 FD 00 05 00 00 00 07
// 激活类型: 0x01 (WWH-OBD)
// 保留: 0x00 0x00
// 源地址: 0x0E 0x00 (诊断仪的逻辑地址)
ECU收到后,会分配一个逻辑地址给诊断仪,并返回激活响应。如果激活成功,后续的诊断报文就可以通过这个通道传输了。
我记得在某个项目中,路由激活总是失败。查了半天,发现是ECU的socket资源用完了。ISO 13400规定最多支持16个并发路由激活,但有些ECU实现得少。后来我们在代码里加了资源管理,激活前先检查可用socket数。
1.6 小结
DoIP协议基础就这些。说白了,它就是在以太网上跑UDS诊断。但多了车辆发现、路由激活这些步骤。我个人觉得,理解DoIP的关键是抓住两点:
- 报文结构:8字节头部 + 载荷。头部里的payload_type决定了报文干什么用。
- 通信流程:车辆发现 → 路由激活 → 诊断通信。三步走,一步都不能少。
下一章咱们聊DoIP网关的软件架构设计。到时候我会拿一个实际项目的代码出来,给大家看看网关内部是怎么处理这些报文的。
好,今天就到这。有问题随时问我。