3. UDS服务与子服务:服务ID(SID)定义、子功能参数、肯定/否定响应机制

好,咱们今天聊聊UDS里最核心的东西——服务与子服务。说白了,这就是诊断仪和ECU之间对话的“语法规则”。你发一个请求,ECU得知道你想干嘛,然后它得告诉你它干没干成。这套规则,就是靠SID和子功能参数来定义的。

3.1 服务ID(SID)—— 诊断指令的“身份证”

每个UDS服务都有一个唯一的ID,叫SID(Service Identifier)。它是一个单字节的数值,范围从0x00到0x3E。我习惯把它理解为“指令码”。比如你想读ECU的故障码,那就发0x19;你想写数据,就发0x2E。

这里有个关键点:请求的SID是奇数,响应的SID是偶数。你想想看,ECU收到请求后,会把请求SID的最高位置1,作为肯定响应的SID。比如你发0x22(读数据),ECU回复0x62。这个设计很巧妙,一眼就能看出是请求还是响应。

常用SID速查表(我项目中用得最多的几个):

SID(请求) SID(响应) 服务名称 用途
0x10 0x50 诊断会话控制 切换ECU的工作模式
0x22 0x62 读取数据 读DID(数据标识符)
0x2E 0x6E 写入数据 写DID
0x19 0x59 读取DTC信息 读故障码
0x14 0x54 清除DTC信息 清故障码
0x31 0x71 例程控制 启动/停止ECU内部程序

3.2 子功能参数 —— 给指令加点“调料”

光有SID还不够。比如0x10(诊断会话控制),你得告诉ECU切换到哪个会话吧?是默认会话(0x01),还是扩展会话(0x03),还是编程会话(0x02)?这时候就需要子功能参数了。

子功能参数紧跟在SID之后,也是一个字节。它的最高位(bit 7)有特殊含义:0表示不需要响应,1表示需要响应。嗯,这里要注意:如果你把子功能的bit 7置0,ECU就算执行成功了也不会回复你。这在某些批量操作场景下很有用,能减少总线负载。

举个例子,发送0x10 0x03,意思是“切换到扩展会话,并且给我回复”。而0x10 0x83,意思是“切换到扩展会话,但别回复我”。

我的一个小经验:在开发阶段,我建议永远把子功能的bit 7置1。我曾经在调试一个ECU时,为了省流量把响应关了,结果ECU死机了都不知道。后来排查了半天才发现是请求发错了,但ECU没回复,我完全蒙在鼓里。从那以后,我只要不是量产阶段,一律要求响应。

3.3 肯定响应机制 —— “收到,已执行”

当ECU成功执行了你的请求,它会回复一个肯定响应。格式很简单:响应SID + 子功能参数(原样返回) + 数据

比如你发0x22 0xF1 0x90(读取VIN码),ECU回复0x62 0xF1 0x90 0x4C 0x47...(后面跟着VIN码数据)。

这里有个细节:对于不需要子功能的服务(比如0x22),响应里就没有子功能参数,直接是响应SID + 数据。而对于有子功能的服务,响应里必须原样返回子功能参数。这是协议规定的,别搞错了。

3.4 否定响应机制 —— “不行,因为...”

ECU不是万能的。有时候你的请求它执行不了,比如会话不对、数据格式错误、或者条件不满足。这时候ECU会回复一个否定响应。

否定响应的格式是:0x7F + 请求SID + 否定响应码(NRC)

比如你发0x22 0xF1 0x90,但当前会话不允许读这个DID,ECU就会回复0x7F 0x22 0x7F。这里的0x7F就是NRC,意思是“服务在当前会话中不支持”。

常见的NRC(否定响应码)速查:

NRC 含义 常见场景
0x10 一般拒绝 ECU正忙,无法处理
0x11 服务不支持 ECU根本不认识这个SID
0x12 子功能不支持 SID认识,但子功能不对
0x13 报文长度或格式错误 数据长度不对,或者格式有误
0x22 条件不满足 比如写数据时,ECU不在编程会话
0x31 请求超出范围 比如读一个不存在的DID
0x7F 当前会话不支持 服务在默认会话下不可用
0x78 请求正确,正在处理 ECU需要时间,让你等等

避坑指南:我曾经遇到过一个ECU,它总是回复0x78(请求正确,正在处理),然后就没下文了。后来发现是ECU的软件bug,它卡在某个循环里出不来了。所以,如果你收到0x78,一定要设置超时机制。一般建议等待500ms到2s,如果还没收到最终响应,就认为ECU挂了。

3.5 实际报文示例 —— 咱们走一遍流程

假设我想读ECU的VIN码(DID = 0xF190)。

步骤1:发送请求

CAN ID: 0x7DF(功能寻址)或 0x7E0(物理寻址)
数据:0x22 0xF1 0x90
解释:0x22 = 读取数据服务,0xF190 = VIN码的DID

步骤2:ECU肯定响应

CAN ID: 0x7E8
数据:0x62 0xF1 0x90 0x4C 0x47 0x00 0x00 0x00 0x00
解释:0x62 = 肯定响应,0xF190 = DID原样返回,后面是VIN码数据(假设VIN是"LG000000")

步骤3:如果ECU否定响应

CAN ID: 0x7E8
数据:0x7F 0x22 0x7F
解释:0x7F = 否定响应标识,0x22 = 请求的SID,0x7F = NRC(当前会话不支持)

你看,整个流程非常清晰。你发什么,ECU回什么,一目了然。

3.6 我的调试心得

在实际项目中,我建议你准备一个CAN分析工具(比如PCAN、Kvaser、或者Vector的CANoe)。抓一下总线上的报文,看看ECU到底回了什么。很多时候,问题就出在NRC上。

比如你发0x2E写数据,ECU回0x7F 0x2E 0x22。你查表,0x22是“条件不满足”。那你就得想想:是不是当前会话不对?是不是ECU处于某种保护状态?是不是写之前需要先解锁?

嗯,说到解锁,这就是另一个话题了——安全访问(0x27服务)。咱们后面会详细讲。

最后,记住一句话:UDS的调试,本质上就是SID、子功能、NRC这三者的排列组合。你把这套规则吃透了,任何ECU的诊断协议在你面前都是透明的。