4. UDS网络层(ISO 15765-2):单帧与多帧传输机制
好,咱们今天聊聊UDS诊断里的网络层。说白了,就是ISO 15765-2这个标准。很多刚入行的朋友觉得这层就是个“打包拆包”的活儿,没什么技术含量。但我得说,这层要是搞不明白,你后面做诊断开发,踩坑是迟早的事。
我个人习惯把网络层理解成“快递员”。应用层把诊断数据写好,交给网络层,它负责把数据切成合适的大小,通过CAN总线发出去,到了接收端再重新拼起来。就这么简单?嗯,细节都在里面。
4.1 单帧传输(Single Frame, SF)
先说说最简单的单帧。什么时候用单帧?就是你的诊断数据很小,一个CAN报文就能装下。
CAN报文的数据场最多8个字节(CAN FD我们先不谈)。网络层会用第一个字节的一部分作为协议控制信息(PCI),剩下的才是真正的数据。所以单帧能传的有效数据,最多是7个字节。
单帧格式:
- 第一个字节的高4位:固定为0,表示这是单帧
- 第一个字节的低4位:表示数据长度(0-7)
- 后续字节:真正的诊断数据
举个例子,你发一个诊断请求,比如读取VIN码的请求,可能就2个字节。那直接用一个单帧发出去就行了。接收方看到第一个字节的高4位是0,就知道这是个单帧,直接取后面的数据去处理。
我的经验:很多初学者会忽略单帧的长度字段。我曾经见过一个同事,单帧数据长度明明是3个字节,但他把长度字段写成了0x02。结果ECU只处理了前2个字节,第三个字节被当成了下一个报文的开始...嗯,排查了好久才发现是这里的问题。
4.2 多帧传输(Multi-Frame, MF)
那诊断数据超过7个字节怎么办?比如你要上传一个Bootloader文件,或者读取一大段DTC快照数据。这时候就需要多帧传输了。
多帧传输分三个阶段:
- 首帧(First Frame, FF):发送方告诉接收方“我要发一大包数据,总共多少字节”
- 流控制帧(Flow Control, FC):接收方回应“我知道了,你按这个节奏发”
- 连续帧(Consecutive Frame, CF):发送方按照约定的节奏,一帧一帧地把数据发完
首帧的格式和单帧不一样。首帧的第一个字节高4位是0x01,低4位和第二个字节组合起来,表示总的数据长度(最多4095个字节)。
首帧格式:
- 第一个字节高4位:0x1(首帧标识)
- 第一个字节低4位 + 第二个字节:总数据长度(12位)
- 后续6个字节:第一批数据
连续帧就简单了。第一个字节高4位是0x02,低4位是帧序号(从1开始,到0xF后回0)。后面的7个字节都是数据。
注意:帧序号到0xF后,下一个必须是0x00,而不是0x10。我见过有人在这里写了个0x10,结果接收方直接丢弃了后续所有帧。这个坑,踩过的人都知道疼。
4.3 流控制协议(Flow Control, FC)
流控制协议,说白了就是接收方告诉发送方:“你慢点发,我处理不过来”或者“你快点发,我等着呢”。
流控制帧只有3个字节:
| 字节 | 含义 | 说明 |
|---|---|---|
| 第1字节高4位 | 流状态(FS) | 0x0: 继续发送(CTS) 0x1: 等待(WAIT) 0x2: 溢出(OVFLW) |
| 第1字节低4位 | 块大小(BS) | 允许连续发送的帧数,0表示不限 |
| 第2字节 | 最小间隔时间(STmin) | 相邻连续帧之间的最小间隔,单位ms |
我个人习惯把流控制理解成“红绿灯”。
- CTS(0x0):绿灯,继续发。但要注意BS和STmin的限制。
- WAIT(0x1):黄灯,等一下。发送方必须等待下一个FC帧。
- OVFLW(0x2):红灯,我装不下了。发送方收到这个,应该终止传输。
避坑指南:我曾经在一个项目里,ECU的接收缓冲区只有256字节,但Tester一次性发了500字节的数据。ECU收到首帧后,发现数据长度超过缓冲区,直接回了OVFLW。但Tester那边没处理这个状态,还在傻傻地发连续帧...结果就是总线被垃圾数据塞满,其他节点都受影响。所以,OVFLW一定要处理,不能忽略。
4.4 分段传输(Segmentation)与重组(Reassembly)
分段传输,就是把一大包数据切成多个CAN报文发出去。重组,就是接收方把这些报文按顺序拼回原来的数据。
这里有个关键点:分段是在网络层完成的,应用层完全感知不到。你应用层调用一个发送接口,传一个100字节的buffer进去,网络层自动帮你切成15个CAN报文(1个首帧+14个连续帧),再一个一个发出去。接收方收到后,网络层自动重组,然后通知应用层“数据到了”。
重组的时候,接收方需要维护一个缓冲区。我建议缓冲区大小至少是你要接收的最大数据长度的1.5倍。为什么?因为有时候会出现“粘包”的情况——上一个多帧传输还没结束,下一个首帧又来了。这时候如果缓冲区不够,就只能丢弃。
重组的关键步骤:
- 收到首帧,解析总长度,分配缓冲区
- 发送流控制帧,告诉发送方自己的接收能力
- 接收连续帧,按帧序号填入缓冲区
- 检查帧序号是否连续,有没有丢帧
- 所有数据收齐后,校验完整性,通知应用层
4.5 网络层定时参数
网络层有四个关键的定时参数,搞不清楚的话,诊断通信随时可能超时断开。
| 参数 | 名称 | 典型值 | 说明 |
|---|---|---|---|
| N_As | 发送时间 | 1ms | 从应用层请求发送,到CAN报文真正发出 |
| N_Ar | 接收时间 | 1ms | 从CAN报文到达,到网络层通知应用层 |
| N_Bs | 首帧到流控制时间 | 50ms | 发送方发出首帧后,等待FC帧的最大时间 |
| N_Br | 流控制到首帧时间 | 50ms | 接收方发出FC帧后,等待下一个首帧或连续帧 |
| N_Cs | 连续帧发送间隔 | 由STmin决定 | 相邻连续帧之间的最小间隔 |
| N_Cr | 连续帧接收超时 | 50ms | 接收方等待下一个连续帧的最大时间 |
重要提醒:N_Bs和N_Cr这两个超时参数,在实际项目中经常被忽略。我曾经遇到一个案例,Tester和ECU之间的CAN总线负载率很高,导致FC帧延迟了60ms才到达。Tester那边N_Bs设置的是50ms,结果超时了,直接终止了传输。后来我们把N_Bs改成了100ms,问题就解决了。所以,定时参数的设置一定要结合实际的网络负载情况,不能照搬标准里的典型值。
嗯,网络层的内容差不多就这些。你想想看,其实核心就是三件事:怎么切、怎么控、怎么拼。切的时候注意长度和序号,控的时候注意流状态和定时,拼的时候注意缓冲区和完整性。把这三点搞明白了,网络层这块你就拿下了。
下一章我们聊聊应用层,也就是UDS的核心——服务标识符(SID)和诊断服务。到时候我会结合我实际项目中的案例,给大家讲讲那些“看起来简单,做起来全是坑”的诊断服务。