第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个服务到底是怎么工作的。到时候见!