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诊断测试的基本盘就稳了。我个人的习惯是,先搭一个简单的测试脚本,把每个服务的肯定响应和否定响应都跑一遍,然后再做边界和异常测试。这样一步步来,不容易漏掉东西。