3. 诊断协议栈入门

在车载网络HIL与诊断测试工程中,诊断协议栈是连接测试上位机与ECU内部诊断服务的“语言层”。同一根以太网线或CAN总线上,可能同时跑着OBD-II排放诊断、UDS统一诊断服务和DoIP诊断传输。理解它们各自负责什么、如何寻址、如何切换会话、如何解锁安全等级,是后续编写诊断测试用例、搭建HIL台架和排查诊断失败的基础。

一、三种协议的分工与承载关系

OBD-II是法规驱动的排放相关诊断,服务号固定、数据以PID(Parameter ID)组织,典型请求如02 01 0C读取发动机转速,响应返回41 01 0C 1A F8,其中1A F8按公式换算为转速。它面向通用扫描工具,寻址方式简单,通常使用功能寻址广播请求,ECU各自应答。

UDS(ISO 14229)是面向整车研发与售后的统一诊断服务,服务号从0x10到0x3E等,覆盖会话控制、安全访问、读写数据、例程控制、DTC读取等。UDS本身只定义应用层服务,不绑定具体总线,可以跑在CAN、CAN FD、FlexRay或以太网上。

DoIP(ISO 13400)解决的是“UDS如何走以太网”的问题。它在TCP/IP之上定义诊断报文封装、车辆发现、路由激活和诊断电源模式。诊断仪先通过UDP向车辆广播车辆识别请求,获得车辆识别响应后建立TCP连接,再发送路由激活请求,之后才能传输UDS诊断报文。

诊断协议栈分层与寻址路径 诊断仪 / HIL上位机 OBD-II 应用 PID 0x01 / 0x0C UDS 应用 0x10 / 0x27 / 0x22 DoIP 传输层 UDP 13400 / TCP CAN / 以太网驱动 SocketCAN / VN5610 物理接口 OBD口 / 以太网口 CAN_H/CAN_L 协议栈映射关系 OBD-II:法规服务,PID寻址,功能寻址广播 SAE J1979 / ISO 15031-5 UDS:统一诊断服务,会话/安全/数据/例程 ISO 14229-1 应用层 DoIP:车辆发现、路由激活、诊断报文封装 ISO 13400-2 传输层 CAN TP / DoIP 承载 ISO 15765-2 / TCP/IP 物理层:CAN / 100BASE-TX 目标 ECU OBD 服务 PID 响应 0x41 UDS 服务 0x50 / 0x67 / 0x62 DoIP 实体 逻辑地址 0x0E00 诊断栈 会话/安全状态机 应用层 DTC / 数据 / 例程 刷写 / 标定 寻址:OBD功能寻址广播 → UDS物理/功能寻址 → DoIP逻辑地址+路由激活

二、核心服务与典型请求响应

UDS的核心服务可以按用途分组。会话控制0x10用于切换默认会话、编程会话和扩展会话;安全访问0x27通过种子密钥机制解锁受保护服务;读写数据0x22/0x2E按DID访问数据;例程控制0x31启动或停止ECU内部例程;DTC读取0x19获取故障码及快照;通信控制0x28控制报文收发;刷写相关服务包括0x34/0x36/0x37。

以读取VIN为例,请求为22 F1 90,正响应为62 F1 90后跟17字节VIN。若ECU未处于支持该DID的会话,会返回否定响应7F 22 31,其中31表示requestOutOfRange;若安全未解锁,则返回7F 22 33,33表示securityAccessDenied。这些否定响应码是诊断测试中判断失败原因的关键依据。

三、会话管理与安全访问的状态迁移

ECU上电后默认处于默认会话(0x01),此时只能执行基础诊断。要执行写入或刷写,必须先切换到扩展会话(0x03)或编程会话(0x02)。会话切换有超时机制,通常由S3定时器控制,默认约5秒无诊断请求则自动回落到默认会话。因此测试脚本在长流程中需要周期性发送3E 00(TesterPresent)维持会话。

安全访问采用种子密钥挑战。以0x27服务为例,子功能0x01请求种子,ECU返回67 01加种子;诊断仪用约定算法计算密钥,再发送27 02加密钥。若密钥正确,返回67 02;若错误,返回7F 27 35(invalidKey)。连续错误通常触发延时锁定,需要等待或重新上电。安全等级分为多个级别,不同级别解锁不同的DID和例程。

四、诊断寻址方式

诊断寻址决定了报文发给谁、谁来回。物理寻址是点对点,请求发往特定ECU的地址,只有该ECU响应,适合读取特定DID或刷写。功能寻址是广播,多个ECU可同时响应,适合OBD排放查询或UDS的3E 00维持会话。在CAN上,物理寻址使用不同的CAN ID区分请求和响应;在DoIP上,使用逻辑地址(如诊断仪0x0E00、ECU 0x0001)加路由激活来建立点对点通道。

DoIP的车辆发现流程值得单独关注:诊断仪向UDP 13400端口广播车辆识别请求0x0001,车辆返回车辆识别响应0x0004,其中包含VIN、逻辑地址和进一步动作信息。随后诊断仪与目标ECU建立TCP连接,发送路由激活请求0x0005,激活成功后返回0x0006,此时才能发送UDS诊断报文。若路由激活失败,常见原因是逻辑地址不匹配或车辆未处于诊断电源模式。

五、HIL测试中的实操要点

在HIL台架上,诊断测试通常通过CANoe、vTESTstudio或Python的udsoncan库实现。以下是一个使用Python udsoncan读取VIN的示例片段,展示会话切换、安全访问和DID读取的典型顺序:

from udsoncan.client import Client
from udsoncan.connections import PythonIsoTpConnection
import isotp

# 建立ISO-TP连接,CAN ID按实际DBC配置
conn = PythonIsoTpConnection(isotp.TransportLayer(
    rxid=0x7E8, txid=0x7E0, interface="socketcan", channel="can0"))

with Client(conn, request_timeout=2) as client:
    # 1. 切换到扩展会话
    client.change_session(0x03)
    # 2. 安全访问:请求种子并发送密钥
    seed = client.request_seed(0x01)
    key = bytes([b ^ 0xFF for b in seed])  # 示例算法,实际按ECU规范
    client.send_key(0x02, key)
    # 3. 读取VIN
    vin = client.read_data_by_identifier(0xF190)
    print("VIN:", vin.service_data.values[0xF190].decode())

这段代码的关键观察点在于:change_session返回正响应50 03后才继续;request_seed返回的种子长度和内容需与ECU规范一致;send_key后若返回67 02表示解锁成功。若任一步返回否定响应,应打印完整的7F响应码,结合NRC表定位问题,而不是直接重试。

在HIL测试中,建议将诊断请求和响应同时记录到总线日志中,并标注时间戳。会话超时、安全锁定和路由激活失败往往表现为“偶发失败”,只有结合时间轴才能判断是S3超时、密钥错误还是DoIP路由未激活。
安全访问的种子密钥算法属于ECU供应商的保密内容,测试脚本中不应硬编码真实密钥算法。在HIL环境中,通常由ECU仿真模型或诊断描述文件(ODX/CDD)提供算法接口,测试脚本通过调用接口完成解锁,避免密钥泄露。

总结来看,OBD-II、UDS和DoIP分别对应法规诊断、统一诊断服务和以太网诊断传输。理解它们的服务号、会话状态、安全等级和寻址方式,才能在HIL台架上正确构造请求、解读响应,并在诊断失败时快速定位到会话、安全或路由层面的原因。