第2章:UDS协议基础:ISO 14229-1标准解读、UDS服务分类与报文格式

好,咱们正式开始啃UDS协议。说实话,ISO 14229-1这份标准,我第一次看的时候差点睡着。几百页的PDF,全是术语和表格。但后来我发现,搞懂它其实就三件事:标准在说什么、服务怎么分类、报文长什么样。今天咱们就把这三件事掰开揉碎了讲清楚。

2.1 ISO 14229-1标准到底在说什么?

ISO 14229-1,全称是“道路车辆——统一诊断服务——第1部分:应用层”。你想想看,它定义的是应用层,也就是最靠近咱们软件工程师的那一层。它不管数据怎么通过CAN总线传过去,也不管物理层用的是什么接口,它只管一件事:诊断仪和ECU之间怎么“对话”

我个人习惯把这份标准理解成一本“字典”。它规定了:

  • 你能说哪些“单词”(服务ID,简称SID)
  • 每个单词后面必须跟什么“语法”(子功能、数据参数)
  • 对方回答时应该用什么“句式”(肯定响应、否定响应)

举个例子,你告诉ECU“读取数据”,标准里就规定了这个请求的格式必须是 0x22 + DID。ECU收到后,必须回复 0x62 + DID + 数据。这就是规矩,谁都不能乱来。

核心要点:ISO 14229-1只定义应用层,不涉及传输层和网络层。它独立于底层总线(CAN、LIN、FlexRay、以太网都支持)。

我在项目中遇到过一个问题:有人把UDS和ISO 15765(CAN上的诊断)混为一谈。其实UDS是应用层协议,ISO 15765是网络层和传输层协议。你写UDS代码时,底层用的是CAN还是DoIP,UDS本身不需要改。嗯,这里要注意区分。

2.2 UDS服务分类:六大类,一张表说清楚

UDS总共定义了26个服务(SID从0x10到0x3E)。标准把它们分成了六大类。我刚开始记不住,后来自己画了个分类表,一下就清晰了。你也试试看:

分类 服务名称 SID 一句话说明
诊断与通信管理 诊断会话控制 0x10 切换ECU的工作模式(默认、扩展、编程)
ECU复位 0x11 让ECU软复位或硬复位
安全访问 0x27 解锁ECU的受保护功能
数据传输 读取数据通过标识符 0x22 读DID(最常用的服务之一)
写入数据通过标识符 0x2E 写DID(比如写VIN码)
读取数据通过周期标识符 0x2A 周期性读取数据
存储数据传输 清除诊断信息 0x14 清DTC
输入输出控制 输入输出控制通过标识符 0x2F 强制控制某个引脚或执行器
远程激活 例程控制 0x31 启动/停止ECU内部的一段程序
上传下载 请求下载 0x34 准备把数据写到ECU
请求上传 0x35 准备从ECU读数据

这张表我建议你存下来。每次写代码前看一眼,就知道该用哪个SID了。我个人习惯把0x22和0x2E称为“读写双雄”,因为90%的日常诊断都在用它们。

2.3 UDS报文格式:请求与响应的“骨架”

UDS报文格式其实很简单。它就是一个字节一个字节拼起来的。咱们先看请求报文:

| 字节0 | 字节1 | 字节2 ... 字节N |
| SID   | Sub-function (可选) | 数据参数 |

举个例子,发送“读取VIN码”的请求:

0x22 0xF1 0x90
| SID=0x22 | DID高字节=0xF1 | DID低字节=0x90 |

ECU收到后,会回复肯定响应或否定响应。肯定响应的格式是:

| 字节0 | 字节1 ... 字节N |
| SID+0x40 | 数据参数 |

还是刚才的例子,ECU回复VIN码:

0x62 0xF1 0x90 0x4C 0x53 0x56 ...
| SID+0x40=0x62 | DID=0xF190 | VIN数据 |

如果ECU不认这个请求,它会回复否定响应:

| 字节0 | 字节1 | 字节2 |
| 0x7F  | 请求的SID | 否定响应码(NRC) |

比如你发了一个不支持的SID,ECU回复:

0x7F 0x22 0x12
| 否定响应标识 | 请求的SID=0x22 | NRC=0x12(子功能不支持) |

避坑指南:我曾经在调试时发现ECU一直回复0x7F 0x22 0x11,查了半天才发现是DID写错了。NRC 0x11表示“服务不支持”,但其实是DID不对。所以看到NRC别急着怀疑协议栈,先检查你的参数对不对。

2.4 报文格式的细节:子功能与数据长度

有些服务(比如0x10诊断会话控制)需要带子功能。子功能占1个字节,它的最高位(bit 7)有特殊含义:

  • bit 7 = 0:请求ECU执行该子功能,并回复响应
  • bit 7 = 1:请求ECU执行该子功能,但不需要回复(抑制肯定响应指示位)

举个例子,切换到扩展会话:

0x10 0x03
| SID=0x10 | Sub-function=0x03(扩展会话) |

如果我想让ECU切换但不回复,就发:

0x10 0x83  // 0x80 | 0x03

这个抑制位在实际项目中很有用。我记得有一次做批量测试,需要连续切换会话,如果每个都等回复,时间全浪费在等待上了。用抑制位后,速度提升了30%。

2.5 多帧传输:当数据太长时怎么办?

UDS报文在CAN总线上有长度限制(经典CAN最多8字节)。如果你要读的数据超过8字节,就需要分多帧发送。这里涉及ISO 15765的流控制机制,但UDS本身不关心这个。你只需要知道:

  • UDS应用层只管拼数据
  • 底层协议栈会自动分包和重组

举个例子,读取一个256字节的DID:

请求:0x22 0xF1 0x90  // 只有3字节,单帧搞定
响应:0x62 0xF1 0x90 + 256字节数据  // 超过8字节,需要多帧

ECU的协议栈会把256字节分成多个CAN帧发送,诊断仪收到后再拼回去。你写应用层代码时,完全不用管这些细节。但如果你自己写协议栈,那就得好好研究ISO 15765了。

注意:多帧传输中,如果某一帧丢了,整个响应都会失败。我曾经在实车上遇到过CAN总线干扰导致丢帧,ECU一直重发,诊断仪一直超时。最后发现是终端电阻没接好。嗯,硬件问题有时候比软件更坑。

2.6 小结:UDS协议的核心三要素

总结一下,UDS协议基础就三件事:

  1. 标准:ISO 14229-1定义了应用层的“语法”和“字典”
  2. 服务:六大类26个服务,记住0x10、0x22、0x2E、0x2F、0x31、0x34这六个SID,基本够用
  3. 报文:请求是SID+参数,肯定响应是SID+0x40+数据,否定响应是0x7F+SID+NRC

下一章咱们会深入讲诊断会话控制和安全访问,这两个是UDS的“门禁系统”。不搞定它们,后面的服务都用不了。准备好了吗?