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的关键是抓住两点:

  1. 报文结构:8字节头部 + 载荷。头部里的payload_type决定了报文干什么用。
  2. 通信流程:车辆发现 → 路由激活 → 诊断通信。三步走,一步都不能少。

下一章咱们聊DoIP网关的软件架构设计。到时候我会拿一个实际项目的代码出来,给大家看看网关内部是怎么处理这些报文的。

好,今天就到这。有问题随时问我。