第1章:OSI模型与UDS:UDS在OSI七层模型中的映射、物理层与数据链路层基础、网络层(ISO 15765-2)简介
各位同行,大家好。我是老李,在汽车嵌入式这行摸爬滚打了十几年。今天咱们开始聊UDS诊断协议,第一节课,我打算先把UDS的“骨架”给大家搭起来——也就是它跟OSI七层模型的关系。
你想想看,UDS协议本身是应用层的规范,但它要跑在CAN、LIN或者以太网上。这中间怎么封装、怎么传输、怎么保证不出错?这就得靠OSI模型来分层解耦。说白了,每一层各司其职,应用层只管“我要读这个DID”,网络层只管“把数据切成帧发出去”,物理层只管“把电平拉高拉低”。
我个人习惯,在开始写代码之前,一定先把这几层的关系画清楚。不然调试的时候,你根本分不清问题是出在应用层逻辑,还是网络层丢帧,还是物理层信号质量差。
1.1 UDS在OSI七层模型中的映射
UDS协议(ISO 14229)主要定义在应用层(第7层)。但它不是孤立的,它依赖下面几层的服务。我整理了一个映射表,大家一看就明白:
| OSI层 | UDS对应的协议/标准 | 我的理解 |
|---|---|---|
| 7 应用层 | ISO 14229-1 (UDS) | 定义服务ID、子功能、数据格式。比如0x22读数据,0x2E写数据。 |
| 6 表示层 | 无(UDS直接使用应用层数据) | UDS不搞加密解密那一套,数据怎么发就怎么收。 |
| 5 会话层 | 无(UDS通过应用层管理会话) | 会话控制(0x10服务)其实是在应用层实现的。 |
| 4 传输层 | ISO 15765-2 (DoCAN) / ISO 13400 (DoIP) | 负责长数据的拆分与重组。CAN单帧最多8字节,超过就得靠传输层。 |
| 3 网络层 | ISO 15765-2 (网络层部分) | 寻址、流控制、单帧/多帧判断。这是今天重点。 |
| 2 数据链路层 | ISO 11898-1 (CAN控制器) | CAN帧的封装、仲裁、错误检测。硬件帮你做了大部分。 |
| 1 物理层 | ISO 11898-2 (CAN收发器) | 差分信号、电平转换、位时序。嗯,这里容易出硬件问题。 |
重点记住:UDS应用层(第7层)的数据,要经过网络层(第3层)的打包,再交给数据链路层(第2层)发送。你写的诊断代码,其实是在第7层工作,但必须理解第3层怎么切包。
1.2 物理层与数据链路层基础
物理层和数据链路层,说白了就是CAN总线的基础。我刚开始做诊断时,总觉得这两层是硬件工程师的事,软件不用管。直到有一次,ECU在高温下诊断通信频繁超时,查了三天,最后发现是CAN收发器的共模电压漂了。
从那以后,我养成了一个习惯:写诊断代码前,先确认物理层没问题。
物理层(ISO 11898-2)
- 差分信号:CAN_H和CAN_L两根线,电压差决定显性/隐性。显性(0)时,CAN_H≈3.5V,CAN_L≈1.5V;隐性(1)时,两者都≈2.5V。
- 位时序:每个位由同步段、传播段、相位缓冲段1、相位缓冲段2组成。采样点通常设在87.5%左右。我建议采样点设在85%~90%之间,太靠前或太靠后都容易误判。
- 终端电阻:总线两端各120Ω。少了信号反射,多了驱动能力不足。
避坑指南:我曾经遇到过一个问题——ECU在低温下诊断通信失败。查到最后,发现是CAN收发器的驱动电流在低温下衰减,导致显性电平不够。所以,物理层选型一定要看温度范围,别只看常温参数。
数据链路层(ISO 11898-1)
数据链路层由CAN控制器(比如SJA1000、M_CAN)实现。它负责:
- 帧封装:标准帧11位ID,扩展帧29位ID。诊断通常用扩展帧,因为需要包含源地址和目标地址。
- 仲裁:ID越小优先级越高。诊断报文通常设较低ID(高优先级),确保诊断请求能及时发送。
- 错误检测:CRC校验、位填充、格式检查。硬件自动完成,软件只需要读错误状态寄存器。
你想想看,数据链路层其实把大部分脏活累活都干了。软件只需要配置好CAN控制器,然后往发送缓冲区写数据,再从接收缓冲区读数据。嗯,这里要注意:发送缓冲区满时,别硬写,要等。
1.3 网络层(ISO 15765-2)简介
网络层是UDS诊断的“交通警察”。它负责把应用层的数据(可能很长)切成适合CAN总线传输的小块,再在接收端拼回去。
ISO 15765-2定义了四种协议数据单元(PDU):
| PDU类型 | 用途 | PCI字节内容 |
|---|---|---|
| 单帧(SF) | 数据长度 ≤ 7字节(CAN标准帧)或 ≤ 8字节(CAN FD) | PCI = 0x0 + 数据长度 |
| 首帧(FF) | 数据长度 > 7字节,发送第一个帧 | PCI = 0x10 + 数据长度高4位,第二字节为数据长度低8位 |
| 连续帧(CF) | 首帧之后的所有后续帧 | PCI = 0x20 + 帧序号(1~15循环) |
| 流控制帧(FC) | 接收端告诉发送端:继续发/等待/停止 | PCI = 0x30 + 流状态 + 块大小 + 最小间隔时间 |
核心逻辑:发送端发首帧(FF),接收端回流控制帧(FC),然后发送端发连续帧(CF),直到数据发完。这就是所谓的“多帧传输”。
流控制参数详解
流控制帧里有三个关键参数,我当年调试时被它们坑过:
- 流状态(FS):
- 0x0 = 继续发送(CTS)
- 0x1 = 等待(WAIT)
- 0x2 = 溢出/中止(OVFLW)
- 块大小(BS):允许连续发送的CF帧数量。BS=0表示不限制,一口气发完。BS=5表示发5个CF后,必须等下一个FC。
- 最小间隔时间(STmin):两个连续CF帧之间的最小时间间隔。单位是微秒(0x00~0x7F)或毫秒(0xF1~0xF9)。
我的经验:STmin别设太小。我曾经设了0x00(0微秒),结果接收端处理不过来,丢帧了。后来改成0x10(16微秒),问题解决。建议STmin至少设0x10以上,除非你确认接收端足够快。
一个简单的多帧传输示例
假设你要发送一个长度为20字节的诊断请求(比如0x22 + DID + 数据)。CAN标准帧每帧最多8字节,减去PCI占用的1~2字节,实际有效数据只有6~7字节。所以需要多帧传输:
发送端(Tester):
FF: PCI=0x14 (20字节), 数据=0x22 0x01 0x02 0x03 0x04 0x05 0x06
接收端(ECU):
FC: PCI=0x30, FS=0x00 (继续), BS=5, STmin=0x10 (16微秒)
发送端:
CF1: PCI=0x21, 数据=0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D
CF2: PCI=0x22, 数据=0x0E 0x0F 0x10 0x11 0x12 0x13 0x14
CF3: PCI=0x23, 数据=0x15 0x16 0x17 0x18 0x19 0x1A 0x1B
CF4: PCI=0x24, 数据=0x1C 0x1D 0x1E 0x1F 0x20 0x21 0x22
CF5: PCI=0x25, 数据=0x23 0x24 0x25 0x26 0x27 0x28 0x29
(发完5个CF,等待下一个FC)
接收端:
FC: PCI=0x30, FS=0x00, BS=5, STmin=0x10
发送端:
CF6: PCI=0x26, 数据=0x2A 0x2B 0x2C 0x2D 0x2E 0x2F 0x30
CF7: PCI=0x27, 数据=0x31 0x32 0x33 0x34 0x35 0x36 0x37
(数据发完,结束)
注意:CF帧的序号从1开始,到15后循环回0。如果序号不连续,接收端会认为丢帧,直接丢弃整个多帧消息。我曾经因为序号计算错误,导致ECU一直收不到完整请求,排查了半天才发现是序号写成了0x20而不是0x21。
小结
这一章我们聊了UDS在OSI模型中的位置,重点看了物理层、数据链路层和网络层。我个人觉得,理解网络层(ISO 15765-2)的多帧传输机制,是写好诊断代码的前提。你想想看,如果连数据怎么切、怎么拼都不清楚,那应用层的服务写得再漂亮,也传不出去。
下一章,我们会深入UDS应用层,从最常用的0x10(诊断会话控制)和0x3E(测试器在线)开始。嗯,到时候我会分享一些实际项目中的会话管理经验,敬请期待。
公众号:蓝海资料掘金营,微信deep3321