4. CANFD应用层协议:UDS on CANFD概述

好,咱们进入第四章。这一章要聊的,是CANFD协议栈里最贴近「人话」的一层——应用层协议。说白了,就是ECU之间怎么用CANFD来「对话」。

你可能会问:CANFD不是已经把物理层和数据链路层都定义好了吗?为什么还要搞个应用层?嗯,这个问题我当年刚入行时也困惑过。打个比方:CANFD就像一条高速公路,车能跑、路够宽,但车上运的货物怎么打包、怎么签收、怎么确认,这些都得有人来定规矩。UDS on CANFD,就是这套规矩。

4.1 什么是UDS on CANFD

UDS,全称是Unified Diagnostic Services,统一诊断服务。它最早是为CAN总线设计的,后来随着CANFD出现,自然就扩展到了CANFD上。

我个人习惯把UDS理解成「ECU的体检协议」。你想啊,一辆车上有几十个ECU,每个ECU都有自己的状态。工程师要读取故障码、刷写固件、配置参数,总得有个标准化的方式吧?UDS就是干这个的。

UDS on CANFD和传统UDS on CAN最大的区别在于:

  • 数据长度更灵活:CANFD单帧最多能带64字节数据,而传统CAN只有8字节
  • 传输效率更高:同样的诊断数据,CANFD可能一帧就搞定了,传统CAN得拆成好几帧
  • 流控机制更简洁:因为单帧容量大了,多帧传输的场景少了很多

核心要点:UDS on CANFD并不是重新发明了一套协议,而是在ISO 15765-2的基础上,利用CANFD的大数据场特性做了优化。你如果已经熟悉UDS on CAN,上手CANFD版本会非常快。

4.2 ISO 15765-2协议栈实现

ISO 15765-2,圈内人常叫它「ISO TP」或者「DoCAN」。它定义了UDS数据怎么在CAN/CANFD总线上传输。我当年啃这个标准时,啃得头皮发麻——标准文档写得那叫一个晦涩。

但说白了,ISO 15765-2就干三件事:

  1. 数据分段:把超过单帧容量的UDS消息拆成多个CANFD帧
  2. 流控制:协调发送方和接收方的节奏,防止数据溢出
  3. 重组:接收方把收到的多个帧拼回完整的UDS消息

咱们来看一个典型的协议栈分层结构:

+----------------------------+
|      UDS 应用层 (ISO 14229)  |
+----------------------------+
|    ISO 15765-2 (DoCAN)     |
+----------------------------+
|    CANFD 数据链路层         |
+----------------------------+
|    CANFD 物理层             |
+----------------------------+

这里有个关键点:ISO 15765-2是夹在UDS应用层和CANFD驱动层之间的「翻译官」。它负责把UDS的请求/响应消息,翻译成CANFD总线能理解的一帧帧数据。

4.3 多帧传输与流控制

好,接下来是本章的重头戏——多帧传输。虽然CANFD单帧能带64字节,但UDS消息有时候会更大。比如刷写固件时,一个数据块可能几百KB甚至几MB。这时候就得用多帧传输了。

ISO 15765-2定义了三种帧类型:

帧类型 缩写 PCI字节含义 说明
单帧 SF 第1字节高4位=0,低4位=数据长度 数据能塞进一帧时使用
首帧 FF 第1字节高4位=1,低4位+第2字节=总长度 多帧传输的第一帧
连续帧 CF 第1字节高4位=2,低4位=序列号 首帧之后的后续数据帧
流控帧 FC 第1字节高4位=3,后续字节含流控参数 接收方控制发送节奏

多帧传输的流程,我习惯用「一问一答」来理解:

  1. 发送方先发一个首帧(FF):告诉接收方「我要发这么多数据,你准备好」
  2. 接收方回复流控帧(FC):告诉发送方「我准备好了,你可以发了。每次最多发N帧,每帧间隔M毫秒」
  3. 发送方发连续帧(CF):按照流控帧的要求,一帧一帧地发
  4. 如果接收方忙不过来:可以发流控帧要求暂停(Wait)或者调整参数

避坑指南:我曾经在一个项目中,接收方的接收缓冲区设得太小,结果连续帧一多就丢数据。排查了两天才发现是流控帧里的BlockSize(块大小)参数没配好。记住:BlockSize不是越大越好,要根据接收方的缓冲区大小来定。

咱们来看一个具体的多帧传输例子。假设要发送一个200字节的UDS消息:

发送方 → 接收方: 首帧 (FF)
  PCI: 0x10 0xC8  (总长度=200字节)
  数据: 前62字节的UDS数据

接收方 → 发送方: 流控帧 (FC)
  PCI: 0x30 0x05 0x14
  (BS=5, STmin=20ms)
  含义: 每次最多发5帧,帧间隔20ms

发送方 → 接收方: 连续帧1 (CF)
  PCI: 0x21  (序列号=1)
  数据: 接下来64字节

发送方 → 接收方: 连续帧2 (CF)
  PCI: 0x22  (序列号=2)
  数据: 接下来64字节

... 直到发完所有数据

这里有个细节:连续帧的序列号是从1开始,到15后回绕到0。为什么是15?因为PCI里只给了4位来存序列号。嗯,这就是标准设计时的权衡——为了节省协议开销。

4.4 流控制参数详解

流控帧里三个参数,每个都很关键:

  • FS (Flow Status):流控状态。0=继续发送(CTS),1=等待(WAIT),2=溢出(OVFLW)
  • BS (Block Size):块大小。接收方允许发送方连续发送的最大帧数。0表示不限制
  • STmin (Separation Time minimum):最小帧间隔时间。单位是毫秒或微秒,取决于编码

特别注意:STmin的编码方式有坑。当STmin值在0x00-0x7F之间时,单位是毫秒。但0xF1-0xF9表示微秒级间隔(100μs-900μs)。我见过有工程师直接把STmin设成0x01,以为是最小间隔,结果接收方根本处理不过来。实际上0x01表示1ms,对于高速CANFD来说可能太长了。

我个人习惯的流控参数配置策略:

  • BS设成0:不限制块大小,让发送方一口气发完。前提是接收方缓冲区够大
  • STmin设成0x00:表示不限制间隔,发送方可以连续发。但要注意,有些CANFD控制器在连续发送时会有内部延迟
  • 如果接收方资源紧张:BS设成5-10,STmin设成1-2ms,这样比较稳妥

4.5 实际开发中的注意事项

最后,分享几个我在项目中踩过的坑:

  1. CANFD的BRS位:多帧传输时,首帧和连续帧的BRS位要一致。我见过一个案例,首帧用了CANFD快速模式(BRS=1),连续帧却用了普通模式(BRS=0),结果接收方直接丢帧
  2. 时间同步:流控帧里的STmin是接收方要求的,但发送方实际能不能做到,得看你的RTOS调度。我曾经在一个高负载系统上,STmin设了1ms,但发送任务被更高优先级的任务抢占了,实际间隔变成了3-5ms
  3. 错误处理:如果连续帧序号不连续,或者超时没收到流控帧,协议栈应该能自动重发或者报错。别问我怎么知道的——有一次OTA升级到一半,就因为流控帧丢了,整个刷写流程卡死了

好了,这一章的内容就到这儿。下一章我们会深入UDS on CANFD的具体服务实现,包括诊断会话控制、安全访问、数据读写这些实战内容。到时候咱们再细聊。