3. UDS服务基础:服务标识符(SID)概念、服务分类与服务格式编码
好,咱们今天聊聊UDS协议里最基础、也最核心的东西——服务标识符,也就是SID。
说实话,我刚接触UDS那会儿,看着那一堆十六进制数,心里也犯过嘀咕:这玩意儿到底怎么组织的?后来做网关项目做多了,慢慢就摸清了门道。今天我把这些经验掰开揉碎了讲给你听。
3.1 服务标识符(SID)是什么?
SID,全称Service Identifier。说白了,它就是UDS协议里每条请求的“身份证”。
你想想看,诊断仪发一条报文给ECU,ECU怎么知道你想干嘛?是想读数据?还是想写数据?还是想执行某个动作?
答案就在报文的第一个字节——SID。
核心概念:每个UDS服务都有一个唯一的SID,范围是0x00到0x3E。诊断仪发请求时,SID放在请求报文的首字节。ECU回复时,会在SID基础上加0x40,表示“响应成功”。
举个例子:
- 你发
0x22,表示“我要读数据” - ECU回
0x62,表示“好的,数据给你” - 如果ECU回的是
0x7F,那说明出错了,后面会跟具体错误码
嗯,这里要注意:0x7F 是负响应码,不是某个服务的SID。我见过不少新手把 0x7F 当成服务来用,结果调试半天找不到问题。
3.2 服务分类:六大功能组
UDS协议把26个服务分成了6大类。我个人习惯把这六类记成“诊断六脉神剑”,哈哈,开个玩笑。但确实,每类服务解决一类问题。
| 分类 | SID范围 | 典型服务 | 一句话说明 |
|---|---|---|---|
| 诊断与通信管理 | 0x10 ~ 0x3E | 0x10(诊断会话控制)、0x11(ECU复位)、0x28(通信控制) | 管连接、管模式、管通信 |
| 数据传输 | 0x22 ~ 0x2E | 0x22(读数据)、0x2E(写数据) | 读写DID,最常用 |
| 存储数据传输 | 0x14 ~ 0x19 | 0x14(清除诊断信息)、0x19(读取DTC信息) | 管故障码的 |
| 输入输出控制 | 0x2F | 0x2F(输入输出控制) | 强制控制某个引脚或执行器 |
| 远程激活 | 0x31 | 0x31(例程控制) | 执行一段预定义程序 |
| 上传下载 | 0x34 ~ 0x37 | 0x34(请求下载)、0x36(传输数据)、0x37(请求传输退出) | 刷写固件用的 |
我在项目中遇到过最尴尬的事:有个同事把 0x19(读DTC)和 0x22(读数据)搞混了。他以为读故障码也能用 0x22 加DID的方式,结果ECU一直回负响应。后来我帮他一看,服务都选错了,能不报错吗?
3.3 服务格式与编码
每个UDS服务都有固定的格式。我总结了一个通用模板:
请求格式: [SID] [Sub-function/Data] [参数...]
正响应格式: [SID + 0x40] [Sub-function/Data] [参数...]
负响应格式: [0x7F] [SID] [NRC]
咱们拿最常用的 0x22(读数据)来举例:
请求报文:
22 F1 90
| | |
| | +-- DID高字节 (0x90)
| +------ DID低字节 (0xF1)
+--------- SID = 0x22
正响应报文:
62 F1 90 01 02 03 04
| | | | | | |
| | | +--+--+--+-- 数据内容 (4字节)
| | +-- DID高字节
| +------ DID低字节
+--------- SID + 0x40 = 0x62
负响应报文:
7F 22 31
| | |
| | +-- NRC = 0x31 (请求超出范围)
| +------ 请求的SID
+--------- 负响应标识
我的小技巧:调试时,先看响应报文的第一个字节。如果是 0x7F,直接看第三个字节的NRC,能省一半排查时间。我曾经靠这个习惯,在客户现场10分钟定位了一个刷写失败的问题。
3.4 子功能参数:Sub-function
有些服务支持子功能,比如 0x10(诊断会话控制)。子功能放在SID后面,占一个字节。
这里有个坑:子功能的最高位(bit 7)有特殊含义。
- bit 7 = 0:表示“请求执行该子功能”
- bit 7 = 1:表示“请求抑制正响应”
举个例子:
10 01 → 请求切换到默认会话,并期望ECU回复
10 81 → 请求切换到默认会话,但ECU不要回复
为什么要抑制正响应?我解释一下:在刷写过程中,如果每发一条报文ECU都回一条,总线负载会很高。抑制正响应能减少总线流量,提高刷写速度。嗯,这个技巧在批量刷写时特别有用。
注意:不是所有服务都支持子功能。比如 0x22(读数据)就没有子功能,它的SID后面直接跟DID。如果你给 0x22 加了个子功能字节,ECU会回 0x7F 0x22 0x13(报文长度或格式错误)。
3.5 实际项目中的编码习惯
我建议你在设计诊断栈时,把SID定义成枚举类型。这样代码可读性高,也不容易写错。
typedef enum {
UDS_SID_DIAG_SESSION_CTRL = 0x10,
UDS_SID_ECU_RESET = 0x11,
UDS_SID_READ_DATA = 0x22,
UDS_SID_WRITE_DATA = 0x2E,
UDS_SID_ROUTINE_CTRL = 0x31,
UDS_SID_REQUEST_DOWNLOAD = 0x34,
UDS_SID_TRANSFER_DATA = 0x36,
UDS_SID_REQUEST_TRANSFER_EXIT = 0x37
} UdsSid_t;
另外,响应超时时间也要注意。ISO 14229-1里建议:
- 默认会话:响应超时 50ms
- 扩展会话:响应超时 50ms
- 编程会话:响应超时 500ms ~ 5000ms(取决于具体操作)
我曾经在编程会话里把超时设成了50ms,结果刷写时ECU擦除Flash花了200ms,网关直接判定超时,刷写失败。后来改成2000ms,问题解决。所以,超时时间要根据实际场景灵活调整。
3.6 小结
今天咱们把UDS服务的骨架搭起来了:
- SID是服务的身份证,范围0x00~0x3E
- 六大服务分类,各管一摊
- 请求/响应有固定格式,负响应用0x7F开头
- 子功能的bit 7控制是否抑制正响应
下一章,咱们会深入第一个服务——0x10 诊断会话控制。到时候我会讲讲不同会话模式之间的切换逻辑,以及我在网关项目中踩过的那些坑。
好,今天就到这儿。有什么问题,咱们留言区见。