CAN FD扩展帧

一、CAN FD扩展帧是什么?

1.1 标准帧 vs 扩展帧

特性 标准帧 扩展帧
ID位数 11位 29位
ID范围 0x000 ~ 0x7FF 0x00000000 ~ 0x1FFFFFFF
节点容量 最多2048个 最多5.36亿
应用场景 小型网络 大型复杂系统

扩展帧的29位ID为大型网络提供了海量的地址空间,是CAN FD的强大特性之一。

1.2 CAN FD vs 传统CAN

复制代码
传统CAN 2.0:
┌──────┬──────────┬──────┬─────┐
│ ID   │ DLC(≤8)  │ Data │ CRC │
│ 11/29│  4bit    │ 0-8B │15bit│
└──────┴──────────┴──────┴─────┘

CAN FD:
┌──────┬──────────┬──────┬───────┬─────┐
│ ID   │ DLC(≤64) │ Data │ Stuff │ CRC │
│ 11/29│  4bit    │0-64B │ Count │17/21│
└──────┴──────────┴──────┴───────┴─────┘
         ↑
    可指示最多64字节数据!

核心优势

  • 数据段速率可达8Mbps(传统CAN仅1Mbps)

  • 单帧最大64字节(传统CAN仅8字节)

  • 硬件CRC校验,无需软件组帧


二、扩展帧ID的位域设计

2.1 为什么需要自定义ID结构?

CAN FD的29位ID不仅仅是"地址",更是协议的载体。通过位域划分,可以在ID中编码丰富的语义信息。

2.2 实际案例:机器人关节控制协议

以下是一个真实项目的ID编码方案:

复制代码
/* 29位扩展ID的位域分配 */
#define CAN_DEV_ID_NUM       (7u)    // 设备ID:7位,0-127
#define CAN_DEV_ID_SHIFT     (0u)

#define CAN_PRODUCT_ID_NUM   (7u)    // 产品ID:7位
#define CAN_PRODUCT_ID_SHIFT (7u)    // 注意:从bit7开始!

#define CAN_RW_NUM           (1u)    // 读写标志:1位
#define CAN_RW_SHIFT         (14u)   // 0=读,1=写

#define CAN_REG_NUM          (8u)    // 寄存器号:8位
#define CAN_REG_SHIFT        (15u)

#define CAN_REG_ADD_NUM      (5u)    // 子地址:5位
#define CAN_REG_ADD_SHIFT    (23u)

// 广播地址
#define CAN_BROADCAST_DEV_ID (0u)

ID布局可视化

复制代码
Bit: 28 ...... 24 | 23 ...... 15 | 14 | 13 ...... 7 | 6 ...... 0
     [子地址 5位]   [寄存器 8位]  [R/W] [产品ID 7位] [设备ID 7位]

三、关键认知:ID里存的是源地址!

3.1 CAN总线的寻址哲学

这是初学者最容易混淆的地方:

总线类型 寻址方式 ID含义
I2C/SPI 目标寻址 我要跟谁通信
CAN 源地址广播 我是谁

3.2 为什么这样设计?

CAN总线是广播式通信,所有节点同时监听总线。发送方报出自己身份,接收方根据"谁发的"来决定是否处理。

复制代码
// 发送时:填写自己的ID
uint32_t u32_id = pack_id(MY_PRODUCT_ID, MY_DEV_ID, rw, reg, reg_add);

// 接收时:判断发送方是谁
if(u8_dev_id == MASTER_ID || u8_dev_id == BROADCAST_ID) {
    // 来自主控或广播,处理
}

3.3 目标地址放哪里?

如果需要点对点通信,目标地址放在数据区

复制代码
// 发送给设备5
uint8_t au8_data[64];
au8_data[0] = 0x05;  // 目标设备ID
// ... 其他数据

四、硬件过滤器的正确配置

4.1 过滤的是什么?

过滤的是"发送方ID",不是"目标地址"!

复制代码
// 错误理解:过滤"发给我的帧"
// 正确理解:过滤"我关心的发送方发的帧"

// 示例:只接收来自主控(0x01)和广播(0x00)的帧
uint32_t u32_master_id = pack_id(PRODUCT_ID, 0x01, 0, 0, 0);
uint32_t u32_broadcast_id = pack_id(PRODUCT_ID, 0x00, 0, 0, 0);

st_filter.FilterID1 = u32_master_id;
st_filter.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;

4.2 多FDCAN外设的过滤器配置陷阱

问题现象:FDCAN1收不到数据,FDCAN2正常。

根本原因 :多个FDCAN外设共享同一个过滤器RAM,FilterIndex冲突导致覆盖。

复制代码
// ❌ 错误:两个外设使用相同的FilterIndex
// bsp_fdcan1_init()
st_filter.FilterIndex = 0;  // 被覆盖!
st_filter.FilterIndex = 1;  // 被覆盖!

// bsp_fdcan2_init()
st_filter.FilterIndex = 0;  // 覆盖了CAN1的配置
st_filter.FilterIndex = 1;  // 覆盖了CAN1的配置

✅ 正确方案1:错开FilterIndex

c

复制代码
// FDCAN1使用0~1
st_filter.FilterIndex = 0;
st_filter.FilterIndex = 1;

// FDCAN2使用2~3
st_filter.FilterIndex = 2;
st_filter.FilterIndex = 3;

✅ 正确方案2:使用全局过滤器(推荐)

复制代码
// FDCAN1:所有扩展帧进FIFO0
HAL_FDCAN_ConfigGlobalFilter(&hfdcan1,
    FDCAN_REJECT, FDCAN_REJECT,
    FDCAN_FILTER_TO_RXFIFO0,  // 扩展数据帧
    FDCAN_FILTER_TO_RXFIFO0); // 扩展远程帧

// FDCAN2:所有扩展帧进FIFO1
HAL_FDCAN_ConfigGlobalFilter(&hfdcan2,
    FDCAN_REJECT, FDCAN_REJECT,
    FDCAN_FILTER_TO_RXFIFO1,
    FDCAN_FILTER_TO_RXFIFO1);
相关推荐
长谷深风1113 分钟前
HTTP请求全过程解析【个人八股】
网络·网络协议·http·多线程下载·tcp 连接·请求报文、响应报文·网络请求流程
xhbh6663 分钟前
MC端口映射完全教程:路由器虚拟服务器配置+防火墙放行+内网穿透备用方案
运维·服务器·网络·网络协议·tcp/ip·智能路由器·流量端口转发
code monkey.4 分钟前
【Linux之旅】Linux 网络基础全解析:从协议分层到 Socket 编程,构建高性能网络服务的底层基石
linux·网络·php
艾莉丝努力练剑8 分钟前
【Linux网络】Linux 网络编程:HTTP(四)从手写服务器到生产级 Nginx 与 cpp-httplib 实战
linux·运维·服务器·网络·c++·nginx·http
Ether IC Verifier11 分钟前
TCP拥塞控制详解
网络·网络协议·tcp/ip·计算机网络·dpu
切糕师学AI11 分钟前
计算机网络层次结构详解:从OSI七层模型到TCP/IP四层模型
网络·tcp/ip·计算机网络
咖喱o15 分钟前
IPv6
服务器·前端·网络
IpdataCloud16 分钟前
IP查询工具怎么选?在线API vs IP离线库:精度、速度、成本、隐私全对比
服务器·网络·数据库
艾莉丝努力练剑22 分钟前
【Linux网络】Linux 网络编程:HTTP(三)HTTP 协议原理
linux·运维·服务器·网络·c++·http
Gauss松鼠会24 分钟前
GaussDB(DWS) 资源监控Topsql
java·网络·数据库·算法·oracle·性能优化·gaussdb