4、UDS诊断服务详解(上):读取数据(0x22)、读取故障码(0x19)、写入数据(0x2E)、例程控制(0x31)

各位工程师朋友,今天我们聊聊UDS诊断服务里最常用的四个服务。说实话,这四兄弟在项目里出现的频率极高,你几乎每天都会跟它们打交道。我个人习惯把这四个服务称为「诊断四件套」——读数据、读故障码、写数据、跑例程。

嗯,咱们一个一个来拆解。

4.1 读取数据(0x22)—— 诊断的「眼睛」

0x22服务,说白了就是让ECU把内部数据吐出来给你看。你想知道车速?水温?电池电压?统统用这个服务去拿。

报文结构长这样:

请求:22 + DID(2字节) + 可选子字节
响应:62 + DID + 数据字节

举个例子,读取发动机转速(假设DID = 0x010C):

发送:22 01 0C
回复:62 01 0C 0F A0  → 转速 = 0x0FA0 = 4000 rpm
我的小技巧: 实际项目中,DID的定义每个OEM都不一样。我建议你建一个DID速查表,贴在工位上。我曾经因为记错DID,把一个温度传感器的值当成了电压值读出来,结果分析半天数据都对不上,后来才发现是DID搞混了。

这里有个坑要注意——NRC 0x31(请求超出范围)。如果你发的DID在当前会话模式下不支持,ECU就会甩这个错误回来。比如有些DID只能在扩展会话(0x03)下读取,你在默认会话(0x01)下发,肯定吃闭门羹。

4.2 读取故障码(0x19)—— 诊断的「侦探」

0x19服务,用来读取ECU里存储的故障码。这个服务比0x22复杂不少,因为它有14个子功能。不过别慌,实际项目里常用的就那几个。

常用子功能一览:

子功能 含义 我常用的场景
0x01 读取故障码数量 快速判断ECU有没有报故障
0x02 读取故障码(按状态掩码) 最常用,筛选当前激活的故障
0x0A 读取所有故障码 全量导出,用于下线检测
0x0E 读取故障码快照 分析故障发生时的环境数据

举个例子,读取当前所有激活的故障码(状态掩码 = 0x09):

发送:19 02 09
回复:59 02 01 01 00 11 22 33  → 1个故障码,DTC = 0x112233
注意: 故障码的状态掩码是个位掩码,每一位代表不同含义。bit0 = 测试失败,bit1 = 当前故障,bit2 = 历史故障... 我曾经见过新手工程师把掩码写成了0x01,结果只读到了测试失败的故障,漏掉了当前激活的故障,排查了半天。

我个人习惯用0x0A子功能做全量导出,然后用脚本解析。这样效率高,不容易漏数据。

4.3 写入数据(0x2E)—— 诊断的「手」

0x2E服务,就是往ECU里写数据。比如标定参数、配置信息、清除学习值等等。

报文结构:

请求:2E + DID(2字节) + 数据字节
响应:6E + DID + 数据字节

举个例子,写入VIN码(假设DID = 0xF190):

发送:2E F1 90 4C 53 56 38 30 30 30 30 31 32 33 34 35 36 37
回复:6E F1 90 4C 53 56 38 30 30 30 30 31 32 33 34 35 36 37
核心要点: 0x2E服务通常需要先进入扩展会话(0x10 03),并且可能需要安全访问(0x27)解锁。你想想看,如果随便谁都能往ECU里写数据,那车还不得乱套?

嗯,这里要注意——写入的数据长度必须和DID定义的长度一致。多一个字节或少一个字节,ECU都会返回NRC 0x13(报文长度错误)。我遇到过最离谱的一次,是有人把VIN码写成了18个字节,但DID定义的是17个字节,结果ECU直接锁死了写入功能,最后只能重新刷写固件才恢复。

4.4 例程控制(0x31)—— 诊断的「手术刀」

0x31服务,用来执行ECU内部的一段程序。比如自学习、传感器校准、执行器测试等等。这个服务有三个子功能:

子功能 含义 典型应用
0x01 启动例程 开始执行某个功能
0x02 停止例程 中断正在执行的例程
0x03 查询例程结果 获取执行状态或结果数据

举个例子,启动一个「节气门自学习」例程(假设RID = 0x0201):

发送:31 01 02 01
回复:71 01 02 01 00  → 例程启动成功
避坑指南: 我曾经在项目里遇到一个问题——启动例程后,ECU一直不返回响应。后来发现是因为例程执行时间太长(超过5秒),而诊断仪的超时时间设得太短。所以,启动例程前,一定要先确认例程的预期执行时间,适当调整超时设置。

另外,有些例程是「一次性」的,启动后就不能再启动了,直到ECU重新上电。比如某些传感器的校准例程。如果你连续发两次启动请求,第二次就会收到NRC 0x22(条件不满足)。

4.5 四个服务的对比与选择

最后,我做个简单的对比,帮你快速决策:

服务 用途 需要安全访问? 常见NRC
0x22 读取数据 通常不需要 0x31(DID不支持)
0x19 读取故障码 不需要 0x12(子功能不支持)
0x2E 写入数据 通常需要 0x13(长度错误)、0x31(DID不支持)
0x31 执行例程 通常需要 0x22(条件不满足)、0x31(RID不支持)

说白了,这四个服务覆盖了诊断开发中80%以上的需求。你只要把这四个服务吃透了,日常的测试工作基本就能应付自如。下一章我们继续聊剩下的几个服务——0x27安全访问、0x28通信控制、0x3E待机握手,这些都是诊断链路里的「守门员」角色。

好,今天就到这里。有什么问题,欢迎在群里交流。