第四章 数据读取服务:0x22、0x23、0x24

各位同学,今天我们来聊聊UDS诊断里最常用的三个读取服务。说实话,这三个服务我几乎每天都在用。0x22读数据标识符、0x23读内存地址、0x24读缩放数据标识符。它们看起来很像,但用起来差别不小。

我在项目里见过不少工程师把这三个服务搞混。嗯,今天我们就把它彻底讲清楚。

4.1 0x22服务:读取数据标识符

0x22服务,说白了就是通过一个ID去读ECU内部的数据。这个ID叫DID(Data Identifier),通常是两个字节。比如0xF190代表VIN码,0xF191代表硬件版本号。

我个人习惯把DID分成三类:

  • 标准DID:ISO 14229-1里定义好的,比如0xF186(软件版本)
  • OEM自定义DID:各家车厂自己定义的,比如0xE001代表某个传感器值
  • 供应商保留DID:留给ECU供应商内部调试用的

来看一个典型的请求/响应格式:

// 请求:22 + DID(2字节)
// 例如读取VIN码(DID = 0xF190)
请求:22 F1 90
响应:62 F1 90 + 数据长度 + VIN码数据

// 实际报文示例
请求:22 F1 90
响应:62 F1 90 11 4C 56 4D 32 44 48 47 34 35 36 37 38 39 30 31 32 33
// 62 = 肯定响应SID
// F1 90 = DID
// 11 = 数据长度(17字节)
// 后面是VIN码:LVM2DHG4567890123

这里有个坑。我记得有一次,客户反馈说读VIN码总是超时。查了半天,发现是DID对应的数据长度超过了CAN报文最大8字节的限制。解决方案是用多帧传输,或者把VIN码分段存储。

注意:0x22服务返回的数据长度是可变的。设计时一定要考虑多帧传输的情况。我建议在协议栈里统一用多帧处理机制,别偷懒。

4.2 0x23服务:读取内存地址

0x23服务跟0x22最大的区别是:它直接操作物理地址。你想想看,这多危险。所以这个服务通常只在开发阶段或者产线模式下才开放。

它的请求格式是这样的:

// 请求:23 + 地址长度 + 地址 + 数据长度
// 地址长度:1字节,表示后面地址占几个字节
// 地址:实际物理地址
// 数据长度:要读多少字节

// 例如读取地址0x80001000开始的4个字节
请求:23 04 80 00 10 00 04
响应:63 04 80 00 10 00 04 + 数据内容

// 地址长度=04,表示地址占4字节
// 地址=0x80001000
// 数据长度=04,读4字节

我在项目中遇到过一个问题:某ECU的Flash地址是32位的,但Bootloader里只支持16位地址。结果0x23服务在Bootloader模式下就崩了。后来我们加了个地址转换层,统一用32位地址。

安全建议:
  • 0x23服务必须加安全访问(0x27服务)保护
  • 地址范围要做合法性检查,防止读到系统保护区
  • 建议在量产固件里把这个服务关掉

4.3 0x24服务:读取缩放数据标识符

0x24服务,说实话用得不多。它跟0x22很像,但多了一个“缩放”的概念。什么意思呢?就是同一个DID,你可以用不同的单位或格式去读。

举个例子:

DID 原始数据 缩放因子 缩放后数据
0xF190 0x0A 0x01(无缩放) 10
0xF190 0x0A 0x02(乘以10) 100
0xF190 0x0A 0x03(除以2) 5

请求格式:

// 请求:24 + DID(2字节) + 缩放因子(1字节)
// 例如读取DID=0xF190,缩放因子=0x02
请求:24 F1 90 02
响应:64 F1 90 02 + 缩放后的数据

// 注意:响应里的缩放因子必须跟请求一致

为什么要有这个服务?我个人理解是为了兼容不同OEM的需求。有的OEM想要原始值,有的想要工程值。用0x24服务,一个DID就能搞定。

经验之谈:0x24服务在量产车诊断里很少用。我建议你优先实现0x22服务,0x23服务看需求,0x24服务可以放到后面再做。别一开始就把所有服务都堆上去,容易出bug。

4.4 三个服务的对比

服务 访问方式 安全性 使用频率 典型场景
0x22 通过DID 低(可公开) 极高 读VIN、版本号、故障码
0x23 通过物理地址 高(需安全访问) 开发调试、产线测试
0x24 通过DID+缩放因子 低(可公开) 极低 特殊OEM需求

最后说一句,这三个服务的实现其实不难。难的是怎么把它们跟你的应用层数据解耦。我建议你设计一个统一的“数据访问层”,不管上层是0x22、0x23还是0x24,底层都通过同一个接口去读数据。这样后期维护起来会轻松很多。

好了,今天就到这里。下一章我们讲写入服务,那才是真正考验协议栈设计能力的地方。