2. UDS服务基础:SID与子功能、请求响应机制、寻址方式
好,咱们正式开始聊UDS协议里最核心的基础概念。说实话,这部分内容就像建房子的地基,你如果没搞懂SID和子功能的关系,后面做诊断测试一定会踩坑。我自己带团队做项目时,发现很多新人上来就背报文,结果一遇到多帧传输就懵了。今天咱们就把这些基础掰开揉碎了讲清楚。
2.1 服务标识符(SID)与子功能
先说说SID。SID就是Service Identifier,说白了就是每个诊断服务的身份证号。比如你想读取ECU的故障码,就得用0x19这个SID;想读写数据,就用0x22或0x2E。每个SID占一个字节,范围从0x00到0xFF。
但这里有个关键点——不是所有SID都带子功能。我刚开始做诊断时,就犯过这个错。比如0x22(ReadDataByIdentifier)就没有子功能,你直接发请求就行。但像0x19(ReadDTCInformation)这种,就必须带子功能,告诉ECU你想读哪类故障码。
重要概念:子功能(SubFunction)是SID的补充参数,用于细化服务请求。它占请求报文的第二个字节,取值范围0x00-0x7F(支持肯定响应抑制位)。
举个例子,0x19服务常见的子功能:
| 子功能值 | 含义 | 我常用的场景 |
|---|---|---|
| 0x01 | 按状态读取DTC数量 | 快速检查ECU有没有故障 |
| 0x02 | 按状态读取DTC列表 | 获取具体故障码 |
| 0x04 | 读取快照数据 | 分析故障发生时的环境条件 |
| 0x06 | 读取扩展数据 | 获取故障发生次数等统计信息 |
你可能会问:子功能0x00和0x7F有什么区别?嗯,这里要注意——0x00通常表示“请求所有子功能”,而0x7F是保留值。我个人习惯在写测试用例时,优先用0x01-0x7E之间的值,避免踩到保留区的坑。
2.2 请求与响应机制
UDS的通信模式很简单:你发一个请求,ECU回一个响应。但这里面的门道可不少。
2.2.1 单帧传输
单帧就是一条CAN报文搞定的事。比如读取VIN码,如果VIN只有17个字节,一条报文就够了。单帧的格式很简单:
请求:02 22 F1 90
解释:
- 02:数据长度(2个字节)
- 22:SID(ReadDataByIdentifier)
- F1 90:数据标识符(VIN码的ID)
响应呢?ECU会回:
响应:12 62 F1 90 4C 56 4E ...
解释:
- 12:响应长度(18个字节)
- 62:肯定响应(SID+0x40)
- F1 90:数据标识符
- 4C 56 4E...:VIN码内容
这里有个小技巧:肯定响应的SID = 请求SID + 0x40。比如0x22的肯定响应是0x62,0x19的肯定响应是0x59。这个规律记牢了,看报文一眼就能判断是请求还是响应。
我的经验:在测试时,如果收到0x7F开头的响应,说明ECU拒绝了你的请求。后面跟着的NRC(Negative Response Code)会告诉你原因。比如0x7F 0x22 0x12,意思就是“0x22服务不支持”。
2.2.2 多帧传输
当数据量超过单帧容量(通常是8字节CAN报文)时,就得用多帧了。比如读取ECU的软件版本号,可能长达几百个字节。这时候就需要分段发送。
多帧传输遵循ISO 15765-2协议,分为三种帧类型:
- 首帧(FF):包含总数据长度,告诉ECU“我要发这么多数据”
- 连续帧(CF):实际的数据内容,每帧带一个序列号
- 流控帧(FC):接收方告诉发送方“你可以继续发”或“等一下”
举个例子,你要发一个100字节的请求:
首帧:10 64 [数据头] [数据1-5]
解释:
- 10:首帧标识
- 64:总长度(100字节)
- 后面跟着前6个字节的数据
连续帧1:21 [数据6-13]
连续帧2:22 [数据14-21]
...
连续帧N:2N [最后的数据]
你可能会问:流控帧什么时候用?嗯,这取决于接收方的处理能力。我曾经遇到过一个ECU,它的接收缓冲区只有64字节。如果你一次性发100字节,它就会发流控帧说“等一下,我还没处理完”。这时候发送方就得暂停,等收到新的流控帧再继续。
避坑指南:我曾经在项目里遇到过一个问题——连续帧的序列号从1开始,到15后归零。如果发送方和接收方的序列号不同步,就会导致数据错乱。所以测试时一定要检查序列号的连续性。
2.3 物理寻址与功能寻址
这是UDS里最容易混淆的概念之一。说白了,就是你的诊断请求发给谁的问题。
2.3.1 物理寻址
物理寻址就是点对点通信。你发一条报文,只有指定的ECU会响应。比如你想读取发动机ECU的故障码,就用物理寻址,只发给发动机ECU。
物理寻址的CAN ID通常是:
- 请求ID:0x7E0(诊断仪到ECU)
- 响应ID:0x7E8(ECU到诊断仪)
当然,不同车企的ID分配可能不同。我见过用0x7DF做物理寻址的,也见过用0x18DAxxF1的。具体看你的网络设计。
2.3.2 功能寻址
功能寻址是一对多通信。你发一条报文,总线上所有支持该功能的ECU都会响应。比如你想让所有ECU同时进入扩展会话模式,就用功能寻址。
功能寻址的CAN ID通常是:
- 请求ID:0x7DF(诊断仪到所有ECU)
- 响应ID:每个ECU用自己的物理响应ID回复
这里有个关键点:功能寻址的响应是并发的。如果你同时给10个ECU发请求,它们会同时回复,导致总线冲突。所以功能寻址通常用于不需要响应的服务,比如0x11(ECUReset)或0x28(CommunicationControl)。
重要区别:
- 物理寻址:一对一,有响应,用于读写数据
- 功能寻址:一对多,可无响应,用于广播控制
我建议你在测试时,优先用物理寻址。因为功能寻址的并发响应很容易导致总线负载过高,而且你很难判断哪个ECU没响应。除非你明确知道所有ECU都能处理并发响应,否则别轻易用功能寻址做读写操作。
2.4 实战中的注意事项
最后分享几个我在项目中踩过的坑:
- SID和子功能的组合要查规范。不是所有子功能都支持所有SID。比如0x19服务支持0x01-0x0E的子功能,但0x22服务就不支持任何子功能。查ISO 14229-1的表格最靠谱。
- 多帧传输的超时时间要设对。ISO 15765-2规定了N_As、N_Bs、N_Cs等超时参数。我一般设N_As为100ms,N_Bs为1000ms,N_Cs为50ms。太短容易误判,太长影响效率。
- 功能寻址的响应抑制位。在子功能的最高位(bit 7)置1,可以告诉ECU“你不用回复我”。比如0x19的请求是0x19 0x01,如果改成0x19 0x81,ECU就不会回复。这个技巧在广播复位时特别有用。
好了,这一章的内容就到这里。下一章咱们会深入聊UDS的会话管理,包括默认会话、扩展会话和编程会话的区别。到时候我会分享一个真实案例——有一次我在产线上因为会话切换顺序搞错,导致ECU刷写失败,差点耽误了交期。嗯,到时候再细说。