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会直接给你扔个否定响应回来。
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映射表。
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。
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。解析的时候如果不注意,就会显示成乱码。
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都是可读的。
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的理解。把这一章吃透了,后面的路就好走了。