4、DoCAN网络层协议:单帧与多帧传输、流控制帧、连续帧、时间参数(N_As/N_Bs/N_Cr)

好,咱们进入第四讲。这一讲是UDS诊断的“通信基础课”——DoCAN网络层协议。说白了,就是诊断报文在CAN总线上到底怎么传的。

你可能会问:“不就是发个数据吗?CAN报文一次就能发8个字节,够用了吧?”

嗯,大部分诊断请求确实够。但你想过没有,一个ECU的软件版本信息、一段Bootloader的刷写数据,动辄几百上千字节。8个字节的CAN帧根本装不下。这时候,就需要网络层协议来“分包”和“重组”。

我个人习惯把这一层比作“快递分拣系统”。单帧就是小包裹,直接送;多帧就是大件货,得拆成好几箱,还要跟收件人确认“我能不能发?你准备好了没?”

核心概念:ISO 15765-2 定义了DoCAN网络层。它负责把上层的诊断消息(比如一条完整的UDS请求)分割成若干个CAN数据帧,并在接收端重新组装起来。

4.1 单帧(SF)与多帧(FF)传输

先看最简单的——单帧。

单帧(SingleFrame, SF)用于传输长度不超过7个字节的诊断数据。为什么是7个?因为CAN数据场总共8个字节,第一个字节被网络层协议控制信息(PCI)占用了,剩下7个给数据。

举个例子,一条“读取VIN码”的请求,通常只有2-3个字节。这种就直接用单帧发,干脆利落。

那数据超过7个字节怎么办?就得用多帧传输了。

多帧传输的第一步,是发送一个首帧(FirstFrame, FF)。首帧的PCI里包含了两个关键信息:

  • 消息总长度:整个诊断消息有多少个字节
  • 首帧数据:前6个字节的数据(因为首帧PCI占2个字节)

我遇到过不少刚入行的工程师,搞不清首帧和后续帧的区别。其实很简单:首帧是“报信”的,告诉接收方“我要发一个大文件,总共500字节,你先准备着”。后续帧才是真正传数据的。

4.2 流控制帧(FC)

接收方收到首帧后,不会傻等着。它会回复一个流控制帧(FlowControl, FC)。

流控制帧的作用是什么?说白了,就是“流量控制”。

FC帧里包含三个参数:

参数 含义 常见值
FS(流状态) 0=继续发送,1=等待,2=溢出/中止 0x00(继续)
BS(块大小) 每收到多少个连续帧后,需要再次等待FC 0x00(无限制)或 0x01~0xFF
STmin(最小间隔时间) 两个连续帧之间的最小时间间隔 0x00~0xF9(单位ms),0xFA~0xFE(单位100us)

这里有个坑,我踩过。有一次做Bootloader刷写,ECU回复的STmin是0x00,我以为就是“无延迟”,结果连续帧发太快,ECU处理不过来,直接丢帧了。

注意:STmin=0x00 并不代表“无限制”,而是代表“发送方必须等待接收方处理完上一帧”。实际项目中,很多ECU的CAN控制器有硬件FIFO,但软件处理跟不上。我建议你至少设成0x10(16ms)起步,除非你确认接收方性能足够。

4.3 连续帧(CF)

接收方发完FC后,发送方就开始发连续帧(ConsecutiveFrame, CF)。

连续帧的PCI只占1个字节,剩下7个字节全是数据。每个连续帧都有一个序列号(SN),从1开始递增,到15后回绕到0。

嗯,这里要注意:序列号只有4位(0-15)。如果连续帧超过15个,SN会循环。接收方就是靠这个SN来排序和检测丢帧的。

我曾经调试过一个项目,刷写过程中总是随机失败。抓了CAN报文一看,发现发送方在SN=14之后发了SN=0,但接收方还在等SN=15。结果就是“死等”,超时后报错。后来发现是发送方的SN计数器实现有bug,15之后应该回到1,而不是0。

小技巧:抓取多帧传输时,重点关注首帧的DLC(数据长度码)和连续帧的SN序列。如果SN出现跳跃或重复,基本可以断定是网络层实现有问题。

4.4 时间参数(N_As / N_Bs / N_Cr)

网络层协议最让人头疼的,就是这些时间参数。但说实话,理解了它们的含义,调试起来事半功倍。

ISO 15765-2 定义了三个关键时间参数:

参数 定义 典型值 超时后果
N_As 发送方从“准备发送”到“帧实际发出”的时间 ≤ 1000 ms(通常50-200ms) 发送超时,通信中止
N_Bs 接收方从收到首帧到发出FC的时间 ≤ 1000 ms(通常50-500ms) 发送方等待FC超时,中止传输
N_Cr 发送方从收到FC到发出下一个连续帧的时间 ≤ 1000 ms(通常50-200ms) 接收方等待连续帧超时,中止接收

为什么要有这些时间参数?说白了,就是为了防止“死锁”。

你想想看,如果发送方发了首帧,接收方一直不回复FC,发送方总不能无限等下去吧?N_Bs就是用来定义这个等待上限的。

我个人的经验是:

  • N_As 一般不用太操心,只要CAN总线负载不高,硬件发送基本是微秒级的。
  • N_Bs 是调试重点。如果ECU的FC回复慢,先检查它的应用层是不是在处理其他高优先级任务(比如电机控制)。
  • N_Cr 最容易出问题。发送方发完一个连续帧后,要等STmin时间再发下一个。但N_Cr是“从收到FC到发出下一个CF”的总时间,如果发送方处理不过来(比如在同时处理其他CAN报文),就可能超时。

避坑指南:我曾经在一个项目中,ECU的N_Bs设成了500ms,但诊断仪只等了200ms就超时了。两边参数不匹配,导致刷写成功率不到30%。后来统一把N_Bs设为1000ms,问题解决。所以,时间参数一定要上下游对齐,最好在系统需求文档里明确写死。

4.5 实际案例分析

讲个真实案例吧。

有一次,我在调试一款BMS(电池管理系统)的UDS刷写功能。刷写文件大约1MB,需要拆成2000多个连续帧。刚开始刷写总是卡在20%左右失败。

抓了CAN报文一看,发现:

  1. 首帧正常发送,ECU回复FC(BS=0,STmin=0x10)
  2. 发送方连续发了16个CF后,ECU突然不回复FC了
  3. 发送方等N_Bs超时,直接中止

为什么ECU不回复FC?因为BS=0代表“无块大小限制”,但ECU内部其实有一个接收缓冲区,满了之后就不再处理新的CF了。而发送方以为BS=0就是无限发,结果把ECU的缓冲区撑爆了。

解决方案:把BS设为5,即每发5个CF,就等一次FC确认。这样ECU有足够时间处理缓冲区里的数据。

经验总结:BS=0虽然理论上代表“无限制”,但实际项目中,我建议你根据ECU的RAM大小和CPU处理能力,设置一个合理的BS值(比如5-20)。别偷懒用0,除非你确认ECU的驱动层做了流控。

4.6 小结

好了,这一讲的内容就这些。总结一下:

  • 单帧:≤7字节,直接发
  • 多帧:首帧报信,流控帧协商,连续帧传数据
  • 时间参数:N_As、N_Bs、N_Cr,每个都是“保命符”,超时就中止

下一讲,我们会深入UDP的传输层,看看诊断数据在以太网上怎么跑。到时候你会发现,CAN和以太网的网络层逻辑其实很像,但细节上各有千秋。

嗯,今天就到这儿。有问题欢迎在评论区交流,或者直接找我。咱们下节课见。