3、诊断服务之数据读取(0x22):通过ID(DID)读取数据、常见DID定义规范、读取VIN码与ECU软件版本号的实战案例

3.1 0x22服务到底在干什么?

说实话,很多刚入行的工程师会把0x22和0x23搞混。0x22是读数据,0x23是读内存,两者完全不同。

0x22服务的核心逻辑很简单:你给ECU一个ID,ECU把对应的数据返回给你。这个ID就是DID(Data Identifier),一个16位的数字。每个DID背后,都对应着ECU内部的一段数据。

我习惯把DID理解成「钥匙」。你拿着正确的钥匙,才能打开对应的数据抽屉。钥匙不对,ECU会直接给你扔个否定响应回来。

关键点:0x22服务是UDS里最基础、最常用的服务之一。几乎所有诊断场景都会用到它。

3.2 DID定义规范——不是随便写的

DID不是拍脑袋定的。行业内有一套不成文的规范,大部分OEM都会遵循。

常见的DID范围划分如下:

DID范围(十六进制) 用途 举例
0x0000 - 0x00FF ISO 14229-1 标准定义 0x00F1(VIN码)
0x0100 - 0x01FF 车辆制造商自定义 0x0100(ECU硬件号)
0x0200 - 0x02FF 系统供应商自定义 0x0201(Bootloader版本)
0xF000 - 0xFFFF OEM内部专用 0xF190(生产日期)

嗯,这里要注意:虽然ISO标准定义了0x00F1是VIN码,但有些OEM会自己重新映射。我在项目中就遇到过,某德系车厂的VIN码居然放在0xF190下面。所以,拿到诊断规范文档后,第一件事就是核对DID映射表。

我的习惯:在项目初期,我会把所有DID整理成一个Excel表格,标注好每个DID的字节长度、访问权限、数据格式。这玩意儿后面调试时能救命。

3.3 实战案例:读取VIN码

VIN码是每辆车的身份证。17位字符,包含制造商、车型、年份等信息。读取VIN码是诊断中最常见的操作之一。

请求报文格式:

请求:22 F1 00
解释:
  0x22 - 服务ID(读取数据)
  0xF1 0x00 - DID(VIN码,高字节在前)

响应报文格式:

响应:62 F1 00 4C 46 56 33 33 30 30 30 30 30 30 30 30 30 30 30 30
解释:
  0x62 - 服务ID + 0x40(肯定响应)
  0xF1 0x00 - DID回显
  后续17字节 - VIN码ASCII码("LFV33000000000000")

你想想看,如果VIN码长度不对怎么办?ISO标准规定VIN码必须是17字节。但有些ECU会返回少于17字节,这时候就需要做填充处理。我曾经在某个项目里,ECU只返回了16字节,最后一位被截断了。排查了半天,发现是ECU固件的一个bug。

避坑指南:我曾经因为VIN码读取超时而踩过坑。有些ECU在读取VIN码时,需要先完成某些初始化流程。如果ECU还没准备好,你发0x22请求,它会返回0x78(待处理响应)。这时候需要等待,不能直接判定失败。

3.4 实战案例:读取ECU软件版本号

软件版本号是售后和OTA升级时最关心的数据。不同OEM对软件版本号的DID定义不同,但通常集中在0x01xx或0xFxxx范围内。

举个例子,某OEM定义:

  • DID 0x0110:应用软件版本号(ASCII字符串,如"V1.2.3")
  • DID 0x0111:Bootloader版本号(ASCII字符串,如"BL2.0")
  • DID 0x0112:硬件版本号(ASCII字符串,如"HW01A")

请求示例:

请求:22 01 10
响应:62 01 10 56 31 2E 32 2E 33
      ("V1.2.3"的ASCII码)

这里有个细节:版本号的编码格式。大部分OEM用ASCII字符串,但也有用BCD码的。我见过一个项目,版本号用4字节BCD码表示,比如0x01020304代表V1.2.3.4。解析的时候如果不注意,就会显示成乱码。

我的建议:在写解析代码时,先读一下DID的数据格式定义。是ASCII?是BCD?还是纯数值?搞清楚再动手。否则调试时你会怀疑人生。

3.5 0x22服务的常见否定响应

不是每次请求都能成功。ECU会通过否定响应告诉你哪里出了问题。

NRC(否定响应码) 含义 常见原因
0x12 子功能不支持 0x22没有子功能,但ECU可能误判
0x22 条件不满足 ECU处于安全模式,不允许读取
0x31 请求超出范围 DID不存在或未实现
0x33 安全访问拒绝 需要先解锁安全等级
0x78 待处理 ECU正在忙,稍后重试

说白了,0x31是最常见的。你发了一个ECU不认识的DID,它就直接给你打回来。所以,开发阶段一定要对照DID清单,别想当然。

3.6 多DID读取——一次请求搞定

0x22服务支持一次请求读取多个DID。这在实车测试中非常实用,可以减少总线负载。

请求格式:

请求:22 F1 00 01 10
解释:
  0x22 - 服务ID
  0xF1 0x00 - 第一个DID(VIN码)
  0x01 0x10 - 第二个DID(软件版本号)

响应格式:

响应:62 F1 00 4C 46 56 33 33 30 30 30 30 30 30 30 30 30 30 30 30 01 10 56 31 2E 32 2E 33
解释:
  0x62 - 肯定响应
  0xF1 0x00 - 第一个DID回显 + 17字节VIN码
  0x01 0x10 - 第二个DID回显 + 5字节版本号

注意:如果其中任何一个DID读取失败,ECU会返回否定响应,并且不会返回任何数据。所以,多DID读取时,要确保所有DID都是可读的。

经验之谈:我在做产线诊断时,经常用多DID读取一次拉取VIN码、软件版本、硬件版本、生产日期。这样一次请求就能拿到所有关键信息,效率提升很明显。

3.7 实车验证中的注意事项

纸上谈兵容易,真到了实车上,问题就来了。

  • 总线负载:不要频繁发送0x22请求。有些ECU处理能力有限,你发得太快,它会直接忽略你。我建议每次请求间隔至少10ms。
  • 响应超时:默认响应超时是50ms,但有些ECU处理复杂DID时需要更长时间。如果收到0x78,可以等待100ms后再重试。
  • 字节序:DID是16位,高字节在前。这个别搞反了,否则ECU会认为你请求了一个不存在的DID。
  • ASCII编码:VIN码和版本号通常是ASCII字符串。但有些ECU会用UTF-8或GBK编码。如果显示乱码,先检查编码格式。

嗯,最后说一句。0x22服务看似简单,但它是所有诊断功能的基础。你后面学到的0x2E(写数据)、0x2F(输入输出控制),都离不开对DID的理解。把这一章吃透了,后面的路就好走了。