诊断协议 UDS 与 OBD

车载诊断协议全解:UDS 服务分类与会话管理、SecurityAccess 安全访问的种子密钥机制、DTC 与故障管理、OBD-II 法规要求、34/36/37 刷写流程与内存地址校验、ODX/CDD 描述文件、P2/P2* 时序参数,以及诊断自动化的测试框架与刷写失败排查方法。

引言

诊断是车载软件里最「古老」也最实用的部分。无论是产线下线检测、售后维修读故障码、还是远程 OTA 刷写,走的都是同一套诊断协议:UDS(ISO 14229)定义服务,ISO-TP(ISO 15765-2)或 DoIP(ISO 13400)负责传输,OBD-II 是法规强制的排放诊断子集。

UDS 的设计哲学是「请求-响应」:诊断仪(Tester)发一个请求,ECU 回一个响应(肯定或否定)。看似简单,但工程上有大量细节:会话切换的状态机、安全访问的种子密钥算法、故障码的存储与快照、刷写的地址校验与分块、以及严格的时序参数(P2/P2*)。

难点集中在刷写与安全:刷写流程错了会让 ECU「变砖」(必须靠 Bootloader 与 A/B 分区兜底),安全访问算法被破解会让攻击者能刷入恶意固件(R155 关注的核心)。本文逐层拆解。

目录

  1. 诊断栈概览:UDS on CAN 与 DoIP
  2. 服务分类与会话管理
  3. 安全访问 SecurityAccess
  4. DTC 与故障管理
  5. OBD-II 与法规要求
  6. 刷写流程 34/36/37
  7. 诊断描述 ODX 与 CDD
  8. 诊断时序参数 P2 与 P2*
  9. 诊断测试与自动化

1. 诊断栈概览:UDS on CAN 与 DoIP

诊断协议是分层的,UDS 是应用层,下面可以走 CAN 或以太网:

UDS on CAN(DoCAN):
  UDS(ISO 14229)       应用层
  ISO-TP(ISO 15765-2)  传输层(分段与重组)
  CAN(ISO 11898)       数据链路层
  CAN 物理层

UDS on IP(DoIP):
  UDS(ISO 14229)       应用层
  DoIP(ISO 13400)      传输层 + 路由激活
  TCP/UDP + IP           网络层
  100BASE-T1             物理层

ISO-TP 分段(DoCAN):
  单帧(SF):≤ 7 字节数据(经典 CAN)
  首帧(FF):多帧的第一帧,含总长度
  连续帧(CF):后续帧,含序号
  流控帧(FC):接收方控制发送节奏(BS、STmin)

  示例(发送 20 字节):
    FF: 10 14 <6 字节数据>      ← 总长 0x014 = 20
    FC: 30 00 00                 ← 允许发送,块大小 0,间隔 0
    CF: 21 <7 字节>
    CF: 22 <7 字节>

ISO-TP 的流控参数(块大小 BS、最小间隔 STmin)配错会导致传输超时或丢帧。

2. 服务分类与会话管理

UDS 服务按功能分为六组,会话(Session)决定哪些服务可用:

服务分组(按 SID 高位):
  0x10~0x1F 诊断与通信管理
  0x20~0x2F 数据传输
  0x30~0x3F 传输控制(刷写)
  0x40~0x5F 输入输出控制
  0x60~0x7F 存储传输
  0x80~0x9F 例程与故障

常用服务:
  0x10 DiagnosticSessionControl  切换会话
  0x11 ECUReset                  复位
  0x14 ClearDiagnosticInformation 清 DTC
  0x19 ReadDTCInformation        读 DTC
  0x22 ReadDataByIdentifier      读数据
  0x27 SecurityAccess            安全访问
  0x28 CommunicationControl      通信控制
  0x2E WriteDataByIdentifier     写数据
  0x31 RoutineControl            例程控制
  0x34 RequestDownload           请求下载
  0x36 TransferData              传输数据
  0x37 RequestTransferExit       退出传输
  0x3E TesterPresent             保持会话
  0x85 ControlDTCSetting         控制 DTC 记录

会话类型:
  0x01 默认会话(Default):只读服务
  0x02 编程会话(Programming):刷写,需先解锁
  0x03 扩展会话(Extended):标定、例程
  0x04 安全系统会话(Safety System)

会话超时(S3):
  默认会话不超时
  非默认会话 5 秒无请求 → 自动回默认会话
  诊断仪需周期发 0x3E(TesterPresent)保活

3. 安全访问 SecurityAccess

0x27 服务用「种子-密钥」挑战应答防止未授权操作:

流程:
  1. Tester: 27 01(请求种子,奇数子功能)
  2. ECU:    67 01 <seed>(返回随机种子,如 4 字节)
  3. Tester: 27 02 <key>(发送计算出的密钥)
  4. ECU:    67 02(解锁成功)或 7F 27 35(密钥错误)

子功能:
  0x01 请求种子(Level 1)
  0x02 发送密钥(Level 1)
  0x03/0x04 Level 2
  ...
  0x11/0x12 ... 更高等级

密钥算法:
  厂商私有,常见形式:
    key = AES128(seed, secret_key) 的前 4 字节
    key = seed XOR 固定掩码
    key = 查表 / 线性变换

  错误次数限制:
    连续 3 次密钥错误 → 锁定一段时间(如 10 秒)
    防止暴力破解

安全等级示例:
  Level 1:写 VIN、清故障码
  Level 2:标定参数
  Level 3:刷写固件(配合编程会话)

安全访问是 R155 的重点审查对象——算法必须足够强,且密钥不能硬编码在可提取的位置。

4. DTC 与故障管理

DTC(Diagnostic Trouble Code)是故障的标准化编码:

DTC 组成(3 字节):
  Byte 1~2:故障码(如 P0301 = 气缸 1 失火)
  Byte 3:故障类型字节(FTB)
    bit0-3:故障类型(如 0x01 一般电气故障)
    bit4-5:故障类别
    bit6-7:确认状态

  例:P0301-00
    P = Powertrain(动力总成)
    0 = 通用码(1 厂商自定义)
    301 = 具体故障

DTC 状态字节(8 bit,ISO 14229-1):
  bit0 testFailed             当前测试失败
  bit1 testFailedThisOperationCycle  本循环失败过
  bit2 pendingDTC             待定
  bit3 confirmedDTC           已确认
  bit4 testNotCompletedSinceLastClear 清除后未测试
  bit5 testFailedSinceLastClear 清除后失败过
  bit6 testNotCompletedThisOperationCycle 本循环未测试
  bit7 warningIndicatorRequested 请求点亮故障灯

冻结帧(Freeze Frame):
  故障发生瞬间的快照数据
  如发生时的车速、转速、温度、里程
  用于售后复现问题

0x19 子功能:
  0x01 报告 DTC 数量
  0x02 按状态掩码报告 DTC
  0x04 报告 DTC 快照记录
  0x06 报告 DTC 扩展数据
  0x0A 报告所有支持的 DTC

故障管理(Dem 模块)负责 DTC 的触发条件、去抖、存储与老化(长时间未复现则清除)。

5. OBD-II 与法规要求

OBD-II 是排放相关的法规诊断,比 UDS 更严格地受政府监管:

OBD-II 服务(9 种,法规强制):
  0x01 当前数据(实时 PID)
  0x02 冻结帧数据
  0x03 排放相关 DTC
  0x04 清除 DTC
  0x05 氧传感器测试结果
  0x06 车载监测结果(非连续监测)
  0x07 当前循环 DTC
  0x08 控制车载系统
  0x09 车辆信息(VIN、标定 ID)

PID(参数 ID):
  0x0C 发动机转速
  0x0D 车速
  0x05 冷却液温度
  0x2F 燃油液位

法规要求:
  美国:EPA / CARB,OBD-II 强制
  欧洲:EOBD,欧 5 起强制
  中国:国六 OBD,与国标一致
  Malfunction Indicator Lamp(MIL):故障时点亮仪表灯
  监测器就绪状态(Readiness):年检必查

OBD-II 与 UDS 共享物理层,但服务集是 UDS 的子集(PID 概念对应 UDS 的 DID)。

6. 刷写流程 34/36/37

刷写是诊断最复杂的场景,涉及内存地址与 Bootloader:

刷写流程:
  1. 0x10 0x02          切编程会话
  2. 0x27 0x11/0x12     安全访问解锁(刷写等级)
  3. 0x31 0x01 0xFF00   擦除内存(例程控制)
     或 0x31 0x01 <eraseRoutine>
  4. 0x34 <addr> <size> 请求下载
     请求参数:地址(4 字节)+ 大小(4 字节)+ 格式
     ECU 回:0x74 + 最大块长度(如 0x0402 = 1026 字节)
  5. 0x36 <block>      传输数据(可多次)
     每块不超过 ECU 返回的最大块长度
  6. 0x37               请求传输退出
  7. 0x31 0x01 <check>  校验(CRC/哈希)
  8. 0x11 0x01          复位
  9. 新固件启动

关键参数:
  地址:逻辑地址或物理地址,与 Bootloader 约定
  块长度:ECU 限制(受 RAM 缓冲大小约束)
  CRC:通常 CRC-32 或 SHA-256

Bootloader 职责:
  接收刷写数据、写入 Flash、校验、跳转到应用
  应用损坏时能重新进入 Bootloader(救命通道)
  通过 CAN ID 或专用引脚触发进入

A/B 分区:
  应用跑在 A 区,新固件写入 B 区
  校验通过后切换启动标志
  失败可回滚到 A 区

刷写失败的最坏情况是 ECU 变砖,所以 Bootloader 必须独立于应用且不可被刷写覆盖。

7. 诊断描述 ODX 与 CDD

诊断的「接口契约」由描述文件定义:

ODX(Open Diagnostic data eXchange,ISO 22901):
  标准化 XML 格式,描述诊断数据
  包含:服务、DID、DTC、参数、通信参数
  以 PDX(打包的 ODX)分发

CDD(CANdela Diagnostic Description):
  Vector 的私有格式,用 CANdelaStudio 编辑
  可导出为 ODX

ODX 结构:
  <DIAG-LAYER>
    <DIAG-COMM>  诊断通信(请求/响应)
      <REQUEST>  <RESPONSE>
    <DIAG-DATA-DICTIONARY-SPEC> 数据字典
      <DATA-OBJECT-PROP>  数据对象(DID、DTC)
    <COMPARAM-SPEC>  通信参数(P2、STmin)

为什么需要:
  诊断仪靠 ODX 生成请求,无需硬编码
  ECU 靠 ODX 生成诊断处理代码
  版本错配 → 诊断失败

工程现实:
  一套完整 ODX 几十 MB
  与 ECU 软件版本强绑定
  变更管理是难点

8. 诊断时序参数 P2 与 P2*

时序参数决定诊断的实时性与超时行为:

P2(P2_server_max):
  ECU 收到请求后到发出响应的最大时间
  典型值:50 ms(默认会话),5000 ms(编程会话)

P2*(P2*_server_max):
  ECU 发送「响应挂起」(0x78)后,下一次响应的最大时间
  典型值:5000 ms(默认),5000~10000 ms(编程)

S3(S3_server):
  非默认会话超时,典型 5000 ms

诊断仪侧参数:
  P2_client:等待响应的超时(通常 P2 + 余量)
  STmin:连续帧最小间隔(ISO-TP)
  BlockSize:每多少帧等流控

NRC 0x78(ResponsePending):
  ECU 需要更多时间处理时先回 0x78
  诊断仪重置 P2* 计时器等待
  擦除 Flash 时常用(耗时几秒)

时序配错的表现:
  P2 太短 → 诊断仪误判超时,实际 ECU 还在处理
  P2 太长 → 故障响应迟钝
  S3 太短 → 会话频繁掉线

9. 诊断测试与自动化

诊断测试是产线与整车验证的重要环节:

测试层次:
  1. 单元级:ECU 台架,验证每个服务的响应
  2. 集成级:整车网络,验证诊断路由与网关
  3. 产线级:EOL 测试,自动化跑全套
  4. 售后级:诊断仪 + ODX 验证

自动化工具:
  CANoe.DiVa:基于 ODX 自动生成诊断测试用例
  CAPL 脚本:编写自定义测试
  Python + udsoncan:开源诊断库

Python 示例(udsoncan):
  import udsoncan
  from udsoncan.connections import IsoTPSocketConnection
  from udsoncan.client import Client
  from udsoncan.services import *

  conn = IsoTPSocketConnection('can0', rxid=0x7E8, txid=0x7E0)
  with Client(conn, request_timeout=2) as client:
      client.change_session(DiagnosticSessionControl.Session.programmingSession)
      client.unlock_security_access(0x11)
      # 读 VIN
      resp = client.read_data_by_identifier(0xF190)
      print(resp.service_data.values[0xF190])

EOL 测试内容:
  读软件版本、VIN
  读/清 DTC
  执行例程(自检)
  写配置参数
  单车耗时 2~5 分钟

权衡取舍

决策点选项 A选项 B建议
传输DoCANDoIP小 ECU 用 DoCAN,大包刷写用 DoIP
安全算法简单 XORAES刷写用强算法(AES),普通用轻量
刷写分区单分区A/B 分区支持 OTA 的必须 A/B,可回滚
块长度大(1024)小(256)受 ECU RAM 限制,取最大值提效率
描述文件ODXCDD跨工具协作用 ODX,Vector 生态用 CDD
DTC 存储RAMNVM确认故障必须存 NVM,掉电不丢

常见坑清单

  1. 会话未保活:非默认会话 5 秒超时自动回默认,诊断仪需周期发 0x3E。
  2. 安全访问次数无限:可被暴力破解,必须加错误次数限制与锁定。
  3. 刷写不校验:跳过 0x31 校验直接复位,损坏固件导致变砖。
  4. Bootloader 被覆盖:应用刷写范围包含 Bootloader 区,必须严格分区。
  5. ODX 版本错配:诊断仪请求格式与 ECU 不一致,服务不支持,需强绑定版本。
  6. P2 超时太短:ECU 擦除 Flash 耗时几秒,需先回 0x78 并配 P2*。
  7. ISO-TP 流控参数不当:块大小或 STmin 配错导致丢帧,按 ECU 能力协商。
  8. DTC 未做去抖:瞬态干扰误报故障,需去抖计数器与确认逻辑。
  9. 冻结帧未存:故障复现困难,关键 DTC 必须存冻结帧与扩展数据。
  10. 无 A/B 分区:一次失败刷写即需返厂,OTA 车型必须支持回滚。

小结

UDS 是车载诊断的统一语言:0x10 管会话、0x27 管安全、0x22/0x2E 管数据、0x19 管故障、0x34/36/37 管刷写。理解它的钥匙是三条线——会话状态机(什么时候能做什么)、安全访问(谁能做)、时序参数(多久算超时)。

实践路径:先用 udsoncan 或 CANoe 跑通基本服务(读版本、读 DTC),再实现安全访问,然后做完整刷写流程,最后接入自动化测试。刷写的完整链路与 OTA 结合见 车载 OTA 升级与安全 ;诊断的网络层细节见 CAN FD 与车载以太网 。

故障管理的理论基础与功能安全相关,见 功能安全 ISO 26262 工程实践 ;若关注诊断的网络安全(防篡改),可参考 车载网络安全加固 。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「车载软件」更多文章

  1. 三电与动力总成软件
  2. 车载软件测试与 HIL 台架
  3. ADAS 决策规划与横纵向控制