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快照数据。这时候就需要多帧传输了。

多帧传输分三个阶段:

  1. 首帧(First Frame, FF):发送方告诉接收方“我要发一大包数据,总共多少字节”
  2. 流控制帧(Flow Control, FC):接收方回应“我知道了,你按这个节奏发”
  3. 连续帧(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倍。为什么?因为有时候会出现“粘包”的情况——上一个多帧传输还没结束,下一个首帧又来了。这时候如果缓冲区不够,就只能丢弃。

重组的关键步骤:

  1. 收到首帧,解析总长度,分配缓冲区
  2. 发送流控制帧,告诉发送方自己的接收能力
  3. 接收连续帧,按帧序号填入缓冲区
  4. 检查帧序号是否连续,有没有丢帧
  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)和诊断服务。到时候我会结合我实际项目中的案例,给大家讲讲那些“看起来简单,做起来全是坑”的诊断服务。