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

各位好,欢迎来到实战手册的第四讲。

今天咱们聊聊UDS诊断服务里最常用的三个“老面孔”——0x22、0x19和0x2E。说实话,这三个服务我几乎每天都在用。不管是产线刷写还是售后排查,它们都是绕不开的基础操作。

你想想看,一个TBOX连上车之后,第一件事是什么?肯定是先看看车在说什么。读数据、读故障码、写配置,这三板斧打好了,后面的事情就顺了。

4.1 读取数据(0x22)—— 想查什么就查什么

0x22服务,全称是“ReadDataByIdentifier”。说白了,就是通过一个ID去拿ECU里的某个数据。这个ID叫DID(Data Identifier),每个DID对应一个具体的数据项。

报文结构长什么样?

请求报文很简单:

| 字节 | 内容           | 说明                     |
|------|----------------|--------------------------|
| 0    | 0x22           | 服务ID                   |
| 1-2  | DID (高字节+低字节) | 比如 0xF190 表示VIN码    |

正响应报文:

| 字节 | 内容           | 说明                     |
|------|----------------|--------------------------|
| 0    | 0x62           | 服务ID + 0x40            |
| 1-2  | DID            | 回显请求中的DID          |
| 3..N | 数据           | 实际读取到的数据内容     |

负响应报文:

| 字节 | 内容           | 说明                     |
|------|----------------|--------------------------|
| 0    | 0x7F           | 负响应固定开头           |
| 1    | 0x22           | 回显请求的服务ID         |
| 2    | NRC            | 否定响应码               |

常见DID举例:

  • 0xF190:VIN码(17字节ASCII)
  • 0xF186:ECU硬件版本号
  • 0xF187:ECU软件版本号
  • 0xF18C:ECU序列号
  • 0x0100-0xFFFF:各OEM自定义的DID

我个人习惯,在开发TBOX远程诊断功能时,会把常用的DID整理成一个配置文件。这样换项目时改改配置就行,不用动代码。嗯,这里要注意:不同OEM对DID的定义可能完全不同,千万别拿A项目的DID去B项目用,会出大问题。

小技巧: 如果你要读多个DID,可以用0x22一次请求多个DID吗?答案是不行。0x22一次只能读一个DID。想批量读?后面我们会讲0x23服务,那个是专门干这个的。

4.2 读取故障码(0x19)—— 车到底哪里不舒服?

0x19服务,全称是“ReadDTCInformation”。这个服务比0x22复杂多了,因为它有子功能(SubFunction)。

为什么会这样?因为故障码的信息太丰富了。你不仅要读当前故障码,还要读历史故障码、故障发生时的快照数据、故障发生的次数等等。一个服务ID搞不定,所以用子功能来区分。

最常用的子功能:

子功能(SubFunction) 含义 说明
0x01 读取当前故障码数量 返回当前激活的DTC个数
0x02 读取当前故障码 返回所有当前激活的DTC列表
0x0A 读取所有故障码(含历史) 返回所有存储的DTC
0x04 读取故障码快照数据 返回故障发生时的环境数据
0x06 读取故障码扩展数据 返回故障发生次数、老化计数器等

举个例子,读当前故障码:

请求: 0x19 0x02
响应: 0x59 0x02 [DTC1高字节] [DTC1低字节] [状态字节] [DTC2高字节] [DTC2低字节] [状态字节] ...

这里的状态字节很关键。它用8个bit告诉你这个故障码的“身世”:

  • Bit 0:测试失败(当前有故障)
  • Bit 1:当前故障(确认过)
  • Bit 2:历史故障(曾经有过)
  • Bit 3:老化计数器
  • Bit 4-7:保留位

注意: 我曾经在项目里踩过一个坑——读故障码时只看了Bit0,以为没故障就完事了。结果客户反馈说车偶尔报故障,查了半天才发现是历史故障码没清。所以,读故障码时一定要看完整的状态字节,尤其是Bit2(历史故障)和Bit3(老化计数器)。

另外,0x19服务还有一个很实用的功能——读取故障码快照数据(子功能0x04)。这个数据记录了故障发生那一瞬间的车辆状态,比如车速、水温、发动机转速等。对于远程诊断来说,这个数据太重要了。我经常跟团队说:“没有快照数据的故障码,就像没有监控录像的事故现场,你永远不知道当时发生了什么。”

4.3 写入数据(0x2E)—— 给ECU“写纸条”

0x2E服务,全称是“WriteDataByIdentifier”。和0x22正好相反,它是往ECU里写数据的。

报文结构:

请求: 0x2E [DID高字节] [DID低字节] [数据...]
响应: 0x6E [DID高字节] [DID低字节] [数据...]

看起来很简单对吧?但这里有个大坑——不是所有DID都能写。有些DID是只读的,你写进去ECU会给你一个NRC(否定响应码)。

常见的NRC:

NRC 含义 说明
0x31 请求超出范围 DID不存在或数据格式不对
0x72 写入条件不满足 比如需要先进入扩展会话才能写
0x13 数据长度错误 你给的数据长度和DID定义的不一致

避坑指南: 我曾经在远程刷写配置时,用0x2E写一个DID,结果一直返回0x72。查了半天才发现,那个DID需要在“扩展会话模式”下才能写。所以,写数据之前一定要确认两件事:1)当前会话模式对不对;2)安全访问(Security Access)解锁了没有。

实际应用场景:

  • 写VIN码:产线刷写时,用0x2E把VIN码写入ECU
  • 写配置参数:比如远程修改TBOX的上报周期、服务器地址等
  • 写个性化设置:比如调整座椅记忆、灯光模式等

你想想看,如果没有0x2E服务,那TBOX的远程配置功能就完全没法实现了。每次改配置都得去4S店用诊断仪连车,多麻烦。

4.4 三个服务的协作关系

这三个服务在实际项目中经常配合使用。我举个例子:

  1. 先用0x19读故障码,发现某个传感器报“电压过高”
  2. 再用0x22读该传感器的实时电压值,确认当前状态
  3. 最后用0x2E写入一个校准参数,调整传感器的阈值

你看,读故障码发现问题,读数据确认问题,写数据解决问题。一套组合拳下来,远程诊断就闭环了。

总结一下:

  • 0x22:读数据,一次一个DID,简单直接
  • 0x19:读故障码,子功能多,信息丰富
  • 0x2E:写数据,注意会话模式和权限

好了,这一讲就到这里。下一讲我们会继续聊UDS诊断服务的“下半场”——0x23(批量读取)、0x14(清除故障码)和0x10(诊断会话控制)。这些服务在远程诊断中同样重要,咱们到时候再细聊。

记住一句话:诊断服务是TBOX和ECU沟通的“语言”,把这门语言学好了,远程诊断功能开发就成功了一半。