第2章:UDS协议栈架构:五层模型详解

大家好,我是你们的老朋友。今天我们来聊聊UDS协议栈的架构。

很多新手一上来就盯着诊断报文看,结果越看越懵。为什么?因为你不理解分层。我刚开始做UDS开发时,也犯过这个错。后来一位老工程师点醒了我:「诊断通信就像寄快递,每一层各司其职」。今天我就把这五层掰开揉碎了讲给你听。

2.1 为什么需要分层?

你想想看,如果所有功能都揉在一起,会是什么后果?

  • 应用层想改个诊断服务,结果发现底层驱动也要改
  • 换个CAN控制器,整个协议栈都得重写
  • 出了问题,根本不知道是哪个环节的锅

分层就是为了解决这些问题。每一层只关心自己的事,层与层之间通过标准接口通信。这样,上层不用管底层用什么硬件,底层也不用管上层在做什么服务

核心思想: 分层隔离,各司其职。这是所有通信协议栈的通用设计哲学。

2.2 五层模型总览

UDS协议栈从上到下分为五层:

层级 名称 主要职责 对应标准
第5层 应用层 诊断服务定义、数据解析 ISO 14229-1
第4层 会话层 会话管理、定时控制 ISO 14229-2
第3层 网络层 报文分段、重组、流控 ISO 15765-2
第2层 数据链路层 帧封装、错误检测、仲裁 ISO 11898-1
第1层 物理层 电气特性、位编码、传输介质 ISO 11898-2

嗯,这里要注意:并不是所有UDS实现都必须用这五层。比如有些简单的LIN诊断,可能就三层。但CAN上的UDS,这五层一个都不能少。

2.3 应用层(第5层)—— 诊断服务的「大脑」

应用层是离我们工程师最近的一层。说白了,它就是诊断服务的定义和实现

这一层主要干三件事:

  • 服务定义: 比如0x10(诊断会话控制)、0x22(读取数据)、0x2E(写入数据)等
  • 数据解析: 把收到的字节流解析成有意义的参数
  • 响应生成: 根据请求生成肯定响应或否定响应

举个例子,你发一个读取VIN码的请求:

请求:22 F1 90
  - 22 = 服务ID(读取数据)
  - F1 90 = 数据标识符(VIN码的ID)

响应:62 F1 90 4C 56 47 ...(VIN码内容)
  - 62 = 肯定响应(22 + 0x40)
  - F1 90 = 数据标识符回显
  - 4C 56 47... = VIN码的ASCII码

我的经验: 应用层最容易出bug的地方是「否定响应码」的处理。很多工程师只写了肯定响应的逻辑,忘了处理各种异常情况。我曾经因为一个NRC(否定响应码)没处理好,导致ECU在特定条件下死机,查了整整两天。

2.4 会话层(第4层)—— 诊断会话的「管家」

会话层很多人会忽略,但它其实非常关键。它管理着诊断会话的状态和定时

UDS定义了三种会话:

  • 默认会话(Default Session): 上电后的初始状态,功能受限
  • 编程会话(Programming Session): 用于刷写软件,需要安全访问
  • 扩展会话(Extended Session): 用于标定、调试等高级功能

会话层还负责三个定时器:

定时器 默认值 作用
S3Server 5秒 会话超时时间,超时自动回到默认会话
S2Server 50ms 响应超时时间,超过这个时间没响应就报错
S3Client 5秒 客户端发送TesterPresent的间隔

为什么会这样设计?你想想看,如果ECU进入了编程会话,但测试设备突然断开了,ECU总不能一直卡在编程会话里吧?所以会话层会自动超时回退,保证ECU的安全性。

避坑指南: 我曾经遇到过一个问题:ECU在扩展会话下做标定,结果S3Server定时器设置得太短,标定还没做完就超时回退到默认会话了。后来我把S3Server改成了30秒,才解决问题。记住:定时器的值要根据实际应用场景来调整,不是越大越好,也不是越小越好。

2.5 网络层(第3层)—— 报文的「快递员」

网络层是UDS协议栈里最复杂的一层。它的核心任务就是把应用层的数据打包成CAN帧发送出去,再把收到的CAN帧重组回应用层数据

为什么需要网络层?因为CAN帧一次最多只能传8个字节(CAN 2.0)。但UDS的请求/响应可能很长,比如刷写一个软件包可能有几兆字节。这时候就需要网络层来做分段和重组

网络层定义了四种帧类型:

  • 单帧(SF): 数据长度 ≤ 7字节,一帧搞定
  • 首帧(FF): 多帧传输的第一帧,包含总数据长度
  • 连续帧(CF): 后续的数据帧,带序列号
  • 流控帧(FC): 接收方告诉发送方「慢点发」或「继续发」

看个多帧传输的例子:

发送方:                       接收方:
  FF (首帧)  ──────────────>  
  数据长度 = 100字节
                                FC (流控帧)  <──────────────
                                BS=2, STmin=10ms
  CF (连续帧, SN=1) ──────>  
  CF (连续帧, SN=2) ──────>  
                                FC (流控帧)  <──────────────
                                BS=2, STmin=10ms
  CF (连续帧, SN=3) ──────>  
  ...(直到发送完毕)

关键参数:

  • BS(Block Size): 一次最多发多少帧
  • STmin(Separation Time): 帧与帧之间的最小间隔

这两个参数直接影响传输效率。BS太小,流控帧太多,效率低;BS太大,接收方缓冲区可能溢出。

我个人习惯把BS设为10,STmin设为5ms。这个组合在大多数场景下表现都不错。当然,具体值要根据你的硬件性能和实时性要求来调。

2.6 数据链路层(第2层)—— CAN协议的「执行者」

数据链路层就是CAN协议本身。它负责:

  • 帧封装: 把网络层的数据包成CAN帧(SOF、ID、DLC、数据、CRC、ACK、EOF)
  • 错误检测: CRC校验、位填充、格式检查
  • 仲裁机制: 多个节点同时发送时,ID小的优先
  • 确认机制: 接收方发送ACK位确认收到

这一层对UDS开发者来说,通常不需要太关心。因为CAN控制器硬件已经帮你实现了大部分功能。你只需要配置好CAN控制器的寄存器就行。

但有一点要注意:CAN ID的分配。UDS诊断通常使用功能寻址(0x7DF)和物理寻址(比如0x7E0~0x7E7)。功能寻址是一对多,物理寻址是一对一。

我的建议: 在项目初期就规划好CAN ID的分配方案。我曾经在一个项目里,因为CAN ID分配不合理,导致诊断报文和普通报文冲突,排查了整整一周。后来我们制定了严格的ID分配规则:诊断报文用0x7xx段,普通报文用0x1xx~0x6xx段,从此再没出过问题。

2.7 物理层(第1层)—— 信号的「搬运工」

物理层是最底层,它定义了:

  • 电气特性: 电压电平、电流驱动能力
  • 位编码: NRZ编码、位填充
  • 传输介质: 双绞线、终端电阻
  • 连接器: DB9、OBD-II接口

对于UDS开发者来说,物理层通常不需要直接操作。但了解一些基本知识还是有必要的:

  • CAN总线需要两个120Ω的终端电阻,一个在总线一端,一个在另一端
  • CAN_H和CAN_L的差分电压决定了逻辑0和逻辑1
  • 总线长度越长,通信速率越低(比如500kbps时,总线长度最好不超过40米)

避坑指南: 我曾经在一个现场调试中,发现诊断通信时好时坏。查了半天,发现是终端电阻没接好。CAN总线如果没有终端电阻,信号会反射,导致通信不稳定。记住:终端电阻不是可选项,是必选项

2.8 五层之间的数据流

最后,我们来看看数据是怎么在这五层之间流动的:

应用层(第5层):  22 F1 90(请求读取VIN码)
     ↓
会话层(第4层):  检查当前会话是否允许该服务
     ↓
网络层(第3层):  封装成单帧:02 22 F1 90
     ↓
数据链路层(第2层): 封装成CAN帧:ID=0x7E0, DLC=4, Data=02 22 F1 90
     ↓
物理层(第1层):  转换成差分信号,发送到CAN总线上

接收端则反过来:

物理层(第1层):  接收差分信号,还原成数字信号
     ↓
数据链路层(第2层): 解析CAN帧,提取数据
     ↓
网络层(第3层):  重组数据,判断是单帧还是多帧
     ↓
会话层(第4层):  更新会话定时器
     ↓
应用层(第5层):  解析服务ID,执行读取VIN码操作,生成响应

嗯,到这里,UDS协议栈的五层模型就讲完了。你可能会问:这些层在实际开发中都要自己实现吗?

答案是否定的。大多数情况下,物理层和数据链路层由硬件和驱动实现,网络层和会话层由协议栈软件实现,应用层才是我们开发者需要重点关注的

下一章,我会带你深入应用层,看看UDS的27个服务到底是怎么工作的。到时候见!