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);
相关推荐
炸炸鱼.2 小时前
Python 网络编程入门(简易版)
网络·python
帮我吧智能服务平台2 小时前
工业4.0下,装备制造全生命周期服务数字化落地方案(附实操案例)
网络·人工智能·制造
Vis-Lin3 小时前
BLE 协议栈:L2CAP 信道详解
网络·物联网·网络协议·蓝牙·iot·ble
kiku18183 小时前
Python网络编程
开发语言·网络·python
新新学长搞科研3 小时前
【多所权威高校支持】第五届新能源系统与电力工程国际学术会议(NESP 2026)
运维·网络·人工智能·自动化·能源·信号处理·新能源
Deitymoon4 小时前
linux——网络基础
linux·网络
Smile_2542204184 小时前
DCS如何通过KepServerEx向InfluxDB写数据
网络·时序数据库
.select.4 小时前
TCP 5(socket编程)
服务器·网络·tcp/ip
风曦Kisaki4 小时前
Linux服务Day03:自定义YUM仓库、网络YUM仓库(HTTP/FTP)、MariaDB数据库基础操作
linux·网络·数据库