2. UDS协议栈基础:UDS协议栈架构、应用层、会话层、网络层、数据链路层、物理层

好,咱们进入第二章。这一章,我打算把UDS协议栈的骨架给你拆开看看。

很多人一上来就啃ISO 14229-1,结果被那一堆状态机、定时器、服务ID搞得晕头转向。其实没必要。你想想看,UDS协议栈说白了就是一套“汽车ECU的沟通规则”。它规定了:谁先说话、说什么、怎么说、说错了怎么办。

我个人习惯,把UDS协议栈分成五层来理解。从下往上分别是:物理层、数据链路层、网络层、会话层、应用层。嗯,咱们一层一层剥开它。

2.1 协议栈整体架构

先给你看个总览图,心里有个谱。

OSI七层模型 UDS协议栈对应层 核心协议/标准 我眼中的职责
应用层 (Layer 7) UDS应用层 ISO 14229-1 定义诊断服务(读数据、写数据、例程等)
表示层 (Layer 6) (合并到应用层) - 数据编码格式(比如Intel/Motorola格式)
会话层 (Layer 5) UDS会话层 ISO 14229-2 管理诊断会话(默认、扩展、编程)和定时参数
传输层 (Layer 4) (由网络层承载) - 分段传输的大数据包(比如刷写文件)
网络层 (Layer 3) UDS网络层 ISO 15765-2 (DoCAN) 单帧/多帧传输、流控制、地址寻址
数据链路层 (Layer 2) CAN数据链路层 ISO 11898-1 CAN帧的封装、仲裁、错误检测
物理层 (Layer 1) CAN物理层 ISO 11898-2 差分信号、电平转换、总线拓扑

这张表,我建议你收藏一下。每次调试遇到问题,先定位是哪一层出的问题,能省不少时间。

2.2 物理层:信号怎么跑

物理层,就是最底层的“电线”。在车载诊断里,最常见的就是CAN总线物理层。

说白了,就是两根线:CAN_H和CAN_L。通过差分电压来传递0和1。为什么用差分?抗干扰能力强。你想想看,发动机舱里电磁干扰那么大,单端信号早被干废了。

关键参数:

  • 波特率: 诊断常用250kbps或500kbps。我遇到过一些老车型,只能用125kbps,慢得让人抓狂。
  • 终端电阻: 120Ω,必须加在总线两端。有一次我在台架上调试,忘了接终端电阻,波形乱七八糟,查了半天才发现。
  • 隐性/显性: 隐性=1(CAN_H≈2.5V,CAN_L≈2.5V),显性=0(CAN_H≈3.5V,CAN_L≈1.5V)。显性会覆盖隐性。
⚠️ 避坑指南: 我曾经在实车上遇到一个诡异问题——诊断仪偶尔连不上ECU。最后发现是OBD接口的CAN_H针脚氧化了,接触电阻变大。所以,物理层的问题往往最隐蔽,先检查线束和接头。

2.3 数据链路层:帧怎么传

数据链路层,负责把物理层的比特流,组装成有意义的“帧”。在CAN总线里,就是CAN帧。

一个标准的CAN数据帧长这样:

| SOF | 仲裁场(11/29bit ID) | 控制场(IDE, DLC) | 数据场(0-8字节) | CRC场 | ACK场 | EOF |

这里我只强调几个对UDS重要的点:

  • CAN ID: 诊断请求和响应使用不同的ID。比如请求ID 0x7DF,响应ID 0x7E8。这是物理寻址。
  • DLC(数据长度码): 表示数据场有多少字节。UDS的CAN帧,DLC最大是8(经典CAN)或64(CAN FD)。
  • 数据场: 这才是UDS协议真正关心的内容。网络层的数据,就塞在这里面。

嗯,这里要注意:CAN数据链路层只保证一帧数据不出错。但如果UDS报文超过8字节,就需要网络层来拆包和重组了。

2.4 网络层:数据太大怎么办

网络层,这是UDS协议栈里最“烧脑”的一层,也是ISO 15765-2的核心内容。

为什么需要网络层?因为UDS的一个诊断请求,可能包含几十甚至几百字节(比如刷写ECU的bin文件)。但CAN一帧最多只能发8字节。怎么办?拆!

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

帧类型 PCI字节(协议控制信息) 作用
单帧 (SF) 0x0 + 数据长度 报文长度 ≤ 7字节时使用
首帧 (FF) 0x1 + 数据长度(高4位+低8位) 多帧传输的第一帧,告知总长度
连续帧 (CF) 0x2 + 序列号(SN) 后续数据帧,SN从1开始循环
流控制帧 (FC) 0x3 + 流状态(FS) + 块大小(BS) + 最小间隔时间(STmin) 接收方告诉发送方:慢点发/继续发/别发了

我举个例子,你马上就懂了。假设你要发送一个20字节的诊断请求:

  1. 发送方: 先发一个首帧(FF),告诉接收方“我要发20字节”。
  2. 接收方: 收到FF后,回复一个流控制帧(FC),说“我知道了,你一次发2帧,每帧间隔5ms”。
  3. 发送方: 按照FC的要求,发送连续帧(CF),每帧带2字节数据,直到发完。
💡 个人经验: 流控制帧里的STmin参数特别关键。我曾经把一个ECU的STmin设成了0(无间隔),结果接收方的CAN控制器缓冲区溢出,直接丢帧。后来我习惯把STmin设为0x10(1ms),既保证速度又稳定。

2.5 会话层:ECU在什么状态

会话层,负责管理ECU的“诊断会话状态”。说白了,就是ECU现在愿不愿意理你。

UDS定义了三种默认会话:

  • 默认会话 (Default Session): 上电后的初始状态。只能执行一些“安全”的服务,比如读故障码。不能刷写。
  • 扩展会话 (Extended Session): 允许执行一些“非安全”但非破坏性的服务,比如读写参数、调整配置。
  • 编程会话 (Programming Session): 最高权限。可以刷写ECU固件。进入此会话前,通常需要安全访问解锁。

会话切换是通过10 02(进入扩展)或10 03(进入编程)服务实现的。ECU会回复一个50 0250 03表示成功。

🔧 调试技巧: 如果你发诊断请求,ECU一直不回,或者回NRC 0x78(请求正确接收,但正在处理),先检查一下当前会话对不对。我见过有人没切到编程会话,就发刷写请求,结果ECU一直不理他。

2.6 应用层:真正干活的地方

应用层,就是ISO 14229-1定义的那些诊断服务。这是咱们最终要打交道的地方。

应用层的报文结构很简单:

| 服务ID (SID) | 子功能 (Sub-function) | 数据参数 (Data) |

比如:

  • 22 F1 90:读取DID(数据标识符)F190的值。SID=0x22,子功能=0xF190(其实是数据参数)。
  • 2E F1 90 01 02 03 04:向DID F190写入4个字节的数据。
  • 31 01 FF 00:启动例程ID为FF00的例程。

应用层还定义了“否定响应码”(NRC)。比如:

NRC 含义 常见场景
0x10 一般拒绝 请求格式不对
0x22 条件不满足 没切到正确会话就发请求
0x31 请求超出范围 DID或RID不存在
0x33 安全访问拒绝 没解锁就尝试刷写
0x78 请求正确接收,正在处理 ECU需要时间处理,让你等着

我个人习惯,在写代码前,先把所有可能用到的NRC列个表。这样调试时,看到ECU回了个0x33,我马上就知道是安全访问没通过。

2.7 各层之间的协作

最后,咱们串一下。一个完整的UDS请求,从诊断仪到ECU,是怎么走的?

  1. 应用层: 诊断仪构造一个请求,比如22 F1 90(读数据)。
  2. 会话层: 检查当前会话是否允许执行0x22服务。如果不允许,直接返回NRC 0x22。
  3. 网络层: 如果请求长度≤7字节,封装成单帧(SF)。如果超过,拆成多帧。
  4. 数据链路层: 把网络层的帧,塞进CAN帧的数据场,加上CAN ID和CRC。
  5. 物理层: 通过CAN_H和CAN_L,把比特流发到总线上。

ECU收到后,反向解析:物理层→数据链路层→网络层→会话层→应用层,然后执行服务,再按同样的路径返回响应。

📌 总结: 这一章,咱们把UDS协议栈的五层结构过了一遍。你不需要记住每一层的所有细节,但一定要理解“分层”的思想。遇到问题,先定位是哪一层,再深入查。这是工程师的基本素养。

下一章,咱们会深入应用层,把那些常用的诊断服务一个一个讲透。到时候,你就可以自己动手写诊断请求了。