2. CANFD协议详解:CANFD帧结构、DLC、BRS与ESI
好,咱们进入正题。CANFD协议,说白了就是经典CAN的“升级版”。它保留了CAN的大部分优点,比如可靠性、实时性,同时把数据传输速度提了上去。我最早接触CANFD时,第一反应是:“这不就是CAN吗?” 但仔细一看,帧结构里多了几个新东西。
这一节,咱们就掰开揉碎,把CANFD的帧结构讲清楚。重点看四个核心点:帧结构(标准帧/扩展帧)、数据场长度编码(DLC)、位速率切换(BRS)和错误状态指示(ESI)。嗯,这几个是CANFD的灵魂。
2.1 CANFD帧结构:标准帧 vs 扩展帧
CANFD的帧结构,和经典CAN很像。但多了几个关键位。咱们先看整体结构。
一个完整的CANFD数据帧,包含这些部分:
- 帧起始(SOF):1个显性位,告诉总线“我要发数据了”。
- 仲裁场:包含ID和RTR位。标准帧ID是11位,扩展帧是29位。
- 控制场:这里变化最大。经典CAN是IDE、r0、DLC;CANFD变成了IDE、r0、BRS、ESI、DLC。多出来的BRS和ESI,咱们后面细说。
- 数据场:经典CAN最多8字节,CANFD最多64字节。这是质的飞跃。
- CRC场:CANFD的CRC算法和经典CAN不同,支持更多数据。
- ACK场:应答位,和经典CAN一样。
- EOF:帧结束。
标准帧和扩展帧的区别,主要在ID长度。标准帧ID是11位,扩展帧是29位。我个人习惯,如果节点不多,用标准帧就够了。但项目里如果涉及多个ECU,或者需要兼容不同厂商,扩展帧更灵活。
关键点:CANFD的帧结构,在控制场之后,数据场之前,多了一个“位速率切换”区域。这是CANFD能跑高速的秘密。
2.2 数据场长度编码(DLC)
DLC,全称Data Length Code。它告诉接收方:“我这帧数据有多长”。
经典CAN的DLC是4位,能表示0到8。但CANFD的DLC,虽然也是4位,但编码方式变了。它能表示0到64字节的数据长度。
具体编码规则如下:
| DLC值(二进制) | 经典CAN数据长度(字节) | CANFD数据长度(字节) |
|---|---|---|
| 0000 | 0 | 0 |
| 0001 | 1 | 1 |
| 0010 | 2 | 2 |
| 0011 | 3 | 3 |
| 0100 | 4 | 4 |
| 0101 | 5 | 5 |
| 0110 | 6 | 6 |
| 0111 | 7 | 7 |
| 1000 | 8 | 8 |
| 1001 | 8 | 12 |
| 1010 | 8 | 16 |
| 1011 | 8 | 20 |
| 1100 | 8 | 24 |
| 1101 | 8 | 32 |
| 1110 | 8 | 48 |
| 1111 | 8 | 64 |
你看,DLC从1001到1111,在经典CAN里都代表8字节。但在CANFD里,它们代表12、16、20……一直到64字节。为什么会这样?因为CANFD需要更大的数据场,但DLC只有4位,所以只能重新编码。
避坑指南:我曾经在项目里,用CANFD发64字节数据,但接收端配置成了经典CAN模式。结果接收端只读到8字节,后面的数据全丢了。所以,发送和接收的DLC编码必须一致。否则,数据会错乱。
2.3 位速率切换(BRS)
BRS,全称Bit Rate Switch。这是CANFD最核心的特性之一。
经典CAN的整个帧,都用同一个速率传输。比如500kbps。但CANFD不一样。它在仲裁场和控制场的前半部分,用“慢速”(比如500kbps)。到了数据场,它切换到“快速”(比如2Mbps甚至5Mbps)。
BRS位就控制这个切换。BRS=1,表示“我要切换速率了”。BRS=0,表示“我不切换,全程用慢速”。
你想想看,为什么这么设计?因为仲裁场需要所有节点参与总线仲裁,必须用统一的慢速。但数据场是点对点传输,不需要仲裁,所以可以跑高速。这样既保证了可靠性,又提升了效率。
关键点:BRS位只在CANFD帧里有效。经典CAN帧里,这个位是保留位(r0),必须为0。
我在实际项目中,常用配置是:仲裁场500kbps,数据场2Mbps。这样既能兼容老设备,又能享受高速传输。但要注意,总线上的所有节点,必须支持相同的快速速率。否则,数据场会出错。
2.4 错误状态指示(ESI)
ESI,全称Error State Indicator。它告诉接收方:“我这个发送节点,现在状态好不好”。
CAN节点有三种错误状态:
- 主动错误(Error Active):节点正常工作,能正常发送和接收。
- 被动错误(Error Passive):节点出过一些错误,但还能工作。发送时,会发送被动错误标志。
- 总线关闭(Bus Off):节点彻底罢工,不再参与通信。
ESI位的作用,就是让发送节点告诉接收方:“我现在是主动错误还是被动错误”。
- ESI=0:发送节点处于主动错误状态。这是正常状态。
- ESI=1:发送节点处于被动错误状态。接收方看到这个位,就知道“这个节点可能不太稳定”。
嗯,这里要注意。ESI位只在CANFD帧里有效。经典CAN帧里,这个位是保留位(r1),必须为0。
注意事项:ESI位是发送节点主动设置的。接收节点不能修改它。如果接收节点发现ESI=1,它可以选择忽略这帧数据,或者记录下来做诊断。我建议,在诊断协议里,把ESI位作为节点健康状态的一个指标。这样能提前发现潜在问题。
2.5 实战:如何配置CANFD帧
咱们用STM32的HAL库,配置一个CANFD帧。假设仲裁场速率500kbps,数据场速率2Mbps,数据长度64字节。
/* CANFD配置示例 */
CANFD_HandleTypeDef hcanfd;
hcanfd.Instance = CANFD1;
hcanfd.Init.Mode = CANFD_MODE_NORMAL;
hcanfd.Init.NominalPrescaler = 4; // 仲裁场:500kbps
hcanfd.Init.NominalSyncJumpWidth = 1;
hcanfd.Init.NominalTimeSeg1 = 13;
hcanfd.Init.NominalTimeSeg2 = 2;
hcanfd.Init.DataPrescaler = 1; // 数据场:2Mbps
hcanfd.Init.DataSyncJumpWidth = 1;
hcanfd.Init.DataTimeSeg1 = 13;
hcanfd.Init.DataTimeSeg2 = 2;
hcanfd.Init.StdId = 0x123; // 标准帧ID
hcanfd.Init.ExtId = 0; // 扩展帧ID
hcanfd.Init.IdType = CANFD_ID_STD; // 标准帧
hcanfd.Init.DLC = CANFD_DLC_64B; // 64字节
hcanfd.Init.BRS = CANFD_BRS_ENABLE; // 启用位速率切换
hcanfd.Init.ESI = CANFD_ESI_ACTIVE; // 主动错误状态
HAL_CANFD_Init(&hcanfd);
这段代码里,我配置了BRS为启用,ESI为主动错误。实际项目中,如果节点刚上电,ESI应该是主动错误。如果节点检测到多次发送失败,它会自动切换到被动错误,ESI位也会变成1。
个人经验:我调试CANFD时,经常用逻辑分析仪抓波形。看BRS位有没有正确切换,ESI位是不是预期的状态。有一次,我发现BRS位一直为0,查了半天,原来是配置里忘了使能BRS。所以,配置完一定要检查寄存器。
2.6 总结
这一节,咱们把CANFD帧结构的核心点讲透了。标准帧和扩展帧的区别,DLC的编码规则,BRS的速率切换,ESI的错误状态指示。这些是CANFD协议的基础,也是你写代码时必须掌握的。
下一节,咱们会讲CANFD的同步机制和采样点配置。嗯,那又是另一个坑。到时候我分享几个实战案例,保证让你少走弯路。