4、UDS诊断协议与测试:UDS服务(10/22/2E/31)、诊断会话控制、DTC读写与清除

好,咱们今天聊UDS诊断。说实话,UDS是VCU测试里绕不开的一座大山。你想想看,车跑到路上出了故障,售后工程师拿诊断仪往OBD口一插,靠的就是UDS协议跟ECU对话。如果UDS实现有bug,轻则读不到故障码,重则刷写失败导致控制器变砖——我在项目里真见过这种事。

UDS的全称是 Unified Diagnostic Services,也就是统一诊断服务。它定义在ISO 14229标准里。咱们VCU测试中,最常用的服务就那么几个:10服务(诊断会话控制)、22服务(读取数据标识符)、2E服务(写入数据标识符)、31服务(例程控制),再加上DTC相关的19服务(读取故障码)和14服务(清除故障码)。今天咱们一个一个捋清楚。

4.1 诊断会话控制(10服务)

10服务是UDS的入口。说白了,ECU上电后默认处于一个“非诊断”状态,你想跟它说上话,必须先发一个10 01或者10 03,把它切换到对应的会话模式。

我个人习惯把会话模式分成三类:

  • 默认会话(01):上电后的初始状态,只响应最基本的请求,比如读VIN码。很多诊断服务在这个模式下是被禁用的。
  • 编程会话(02):用于刷写软件。这个模式下,ECU会关闭一些通信报文,腾出总线带宽给刷写用。我记得有一次测试,忘了切到编程会话就直接发34服务(请求下载),结果ECU直接回了NRC 0x78(请求正确接收-响应待定),搞得我排查了半天。
  • 扩展诊断会话(03):这是咱们测试中最常用的模式。读写数据、执行例程、读写DTC,基本都在这个模式下完成。

重要提醒:会话切换是有时效的。ECU内部有个S3定时器,默认一般是5秒。如果在5秒内没有收到任何诊断请求,ECU会自动切回默认会话。我建议你在测试脚本里,每隔2-3秒发一个10 01或者10 03来保持会话活跃。

测试10服务时,我一般会验证这几个点:

  • 从默认会话切换到扩展会话,响应应该是50 03(肯定响应)
  • 从不支持的会话模式(比如10 05),ECU应该回NRC 0x12(子功能不支持)
  • 会话切换后,检查ECU是否真的进入了对应模式——比如在编程会话下,某些DTC相关的报文是否被抑制了

4.2 读取数据标识符(22服务)与写入数据标识符(2E服务)

22服务和2E服务,一个是读,一个是写。它们操作的对象是DID(Data Identifier),每个DID对应一个特定的数据,比如电池SOC、电机转速、系统电压等等。

22服务的报文格式很简单:22 + DID(2字节)。比如读取DID 0xF190(电池SOC),就发 22 F1 90。ECU回复 62 F1 90 + 数据

2E服务则是写入:2E + DID(2字节)+ 数据。比如把DID 0xF190写入50%,就发 2E F1 90 32(假设数据长度1字节,50%就是0x32)。

我的经验:22服务一般不需要切到扩展会话就能用,但2E服务几乎都要求在扩展会话下执行。我曾经踩过一个坑——在默认会话下发2E请求,ECU回了NRC 0x7F(服务不支持),我还以为是DID地址写错了,后来才发现是会话模式不对。

测试22/2E服务时,我建议你关注以下几点:

  • DID的有效性:发一个不存在的DID,ECU应该回NRC 0x12(子功能不支持)或0x31(请求超出范围)
  • 数据长度校验:比如某个DID定义的是2字节数据,你只发了1字节,ECU应该回NRC 0x13(消息长度错误或格式无效)
  • 写入条件:有些DID只能在特定条件下写入(比如车速为0时才能写里程)。我测试时会在不同车速下尝试写入,看ECU是否按预期拒绝
  • 边界值测试:比如DID定义范围是0-100,你写101,ECU应该回NRC 0x31

4.3 例程控制(31服务)

31服务用来执行ECU内部的一段程序。比如“清除学习值”、“执行自检”、“写入VIN码”等等。它有三个子功能:01(开始)、02(停止)、03(查询结果)。

报文格式:31 + 子功能 + RID(2字节)+ 可选数据。RID是例程标识符,每个RID对应一个具体的例程。

举个例子,执行一个“写入VIN码”的例程:

请求:31 01 02 01 4C 53 56 33 32 31 30 30 31 32 33 34 35
      (子功能01=开始,RID 0x0201,后面是VIN码数据)
响应:71 01 02 01
      (肯定响应,表示例程已开始执行)

注意:31服务是“有副作用的”。它可能会修改ECU的NVRAM(非易失性存储器),或者触发硬件动作。我在测试一个“电机堵转自检”的例程时,没注意车辆状态,结果电机真的转起来了——还好当时车轮是悬空的。所以,执行31服务前,一定要确认车辆处于安全状态。

测试31服务的要点:

  • 例程的启动条件:比如某些例程要求车速为0、档位在P档。不满足条件时,ECU应该回NRC 0x22(条件不正确)
  • 例程的执行时间:有些例程执行时间较长(比如几分钟),ECU会先回一个肯定响应,然后异步执行。你需要轮询查询结果(子功能03)
  • 中断例程:发子功能02来停止正在执行的例程,验证ECU是否能正确中断
  • 重复执行:连续两次执行同一个例程,看ECU是否报NRC 0x21(忙-重复请求)

4.4 DTC读写与清除(19服务与14服务)

DTC(Diagnostic Trouble Code)是诊断故障码。19服务用来读取,14服务用来清除。

19服务有多个子功能,咱们测试中最常用的是:

  • 01:按状态读取DTC数量。比如发 19 01 FF,ECU回复当前所有DTC的数量
  • 02:按状态读取DTC列表。比如发 19 02 FF,ECU回复所有DTC的编号和状态
  • 04:读取快照数据。每个DTC可以关联一组快照数据(比如故障发生时的车速、电压等)
  • 06:读取扩展数据。比如DTC发生的次数、最后发生的时间等

14服务就简单了:14 + 组号(3字节)。组号 FF FF FF 表示清除所有DTC,00 00 00 表示清除所有排放相关的DTC。

避坑指南:我曾经遇到一个情况——用14服务清除了DTC,但ECU的故障指示灯(MIL)还是亮的。后来发现,有些DTC是“历史故障”,清除后状态会变成“已测试-未完成”,但MIL灯需要满足一定条件才会熄灭。所以,清除DTC后,最好再读一下DTC状态,确认是否真的清干净了。

测试DTC相关服务的建议:

  • 注入故障:通过CANoe或类似工具,模拟一个故障条件(比如断开某个传感器),让ECU生成DTC。然后读DTC,验证编号和状态是否正确
  • 清除后验证:清除DTC后,再读一次,确认DTC列表为空,或者状态变为“已测试-通过”
  • 掉电保持:生成DTC后,下电再上电,读DTC是否仍然存在(DTC应该存储在NVRAM中)
  • 重复清除:连续发多次14服务,看ECU是否每次都正确响应

嗯,UDS这块内容确实不少,但只要你把10、22、2E、31、19、14这几个服务吃透了,VCU诊断测试的基本盘就稳了。我个人的习惯是,先搭一个简单的测试脚本,把每个服务的肯定响应和否定响应都跑一遍,然后再做边界和异常测试。这样一步步来,不容易漏掉东西。