第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协议基础就三件事:
- 标准:ISO 14229-1定义了应用层的“语法”和“字典”
- 服务:六大类26个服务,记住0x10、0x22、0x2E、0x2F、0x31、0x34这六个SID,基本够用
- 报文:请求是SID+参数,肯定响应是SID+0x40+数据,否定响应是0x7F+SID+NRC
下一章咱们会深入讲诊断会话控制和安全访问,这两个是UDS的“门禁系统”。不搞定它们,后面的服务都用不了。准备好了吗?