第2章:ISO 14229-1标准解读:协议文档结构、服务ID定义、子功能参数、数据格式与编码规则

好,咱们直接进入正题。ISO 14229-1,也就是UDS诊断协议的核心标准。说实话,我刚入行那会儿,看到这份标准文档,第一反应是——这也太厚了吧?几百页的PDF,全是英文,密密麻麻的表格。但你别怕,今天我就带你把它拆开揉碎,讲清楚。

2.1 协议文档的整体结构

ISO 14229-1这份标准,说白了就是一本「诊断服务字典」。它规定了ECU和诊断仪之间该怎么说话,说什么话,以及话里的每个字是什么意思。

整份文档的结构,我习惯把它分成三大块:

  • 第一部分:基础框架(第1章到第4章)—— 讲范围、引用、术语、缩略语。这部分你大概扫一眼就行,知道UDS是干啥的,以及它和ISO 11898(CAN总线)是什么关系。
  • 第二部分:核心机制(第5章到第9章)—— 讲诊断服务的通用规则,比如定时参数、数据格式、安全访问、会话控制。这部分是重点,我建议你反复看。
  • 第三部分:服务定义(第10章到第18章)—— 这才是真正的干货。每个服务ID(SID)、每个子功能参数、每个数据格式,全在这里。
我的小技巧: 拿到标准文档,别从头读到尾。先看第10章以后的服务定义,遇到不懂的再回头翻前面的基础框架。这样效率高得多。

2.2 服务ID(SID)的定义与分类

服务ID,就是诊断请求报文里的第一个字节。它告诉ECU:「嘿,我要干啥?」

举个例子,0x10 表示「诊断会话控制」,0x22 表示「读取数据通过标识符」,0x2E 表示「写入数据通过标识符」。每个SID都是一个8位的数值,范围从 0x000xFF

标准里把SID分成了几类:

SID范围 类别 典型服务
0x10 - 0x3E 诊断与通信管理 0x10(会话控制)、0x11(ECU复位)、0x28(通信控制)
0x22 - 0x2E 数据读取与写入 0x22(读取数据)、0x2E(写入数据)
0x31 - 0x3D 输入输出控制 0x31(例程控制)、0x2F(输入输出控制)
0x34 - 0x37 上传下载 0x34(请求下载)、0x35(请求上传)

嗯,这里要注意:SID的分配是有规律的。比如 0x100x3E 是「诊断与通信管理」类,0x220x2E 是「数据读取与写入」类。你记住这个规律,以后看到SID就能猜出它大概属于哪一类。

避坑指南: 我曾经在项目里遇到过一个坑——ECU对某个SID的响应,和标准文档里写的格式不一样。后来查了半天,发现是供应商自己扩展了SID的定义。所以,拿到ECU的A2L文件或ODX文件后,一定要先确认它支持哪些SID,别完全依赖标准文档。

2.3 子功能参数(SubFunction)

有些服务,光靠SID还不够。比如 0x10(诊断会话控制),你得告诉ECU:「我要切换到哪个会话?」是默认会话(0x01),还是编程会话(0x02),还是扩展会话(0x03)?

这个额外的信息,就是子功能参数。它通常放在SID后面的第二个字节。

子功能参数的结构是这样的:

字节0: SID (0x10)
字节1: 子功能参数 (0x01 表示默认会话, 0x02 表示编程会话, 0x03 表示扩展会话)
字节2...: 其他数据(如果有的话)

子功能参数的最高位(bit 7)有一个特殊含义——它表示「是否需要正响应」。如果bit 7 = 1,ECU不会发送正响应;如果bit 7 = 0,ECU会发送正响应。

举个例子:

  • 0x10 0x01:切换到默认会话,并且ECU要给我回一个正响应。
  • 0x10 0x81:切换到默认会话,但ECU别回正响应(这叫「抑制正响应」)。
我的经验: 抑制正响应这个功能,在批量刷写的时候特别有用。你想想看,刷写过程中要发几百个请求,如果每个请求ECU都回一个正响应,总线负载会很高。所以刷写工具通常会设置抑制正响应,只关心负响应(错误)的情况。

2.4 数据格式与编码规则

UDS里的数据格式,说白了就是「怎么把数据塞进报文里」。标准里定义了几种基本的数据类型:

  • uint8 / uint16 / uint32:无符号整数,分别占1、2、4个字节。比如读取ECU的软件版本号,通常用uint8数组表示。
  • int8 / int16 / int32:有符号整数,用补码表示。比如读取温度值,可能是int16。
  • ASCII字符串:比如读取VIN码(车辆识别号),就是17个字节的ASCII字符串。
  • 原始字节数组:比如读取诊断故障码(DTC),就是一组原始字节。

编码规则方面,UDS用的是「大端序」(Big-Endian)。也就是说,高位字节在前,低位字节在后。

举个例子,如果我要发送一个uint16的值 0x1234

大端序:0x12 0x34
小端序:0x34 0x12

UDS标准明确要求用大端序。这一点你一定要注意,因为有些MCU(比如ARM Cortex-M系列)默认是小端序,你在代码里需要做字节序转换。

警告: 字节序搞错,是UDS开发里最常见的bug之一。我曾经见过一个团队,调试了整整两天,才发现是字节序反了。所以,写代码的时候,一定要显式地做字节序转换,别依赖编译器的默认行为。

2.5 实际报文示例

光说不练假把式。咱们来看一个实际的诊断请求和响应报文。

请求: 读取ECU的软件版本号(假设DID = 0xF190)

请求报文:0x22 0xF1 0x90
解释:
  0x22 = SID(读取数据通过标识符)
  0xF1 0x90 = DID(数据标识符,占2个字节)

响应: ECU返回软件版本号(假设版本号是 "V1.2.3")

响应报文:0x62 0xF1 0x90 0x56 0x31 0x2E 0x32 0x2E 0x33
解释:
  0x62 = 正响应SID(请求SID + 0x40)
  0xF1 0x90 = DID(和请求里的DID一致)
  0x56 0x31 0x2E 0x32 0x2E 0x33 = 数据("V1.2.3"的ASCII编码)

你看,正响应的SID是请求SID加上 0x40。这是UDS的一个固定规则——正响应SID = 请求SID + 0x40。比如 0x22 + 0x40 = 0x62

核心要点: 正响应SID = 请求SID + 0x40。这个规则适用于所有UDS服务。你写代码的时候,可以直接用这个公式来判断收到的报文是正响应还是负响应。

2.6 负响应码(NRC)

如果ECU处理不了你的请求,它会返回一个负响应。负响应的格式是:

0x7F + 请求SID + NRC

比如,你发了一个 0x22 0xF1 0x90,但ECU不支持这个DID,它可能会回:

0x7F 0x22 0x12

这里的 0x12 就是NRC,表示「子功能不支持」或「请求超出范围」。常见的NRC有:

NRC 含义 常见场景
0x10 一般拒绝 ECU正忙,无法处理请求
0x11 服务不支持 ECU不支持这个SID
0x12 子功能不支持 ECU不支持这个子功能参数
0x13 请求长度错误或格式错误 报文长度不对,或者数据格式不对
0x22 条件不满足 比如安全访问没通过,就尝试写入数据
0x31 请求超出范围 DID或RID不在支持的范围内
0x33 安全访问被拒绝 安全访问的种子和密钥不匹配
我的建议: 开发初期,先把NRC的解析逻辑写好。调试的时候,90%的问题都能通过NRC定位到。我曾经遇到一个bug,ECU一直回0x22(条件不满足),查了半天才发现是会话没切换到扩展会话。所以,NRC就是你的调试指南针。

2.7 总结

好,这一章的内容就到这里。咱们回顾一下:

  • ISO 14229-1标准分三大部分:基础框架、核心机制、服务定义。
  • SID是诊断请求的第一个字节,范围从0x00到0xFF,分成了几大类。
  • 子功能参数放在SID后面,最高位控制是否抑制正响应。
  • 数据格式用大端序,uint8/uint16/uint32是基础。
  • 正响应SID = 请求SID + 0x40,负响应以0x7F开头。

下一章,咱们会深入讲「诊断会话控制」这个服务。到时候我会带你手写一个会话切换的代码示例。嗯,敬请期待。