3. UDS协议栈架构:分层、服务与子服务、请求与响应机制

好,咱们今天聊聊UDS协议栈的骨架。说白了,就是搞清楚UDS到底是怎么一层层把诊断指令发出去,又怎么把结果拿回来的。我刚开始接触UDS时,也被这堆分层搞得有点晕,后来自己动手抓了几次报文,才真正看明白。

3.1 UDS协议栈的分层结构

UDS协议栈,它不是一个大铁块,而是分层的。你想想看,就像咱们盖楼,地基、框架、装修,各司其职。UDS也一样,主要分三层:应用层会话层(有时也叫表示层,但咱们按实际工程习惯来)和传输层

  • 应用层(Application Layer):这是最上面的一层,也是咱们工程师直接打交道的地方。它定义了具体的诊断服务,比如读取数据、写入数据、执行例程。说白了,就是“我要干什么”。
  • 会话层(Session Layer):这一层负责管理诊断会话。比如,你进入了一个“扩展诊断会话”,才能执行一些写操作。它就像个门禁,控制着你能做什么。
  • 传输层(Transport Layer):这是最底层,负责把应用层的数据打包成网络能传输的格式,并保证数据能可靠地发出去。在CAN总线上,它通常就是ISO 15765-2(也就是我们常说的CAN TP)。

我个人习惯,把这三层比作一个快递系统:

  • 应用层:你写了一张快递单,上面写着“我要寄一个零件到上海”。这就是诊断服务。
  • 会话层:快递员检查你的单子,确认你是否有权限寄这个零件。这就是会话管理。
  • 传输层:快递员把零件打包、装箱、贴上地址标签,然后扔上卡车。这就是CAN TP的活儿。

3.2 诊断服务与子服务

UDS协议里,每个诊断服务都有一个唯一的服务ID(SID,Service Identifier)。比如,0x22是“按标识符读取数据”,0x2E是“按标识符写入数据”。

但光有服务ID还不够。很多服务下面还有子服务(Sub-function)。子服务用一个字节表示,它告诉ECU,这个请求是“请求”还是“响应”,或者有没有额外的控制逻辑。

举个例子,例程控制服务(0x31),它的子服务可以是:

  • 0x01:启动例程
  • 0x02:停止例程
  • 0x03:查询例程结果

你看,同一个服务ID,通过不同的子服务,就能实现完全不同的操作。我在项目中遇到过,有个同事把子服务号写错了,本来想启动一个自学习例程,结果发成了停止,折腾了半天才发现。嗯,这里要注意,子服务的高位bit(bit 7)还控制着响应抑制,这个咱们后面讲。

避坑指南: 我曾经在调试一个项目时,发现ECU对0x31服务的“启动”子服务没反应。查了半天,原来是子服务字节的bit 7被置1了,变成了“抑制正响应”。ECU执行了操作,但就是不回正响应报文。所以,写代码时一定要检查子服务字节的每一位。

3.3 请求与响应机制

UDS的通信,本质上就是一问一答。诊断仪(Tester)发一个请求(Request),ECU回一个响应(Response)。这个机制很简单,但细节里藏着不少坑。

一个标准的请求报文格式是这样的:

| 服务ID (1字节) | 子服务 (1字节, 可选) | 数据参数 (N字节) |

比如,读取ECU的VIN码(车辆识别号),请求报文就是:

22 F1 90

这里,0x22是服务ID(读取数据),0xF1 90是数据标识符(DID),告诉ECU我要读VIN码。

ECU收到后,会回复一个响应报文:

62 F1 90 [VIN码数据]

0x62是响应服务ID(就是请求服务ID 0x22 + 0x40),0xF1 90还是那个DID,后面跟着的就是VIN码的字节数据。

你可能会问:“如果请求出错了怎么办?” 好问题。ECU会回复一个否定响应(Negative Response),格式是:

7F [请求的服务ID] [否定响应码(NRC)]

比如,你请求读一个不存在的DID,ECU可能回复:

7F 22 31

这里的0x31就是NRC,意思是“请求超出范围”。

注意: 否定响应码(NRC)是诊断调试的关键。我建议你把常用的NRC背下来,比如0x12(子服务不支持)、0x13(报文长度或格式错误)、0x22(条件不满足)。遇到问题,先看NRC,能省一半的排查时间。

3.4 一个完整的请求-响应流程

咱们来走一遍完整的流程,加深理解。假设诊断仪要读取ECU的当前车速:

  1. 诊断仪:构建请求报文,服务ID = 0x22,DID = 0x010C(车速的DID)。
  2. 传输层:把请求报文打包成CAN帧,通过CAN总线发出去。
  3. ECU:收到CAN帧,传输层解包,把数据交给应用层。
  4. ECU应用层:解析服务ID = 0x22,DID = 0x010C。检查当前会话是否有权限读取。如果有,就去获取车速值。
  5. ECU:构建响应报文,服务ID = 0x62,DID = 0x010C,数据 = 车速值(比如0x0032,代表50 km/h)。
  6. 传输层:把响应报文打包成CAN帧,发回给诊断仪。
  7. 诊断仪:收到响应,解析出车速 = 50 km/h,显示在界面上。

你看,整个过程环环相扣。任何一个环节出问题,比如传输层丢包、应用层权限不够,都会导致诊断失败。

我个人经验: 在实际项目中,我习惯先用一个简单的“Tester Present(0x3E)”服务来验证通信链路是否正常。如果0x3E能通,说明传输层和会话层没问题,问题大概率出在应用层。这招屡试不爽。

3.5 总结一下

UDS协议栈的分层,让诊断功能变得清晰、可维护。服务与子服务的组合,提供了灵活的诊断能力。而请求与响应机制,则是整个诊断通信的基础。记住,理解分层是看懂UDS报文的第一步,而掌握请求-响应机制是调试诊断功能的核心

下一章,咱们会深入聊聊具体的诊断服务,比如0x22、0x2E、0x31这些常用的。到时候,我会结合代码示例,带你手把手实现一个简单的诊断服务。敬请期待。