3、UDS网络层与传输层:单帧与多帧传输、流控制协议、网络层定时参数、传输层错误处理
各位工程师朋友,咱们今天聊聊UDS协议栈里最“底层”也最容易被忽视的部分——网络层与传输层。说白了,就是诊断仪和ECU之间到底怎么“说话”的。很多人一上来就研究各种诊断服务(比如0x22、0x2E),结果报文发出去没响应,或者响应不完整,最后发现是网络层配置出了问题。
我个人习惯把这一层比作“快递系统”。你要寄一个包裹(诊断请求),如果包裹很小,一个信封(单帧)就搞定了。但如果是一台冰箱(多帧传输),你就得拆成几个箱子,分批寄,收件人还得告诉你“我收到第1箱了,继续发第2箱”。这个拆包、寄送、确认、重组的过程,就是网络层和传输层干的事。
核心要点:UDS网络层(ISO 15765-2)负责将诊断消息封装成CAN帧,并处理单帧/多帧的拆分与重组。传输层则负责端到端的可靠传输,包括流控制、超时重传和错误恢复。
3.1 单帧传输:小数据量的“直通车”
先看最简单的场景。诊断请求的数据长度 ≤ 7字节(CAN标准帧数据场为8字节,但PCI协议控制信息占用1字节,所以有效载荷最多7字节)。这种情况下,一个CAN帧就能搞定。
单帧的格式很简单:第一个字节的高4位是帧类型(单帧为0x0),低4位是数据长度(DLC,0-7)。后面跟着的就是实际数据。
// 单帧示例:请求VIN码(0x22 F1 90)
// 假设VIN码长度为17字节,但请求本身只有3字节
// 所以用单帧发送
CAN ID: 0x7DF
Data: 02 22 F1 90 00 00 00 00
// 解析:0x02 = 单帧(0x0) + 长度2字节
// 后面是服务ID 0x22 和 DID 0xF190
我在项目中遇到过一个问题:有个同事把单帧的长度字段写成了实际CAN数据场的长度(8),而不是有效载荷长度。结果ECU一直没响应,查了半天才发现是PCI字段解析错了。记住,单帧的DLC范围是0-7,不是0-8。
3.2 多帧传输:大数据的“分段运输”
当诊断数据超过7字节时,就得用多帧传输了。比如读取一个256字节的DTC快照数据,或者刷写一个2KB的Bootloader程序。多帧传输分为三个阶段:
- 首帧(FF):发送第一个帧,告诉接收方“我要发一个大包裹,总长度是XXX字节”。
- 流控制帧(FC):接收方回复“我准备好了,你一次可以发N帧,每帧间隔M毫秒”。
- 连续帧(CF):发送方按照流控制参数,一帧一帧地把剩余数据发完。
你想想看,这就像你给朋友寄一堆书。你先发个微信说“我要寄10本书”(首帧),朋友回“好的,你一次寄3本,每本间隔5分钟”(流控制),然后你分4次寄完(连续帧)。
| 帧类型 | PCI高4位 | PCI低4位/后续字节 | 说明 |
|---|---|---|---|
| 单帧 (SF) | 0x0 | 数据长度 (0-7) | 单个CAN帧完成传输 |
| 首帧 (FF) | 0x1 | 数据总长度 (8-4095) | 多帧传输的第一个帧 |
| 连续帧 (CF) | 0x2 | 序列号 (0-15) | 后续数据帧,循环计数 |
| 流控制帧 (FC) | 0x3 | 流状态 + BS + STmin | 接收方控制发送节奏 |
个人经验:首帧的数据总长度字段是12位的,最大支持4095字节。但实际项目中,很多ECU的接收缓冲区只有256或512字节。我曾经调试过一个刷写失败的问题,就是因为发送方一次性发了800字节,而ECU的缓冲区只有512字节,直接溢出。所以,发送前最好确认ECU的接收能力,或者通过流控制帧的BS参数来限制。
3.3 流控制协议:谁说了算?
流控制帧(FC)是接收方用来控制发送方节奏的。它包含三个关键参数:
- 流状态(FS):0 = 继续发送(CTS),1 = 等待(WAIT),2 = 溢出(OVFLW)。
- 块大小(BS):接收方允许连续发送的最大帧数。发送方发完BS个连续帧后,必须等待下一个流控制帧。
- 最小间隔时间(STmin):连续帧之间的最小间隔时间,单位是毫秒或微秒。
为什么会需要流控制?说白了,ECU的处理能力有限。比如ECU正在执行一个高优先级的控制任务,来不及处理诊断数据,就可以发一个WAIT(FS=1)让诊断仪等一等。或者ECU的接收缓冲区快满了,就设置BS=5,让诊断仪发5帧就停一下。
// 流控制帧示例
// 诊断仪发送首帧后,ECU回复:
CAN ID: 0x7E8
Data: 30 0A 14 00 00 00 00 00
// 解析:0x30 = 流控制帧(0x3) + 流状态CTS(0x0)
// 0x0A = BS = 10(一次最多发10个连续帧)
// 0x14 = STmin = 20ms(帧间隔20毫秒)
避坑指南:我曾经遇到过一个ECU,它的STmin参数设置成了0x00,表示“无间隔”。结果诊断仪连续发送帧时,ECU的CAN控制器因为接收中断处理不过来,丢帧了。后来我们强制将STmin设为0x64(100ms),问题解决。所以,不要完全相信ECU的流控制参数,如果发现丢帧,可以适当增大STmin。
3.4 网络层定时参数:时间就是生命
网络层定义了多个定时参数,用来保证传输的可靠性。这些参数如果设置不当,轻则通信超时,重则导致ECU死锁。
| 参数 | 默认值 | 说明 |
|---|---|---|
| N_As | 50-100ms | 发送CAN帧的超时时间 |
| N_Ar | 50-100ms | 接收CAN帧的超时时间 |
| N_Bs | 1000ms | 等待流控制帧的超时时间 |
| N_Br | 1000ms | 接收流控制帧的超时时间 |
| N_Cs | 50-100ms | 发送连续帧的超时时间 |
| N_Cr | 50-100ms | 接收连续帧的超时时间 |
嗯,这里要注意:N_Bs和N_Br是1000ms,比其他参数大得多。为什么?因为流控制帧可能因为ECU正在处理其他任务而延迟发送。我见过一个极端案例,ECU在刷写过程中需要擦除Flash,耗时800ms,导致流控制帧延迟了900ms才发出。如果N_Bs设置成500ms,就会超时重传,反而加重了ECU的负担。
建议:在项目初期,先使用ISO 15765-2推荐的默认定时参数。如果发现通信不稳定,再根据实际硬件性能(CAN控制器时钟精度、CPU负载、中断响应时间)逐步调整。我个人习惯把N_Bs和N_Br设为1500ms,给ECU留点余量。
3.5 传输层错误处理:出了问题怎么办?
传输层错误处理是保证诊断通信可靠性的最后一道防线。常见的错误类型包括:
- 超时错误:发送方没收到预期的响应帧(流控制帧或连续帧)。
- 序列号错误:连续帧的序列号不连续或重复。
- 长度错误:实际接收的数据长度与首帧声明的总长度不符。
- 流状态错误:接收方发送了OVFLW(溢出)状态。
当检测到错误时,发送方或接收方应该怎么做?ISO 15765-2给出了明确的处理流程:
- 超时重传:发送方在超时后,可以重传当前帧(最多重传3次)。
- 错误通知:接收方发现错误后,可以发送一个错误帧(或者直接丢弃数据,等待发送方超时)。
- 会话重置:如果重传3次仍然失败,发送方应该终止当前传输,并通知应用层(诊断服务)通信失败。
我记得有一次调试一个Bootloader刷写失败的问题。每次刷写到80%左右就卡住了,然后超时重传,再卡住,最后刷写失败。抓了CAN报文后发现,连续帧的序列号在0x0F之后变成了0x00(循环),但ECU的序列号检查逻辑有bug,认为0x00是无效序列号,直接丢弃了后续所有帧。后来我们修改了ECU的序列号处理逻辑,问题解决。
小技巧:在开发阶段,可以在诊断仪端打印出每个网络层帧的详细信息(帧类型、序列号、数据长度、流控制参数)。这样一旦出现问题,可以快速定位是发送方的问题还是接收方的问题。我常用的工具是PCAN-View配合自定义的解析脚本。
3.6 知识结构图:网络层与传输层全景
下面这张图总结了本章的核心知识点,方便你快速回顾。
3.7 实战建议:如何配置网络层参数
最后,分享一些我在实际项目中的配置建议:
- CAN波特率:250kbps或500kbps。500kbps下,一个标准CAN帧(8字节数据)的传输时间约为0.2ms,所以STmin设为1ms就足够了。
- 接收缓冲区:至少256字节。如果支持多帧传输,建议512字节或更大。
- 超时时间:N_As/N_Ar设为100ms,N_Bs/N_Br设为1000ms。如果ECU的CPU负载较高,可以适当增大。
- 重传次数:3次。超过3次后,直接上报“通信超时”错误。
嗯,网络层和传输层的内容就讲到这里。这些参数看起来琐碎,但它们是诊断通信的基石。下次当你遇到“ECU无响应”或“刷写失败”的问题时,记得先检查网络层配置——很可能就是某个定时参数或者流控制参数没设对。