CANopen 过滤器配置
目录
- [CANopen 预定义连接集 COB-ID 分配表](#CANopen 预定义连接集 COB-ID 分配表)
- 从站过滤器配置
- 主站过滤器配置
- [STM32 bxCAN 代码 (F4)](#STM32 bxCAN 代码 (F4))
- [STM32 FDCAN 代码 (H7/G4)](#STM32 FDCAN 代码 (H7/G4))
- 多从站系统滤波器银行优化
- 调试与验证
1. CANopen 预定义连接集 COB-ID 分配表
CANopen 使用 Predefined Connection Set (CiA 301),每条 CAN 消息的 COB-ID 由 功能码 (Function Code, bit10:7) + Node-ID (bit6:0) 组成。
1.1 广播对象 (Broadcast) --- 无 Node-ID
| COB-ID (hex) | 对象 | 方向 | 说明 |
|---|---|---|---|
0x000 |
NMT | 主→从 | 网络管理命令 (Start/Stop/Reset/Pre-Op) |
0x080 |
SYNC | 主→从 | 同步帧,触发同步 PDO 发送 |
0x100 |
TIME | 主→从 | 时间戳 (可选) |
0x101--0x17F |
保留 | --- | CiA 保留,不可用 |
1.2 点对点对象 (Peer-to-Peer) --- 含 Node-ID
COB-ID = 功能码基址 + Node-ID (N = 1--127)。
| COB-ID 范围 | 基址 | 对象 | 方向 | 说明 |
|---|---|---|---|---|
0x081--0x0FF |
0x080 + N |
EMCY | 从→主 | 紧急报文 |
0x181--0x1FF |
0x180 + N |
TPDO1 | 从→主 | 发送 PDO 1 |
0x201--0x27F |
0x200 + N |
RPDO1 | 主→从 | 接收 PDO 1 |
0x281--0x2FF |
0x280 + N |
TPDO2 | 从→主 | 发送 PDO 2 |
0x301--0x37F |
0x300 + N |
RPDO2 | 主→从 | 接收 PDO 2 |
0x381--0x3FF |
0x380 + N |
TPDO3 | 从→主 | 发送 PDO 3 |
0x401--0x47F |
0x400 + N |
RPDO3 | 主→从 | 接收 PDO 3 |
0x481--0x4FF |
0x480 + N |
TPDO4 | 从→主 | 发送 PDO 4 |
0x501--0x57F |
0x500 + N |
RPDO4 | 主→从 | 接收 PDO 4 |
0x581--0x5FF |
0x580 + N |
SDOtx (Server→Client) | 从→主 | SDO 应答/上传 |
0x601--0x67F |
0x600 + N |
SDOrx (Client→Server) | 主→从 | SDO 请求/下载 |
0x701--0x77F |
0x700 + N |
NMT Error Control (alt) | 从→主 | 心跳/guard等 0x700+N |
SS 服务** 使用
0x7E4(Master→Slave) 和0x7E5(Slave→Master),仅在配置阶段使用,可在运行后关闭。
1.3 COB-ID 解析规则
11-bit CAN ID: [10] [9] [8] [7] [6] [5] [4] [3] [2] [1] [0]
◄────── 功能码 ──────► ◄────── Node-ID ──────────►
示例: 从站 Node-ID = 5 (0x05)
┌───────────────┬──────────────┬──────────────┬──────────────────┐
│ 报文 │ 功能码 (bit10-7)│ Node-ID │ COB-ID (hex) │
├───────────────┼──────────────┼──────────────┼──────────────────┤
│ EMCY │ 0x1 (0001) │ 0x05 │ 0x085 │
│ TPDO1 │ 0x3 (0011) │ 0x05 │ 0x185 │
│ RPDO1 │ 0x4 (0100) │ 0x05 │ 0x205 │
│ SDOtx (server)│ 0xB (1011) │ 0x05 │ 0x585 │
│ SDOrx (client)│ 0xC (1100) │ 0x05 │ 0x605 │
│ Heartbeat │ 0xE (1110) │ 0x05 │ 0x705 │
└───────────────┴──────────────┴──────────────┴──────────────────┘
2. 从站过滤器配置
2.1 从站需要接收的帧
从站 (Slave) 只需要关心发给自己的帧 和全局广播帧:
| 接收的帧 | COB-ID | 类型 | 必须? |
|---|---|---|---|
| NMT | 0x000 |
广播 | 必须 |
| SYNC | 0x080 |
广播 | 同步 PDO 时必须 |
| TIME | 0x100 |
广播 | 可选 |
| SDOrx | 0x600 + N |
单播 | 必须 (配置/参数化) |
| RPDO1 | 0x200 + N |
单播 | 使用 PDO 时必须 |
| RPDO2 | 0x300 + N |
单播 | 使用 PDO 时必须 |
| RPDO3 | 0x400 + N |
单播 | 使用 PDO 时必须 |
| RPDO4 | 0x500 + N |
单播 | 使用 PDO 时必须 |
2.2 从站过滤策略
从站 Node-ID = N 时,需要通过的 CAN ID:
┌─────────────────────────────────────────────────────────┐
│ NMT 0x000 ← 广播 (全节点接收) │
│ SYNC 0x080 ← 广播 (全节点接收) │
│ SDOrx 0x600 + N ← 单播, Node-ID = N │
│ RPDO1 0x200 + N ← 单播, Node-ID = N │
│ RPDO2 0x300 + N ← 单播 (如使能) │
│ RPDO3 0x400 + N ← 单播 (如使能) │
│ RPDO4 0x500 + N ← 单播 (如使能) │
│ LSS 0x7E4 ← 广播 (仅配置阶段) │
└─────────────────────────────────────────────────────────┘
2.3 从站滤波器需求汇总
| 从站 Node-ID | 最少需要过滤的 CAN ID | bxCAN Bank 数 | FDCAN Filter 数 |
|---|---|---|---|
| 1 | 0x000, 0x080, 0x181-范围, 0x601 |
3--4 | 4 |
从最少配置 来看,使用 0x000 和 0x080 单独匹配 + 一条 Mask 匹配 0x600+N(精确匹配被寻址的 SDO),PDO 再按需添加。
3. 主站过滤器配置
3.1 主站需要接收的帧
主站 (Master, 通常 Node-ID = 1 或 127) 需要接收所有从站发来的帧:
| 接收的帧 | COB-ID | 方向 | 说明 |
|---|---|---|---|
| EMCY x N | 0x080 + NodeID |
从→主 | 每个从站的紧急报文 |
| SDOtx x N | 0x580 + NodeID |
从→主 | 每个从站的 SDO 应答 |
| Heartbeat x N | 0x700 + NodeID |
从→主 | 每个从站的心跳 |
| TPDO1 x N | 0x180 + NodeID |
从→主 | 每个从站的 TPDO1 |
| TPDO2 x N | 0x280 + NodeID |
从→主 | 每个从站的 TPDO2 |
| TPDO3 x N | 0x380 + NodeID |
从→主 | 每个从站的 TPDO3 |
| TPDO4 x N | 0x480 + NodeID |
从→主 | 每个从站的 TPDO4 |
主站不需要 接收 NMT (
0x000),因为 NMT 由主站自己发出。
3.2 主站过滤策略
主站管理 Node-ID = 1--12 的 12 个从站时,需要通过的 CAN ID:
┌─────────────────────────────────────────────────────────┐
│ EMCY 0x081-0x08C ← 12 个从站紧急报文 │
│ SDOtx 0x581-0x58C ← 12 个从站 SDO 应答 │
│ Heartbeat 0x701-0x70C ← 12 个从站心跳 │
│ TPDO1 0x181-0x18C ← 12 个从站 TPDO1 │
│ TPDO2 0x281-0x28C ← 12 个从站 TPDO2 │
│ TPDO3 0x381-0x38C ← 12 个从站 TPDO3 │
│ TPDO4 0x481-0x48C ← 12 个从站 TPDO4 │
│ LSS 0x7E5 ← 单播 (仅配置阶段) │
└─────────────────────────────────────────────────────────┘
3.3 主站滤波器需求汇总
| 从站数量 | 全开 (所有 PDO) 需要过滤的 CAN ID | bxCAN Bank 数 | 最优方案 |
|---|---|---|---|
| 1 | 7 | 4 | 精确匹配每类 |
| 4 | 28 | ≥14 (互联型满) | 按范围 Mask 批量匹配 |
| 8 | 56 | 不够 | Mask 按功能码段过滤 |
| 12+ | 84+ | 不够 | 全通 + 软件过滤 或分段 Mask |
| 127 | 889 | 不够 | 全通,软件 dispatch |
主站滤波器银行是关键瓶颈 。STM32F4 互联型只有 28 Bank,STM32F1/F3 只有 14 Bank。超过 Bank 数量时必须使用范围 Mask 批量过滤 或全通 + 软件过滤。
4. STM32 bxCAN 代码 (F4)
4.1 从站滤波器示例 --- F407, Node-ID = 5
需求:接收 NMT + SYNC + SDO(0x605) + RPDO1(0x205) + Heartbeat 响应用(0x705, 可选)。
c
/**
* CANopen Slave Filter Configuration --- STM32F407 bxCAN
* Node-ID = 5, 使用 4 个 Filter Bank
*
* 需要接收:
* NMT 0x000 (标准帧 11-bit)
* SYNC 0x080 (标准帧 11-bit)
* SDOrx 0x605 (标准帧 11-bit)
* RPDO1 0x205 (标准帧 11-bit)
*/
#include "stm32f4xx_hal.h"
CAN_HandleTypeDef hcan1;
void canopen_slave_filter_init(uint8_t node_id)
{
CAN_FilterTypeDef filter = {0};
uint32_t sdorx_id = 0x600 + node_id; /* 0x605 */
uint32_t rpdo1_id = 0x200 + node_id; /* 0x205 */
uint32_t rpdo2_id = 0x300 + node_id; /* 按需 */
uint32_t rpdo3_id = 0x400 + node_id; /* 按需 */
uint32_t rpdo4_id = 0x500 + node_id; /* 按需 */
/*
* Bank 分配方案: 标准帧全部用 32-bit 模式,每个 Bank 可配 1 组 ID+Mask
*
* Bank 0: NMT 0x000 + SYNC 0x080 → 用 List 模式放两个 ID
* - 标准帧 16-bit List 模式: 一个 Bank 存 4 个 ID
* - 使用 16-bit List, Bank 0 存入 {0x000, 0x080, 0x605, 0x205}
*/
/* --- Bank 0: 16-bit ID List → 4 个精确 ID --- */
filter.FilterBank = 0;
filter.FilterMode = CAN_FILTERMODE_IDLIST;
filter.FilterScale = CAN_FILTERSCALE_16BIT;
filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;
filter.FilterActivation = ENABLE;
/*
* 16-bit 模式寄存器布局 (每个 Bank 4 个 16-bit ID):
* FilterIdHigh = ID0[15:0] << 5 ← 标准帧 11-bit, 在 bit[15:5]
* FilterIdLow = ID1[15:0] << 5
* FilterMaskIdHigh = ID2[15:0] << 5
* FilterMaskIdLow = ID3[15:0] << 5
*/
filter.FilterIdHigh = (0x000 << 5); /* NMT */
filter.FilterIdLow = (0x080 << 5); /* SYNC */
filter.FilterMaskIdHigh = (sdorx_id << 5);/* SDO rx */
filter.FilterMaskIdLow = (rpdo1_id << 5);/* RPDO1 */
HAL_CAN_ConfigFilter(&hcan1, &filter);
/* --- Bank 1: 按需 --- RPDO2 + RPDO3 + RPDO4 (ID List) --- */
filter.FilterBank = 1;
filter.FilterIdHigh = (rpdo2_id << 5);
filter.FilterIdLow = (rpdo3_id << 5);
filter.FilterMaskIdHigh = (rpdo4_id << 5);
filter.FilterMaskIdLow = 0; /* 未使用 */
HAL_CAN_ConfigFilter(&hcan1, &filter);
/*
* 以上配置总共占用 2 个 Filter Bank, 实现从站所有必要帧的接收。
*
* 如果还需要接收 Emergency Echo (0x080 + other_node_id), 外加 Bank 2。
*/
}
4.2 主站滤波器示例 --- F407, 管理 4 个从站 (Node-ID = 1,2,3,4)
需求:接收每个从站的 EMCY + SDOtx + Heartbeat + TPDO1。
4 从站 × 4 类 = 16 个不同的 CAN ID,bxCAN 无法逐 ID 匹配 (Bank 不够)。
使用 Mask 掩码 按功能码范围批量匹配。
c
/**
* CANopen Master Filter Configuration --- STM32F407 bxCAN
* 管理 4 个从站: Node-ID = 1, 2, 3, 4
*
* Mask 策略: 按功能码段匹配, 忽略低 2 位 Node-ID:
* bit[6:0] = Node-ID, 4 个从站占 bit[2:1] = {00,01,10,11}
*
* 用 Mask = ~(bit[2:1]) 放宽 Node-ID, 每个功能码只需 1 个 Bank
*/
#include "stm32f4xx_hal.h"
CAN_HandleTypeDef hcan1;
void canopen_master_filter_init(void)
{
CAN_FilterTypeDef filter = {0};
uint8_t bank = 0;
/*
* 从站 Node-ID 范围: 1--4 (二进制 001--100)
* 低 2 位覆盖 4 个 ID, 使用 Mask 忽略低 2 位:
*
* 参考 ID = 功能码基址 + 1 (最小 Node-ID)
* Mask = ~0x03 (bit[1:0] 不理, 通过 4 个 Node-ID)
*/
uint32_t node_mask = ~0x03U; /* bit[1:0] = don't care, 覆盖 Node-ID 1--4 */
/* --- Bank 0: EMCY (0x080+N) N=1--4 --- */
filter.FilterIdHigh = ((0x080 + 1) << 5); /* 0x081 → bit[15:5] */
filter.FilterIdLow = 0;
filter.FilterMaskIdHigh = (node_mask << 5); /* bit[1:0] = don't care */
filter.FilterMaskIdLow = 0;
filter.FilterBank = bank++;
filter.FilterMode = CAN_FILTERMODE_IDMASK;
filter.FilterScale = CAN_FILTERSCALE_32BIT;
filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;
filter.FilterActivation = ENABLE;
HAL_CAN_ConfigFilter(&hcan1, &filter);
/* --- Bank 1: SDOtx (0x580+N) N=1--4 --- */
filter.FilterIdHigh = ((0x580 + 1) << 5);
filter.FilterMaskIdHigh = (node_mask << 5);
filter.FilterBank = bank++;
HAL_CAN_ConfigFilter(&hcan1, &filter);
/* --- Bank 2: Heartbeat (0x700+N) N=1--4 --- */
filter.FilterIdHigh = ((0x700 + 1) << 5);
filter.FilterMaskIdHigh = (node_mask << 5);
filter.FilterBank = bank++;
HAL_CAN_ConfigFilter(&hcan1, &filter);
/* --- Bank 3: TPDO1 (0x180+N) N=1--4 --- */
filter.FilterIdHigh = ((0x180 + 1) << 5);
filter.FilterMaskIdHigh = (node_mask << 5);
filter.FilterBank = bank++;
HAL_CAN_ConfigFilter(&hcan1, &filter);
/*
* 只用 4 个 Bank 覆盖了 16 条 CAN ID!
*
* 如果还需要 TPDO2/TPDO3/TPDO4, 照同样模式添加 Bank 4--6:
* TPDO2 = 0x280+N, TPDO3 = 0x380+N, TPDO4 = 0x480+N
* 总计 7 Bank, 远少于 28 Bank 上限。
*/
}
4.3 主站 --- 批量模式 (管理 12 从站)
当从站数量增加到 12 (Node-ID 1--12),node_mask = ~0x0F 覆盖 4 位,但会额外通过 Node-ID 0 和 13-15。如果不需要严格隔离外站 ID(一般 CANopen 总线节点有序分配),可用此方案。
c
void canopen_master_filter_12nodes(void)
{
CAN_FilterTypeDef filter = {0};
uint32_t node_mask = ~0x0FU; /* 忽略低 4 位, 通过 ID 0--15 */
uint32_t base_id = 1U; /* 最小的 Node-ID */
uint8_t bank = 0;
/*
* 功能码 + Node-ID 范围掩码表
* 每个 Bank 覆盖一个对象类型的 ID 范围
*/
struct {
uint16_t func_base; /* 功能码基址 (不含 Node-ID) */
const char *name;
} func_list[] = {
{0x080, "EMCY"}, /* 0x081-0x08F (含 Node-ID=0) */
{0x180, "TPDO1"}, /* 0x181-0x18F */
{0x280, "TPDO2"}, /* 0x281-0x28F */
{0x380, "TPDO3"}, /* 0x381-0x38F */
{0x480, "TPDO4"}, /* 0x481-0x48F */
{0x580, "SDOtx"}, /* 0x581-0x58F */
{0x700, "Heartbeat"}, /* 0x701-0x70F */
};
uint8_t func_count = sizeof(func_list) / sizeof(func_list[0]);
for (uint8_t i = 0; i < func_count; i++) {
filter.FilterBank = bank++;
filter.FilterMode = CAN_FILTERMODE_IDMASK;
filter.FilterScale = CAN_FILTERSCALE_32BIT;
filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;
filter.FilterActivation = ENABLE;
/* 参考 ID = 功能码基址 + 最小 Node-ID */
filter.FilterIdHigh = ((func_list[i].func_base + base_id) << 5);
filter.FilterIdLow = 0;
filter.FilterMaskIdHigh = (node_mask << 5);
filter.FilterMaskIdLow = 0;
HAL_CAN_ConfigFilter(&hcan1, &filter);
}
/* 7 个 Bank 覆盖 7×16 = 112 条 COB-ID, 包含 12 个从站的所有通信对象 */
}
5. STM32 FDCAN 代码 (H7/G4)
FDCAN 的滤波器系统比 bxCAN 更灵活:
- 128 个共享元素(标准帧 + 扩展帧总和)
- 5 种匹配类型:Range, Dual-ID, Classic Mask, Mask (扩展位宽), Range no EIDM
- 每个滤波器元素可单独配置目标:FIFO0 / FIFO1 / Rx Buffer / Reject
5.1 从站 --- H743, Node-ID = 5
c
/**
* CANopen Slave Filter --- STM32H7 FDCAN
* Node-ID = 5, 所有必要帧精确匹配
*/
#include "stm32h7xx_hal.h"
FDCAN_HandleTypeDef hfdcan1;
void fdcan_slave_filter_init(uint8_t node_id)
{
FDCAN_FilterTypeDef filter = {0};
uint32_t sdorx_id = 0x600 + node_id;
uint32_t rpdo1_id = 0x200 + node_id;
filter.IdType = FDCAN_STANDARD_ID;
filter.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
/* --- Filter 0: NMT (0x000) + SYNC (0x080) --- Dual-ID 匹配 --- */
filter.FilterIndex = 0;
filter.FilterType = FDCAN_FILTER_DUAL; /* 匹配 ID1 或 ID2 */
filter.FilterID1 = 0x000;
filter.FilterID2 = 0x080;
HAL_FDCAN_ConfigFilter(&hfdcan1, &filter);
/* --- Filter 1: SDOrx + RPDO1 --- Dual-ID 匹配 --- */
filter.FilterIndex = 1;
filter.FilterType = FDCAN_FILTER_DUAL;
filter.FilterID1 = sdorx_id;
filter.FilterID2 = rpdo1_id;
HAL_FDCAN_ConfigFilter(&hfdcan1, &filter);
/* --- Filter 2: RPDO2 + RPDO3 --- Dual-ID --- */
filter.FilterIndex = 2;
filter.FilterID1 = 0x300 + node_id;
filter.FilterID2 = 0x400 + node_id;
HAL_FDCAN_ConfigFilter(&hfdcan1, &filter);
/* --- Filter 3: RPDO4 精确匹配 --- */
filter.FilterIndex = 3;
filter.FilterID1 = 0x500 + node_id;
filter.FilterID2 = 0x500 + node_id; /* 第二个 ID 不关心, 重复值占位 */
HAL_FDCAN_ConfigFilter(&hfdcan1, &filter);
/* --- Filter 4: LSS (0x7E4) --- 仅配置阶段需要 --- */
filter.FilterIndex = 4;
filter.FilterID1 = 0x7E4;
filter.FilterID2 = 0x7E4;
HAL_FDCAN_ConfigFilter(&hfdcan1, &filter);
/* 5 个滤波器元素, 占 128 中的 5%, 精确匹配所有需要的 ID */
}
5.2 主站 --- H743, 管理 12 从站 (Node-ID 1--12)
FDCAN 的 Range 滤波器 非常适合主站场景------一条 Range 覆盖一个功能码的连续 Node-ID 范围。
c
/**
* CANopen Master Filter --- STM32H7 FDCAN
* 管理 12 个从站 Node-ID 1--12
*
* 使用 Range 滤波器, 每个功能码 1 个 Filter 元素即可覆盖 12 个 ID
*/
#include "stm32h7xx_hal.h"
FDCAN_HandleTypeDef hfdcan1;
void fdcan_master_filter_init(uint8_t first_node, uint8_t last_node)
{
FDCAN_FilterTypeDef filter = {0};
uint8_t idx = 0;
filter.IdType = FDCAN_STANDARD_ID;
filter.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
filter.FilterType = FDCAN_FILTER_RANGE;
/*
* Range 滤波器: FilterID1 ≤ 接收到的 ID ≤ FilterID2
*/
/* EMCY: 0x080 + [1..12] → range [0x081, 0x08C] */
filter.FilterIndex = idx++;
filter.FilterID1 = 0x080 + first_node;
filter.FilterID2 = 0x080 + last_node;
HAL_FDCAN_ConfigFilter(&hfdcan1, &filter);
/* TPDO1: 0x180 + [1..12] */
filter.FilterIndex = idx++;
filter.FilterID1 = 0x180 + first_node;
filter.FilterID2 = 0x180 + last_node;
HAL_FDCAN_ConfigFilter(&hfdcan1, &filter);
/* TPDO2: 0x280 + [1..12] */
filter.FilterIndex = idx++;
filter.FilterID1 = 0x280 + first_node;
filter.FilterID2 = 0x280 + last_node;
HAL_FDCAN_ConfigFilter(&hfdcan1, &filter);
/* TPDO3: 0x380 + [1..12] */
filter.FilterIndex = idx++;
filter.FilterID1 = 0x380 + first_node;
filter.FilterID2 = 0x380 + last_node;
HAL_FDCAN_ConfigFilter(&hfdcan1, &filter);
/* TPDO4: 0x480 + [1..12] */
filter.FilterIndex = idx++;
filter.FilterID1 = 0x480 + first_node;
filter.FilterID2 = 0x480 + last_node;
HAL_FDCAN_ConfigFilter(&hfdcan1, &filter);
/* SDOtx: 0x580 + [1..12] */
filter.FilterIndex = idx++;
filter.FilterID1 = 0x580 + first_node;
filter.FilterID2 = 0x580 + last_node;
HAL_FDCAN_ConfigFilter(&hfdcan1, &filter);
/* Heartbeat: 0x700 + [1..12] */
filter.FilterIndex = idx++;
filter.FilterID1 = 0x700 + first_node;
filter.FilterID2 = 0x700 + last_node;
HAL_FDCAN_ConfigFilter(&hfdcan1, &filter);
/* LSS 响应: 0x7E5 (单 ID) */
filter.FilterIndex = idx++;
filter.FilterType = FDCAN_FILTER_CLASSIC; /* 精确匹配 */
filter.FilterID1 = 0x7E5;
filter.FilterID2 = 0x7E5;
HAL_FDCAN_ConfigFilter(&hfdcan1, &filter);
/*
* 8 个滤波器元素, 覆盖 7×12 = 84 条 COB-ID + 1 条 LSS。
* FDCAN 共 128 元素, 剩余 120 元素可用于扩展、额外从站等。
*/
}
5.3 FDCAN 全局全通 + 拒绝特定 ID(混合策略)
c
/**
* 混合策略: 全通 (接收一切) + 拒绝不需要的 ID
*
* 适用场景: 主站需要接收大多数 ID, 但希望硬件过滤掉部分已知"噪声" ID。
*/
void fdcan_master_mixed_filter(void)
{
FDCAN_FilterTypeDef filter = {0};
uint8_t idx = 0;
/* ---- Reject Filter: 拒绝 Sync Echo (0x080 → 不接收其他主站的 SYNC) ---- */
filter.IdType = FDCAN_STANDARD_ID;
filter.FilterIndex = idx++;
filter.FilterType = FDCAN_FILTER_CLASSIC;
filter.FilterConfig = FDCAN_FILTER_REJECT; /* 丢弃 */
filter.FilterID1 = 0x080;
filter.FilterID2 = 0x080;
HAL_FDCAN_ConfigFilter(&hfdcan1, &filter);
/* ---- Global Filter: 接收其余所有标准帧 ------ 至 FIFO0 ---- */
filter.FilterIndex = idx++;
filter.FilterType = FDCAN_FILTER_RANGE;
filter.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
filter.FilterID1 = 0x000;
filter.FilterID2 = 0x7FF; /* 全范围 */
HAL_FDCAN_ConfigFilter(&hfdcan1, &filter);
/*
* 结果: 除 0x080 外, 所有标准帧都会进入 FIFO0。
*
* 注意: FDCAN 滤波器按 Index 优先级匹配, 先匹配到的 Filter 先生效。
* 所以 Reject 0x080 必须在 Global Pass-All 之前配置。
* FDCAN 匹配顺序:
* - 先匹配标准帧 Filter (Index 0, 1, 2...)
* - 再匹配扩展帧 Filter (Index 0, 1, 2...)
* - 未匹配的帧丢弃
* - 先匹配到的 Filter 生效, 后续不再检查
*/
}
6. 多从站系统滤波器银行优化
6.1 bxCAN bank不足时的优化策略
| 从站数 | 推荐策略 | Bank 用量 | 副作用 |
|---|---|---|---|
| 1--4 | 精确 ID 匹配 (16-bit List, 4 ID/Bank) | 2--4 | 无 |
| 5--8 | 按功能码 Mask (放宽 Node-ID 低 3 位) | 4--7 | 额外通过 0--8 个无用 ID |
| 9--16 | 按功能码 Mask (放宽 Node-ID 低 4 位) | 7 | 额外通过 4--7 个无用 ID |
| 17--127 | 全通 + 软件过滤 | 1 | 所有帧进 CPU, 软件 dispatch |
c
/**
* 全通 + 软件 dispatch --- 从站 > 16 时的兜底方案
*
* 硬件只设 1 个全通 Filter, 所有帧进 FIFO0。
* 在 canDispatch() 中按 Node-ID 做软件路由。
*/
void canopen_master_sw_filter_rx(Message *m)
{
uint8_t func_code = (m->cob_id >> 7) & 0x0F;
uint8_t node_id = m->cob_id & 0x7F;
switch (func_code) {
case 0x01: /* EMCY */
if (node_id >= 1 && node_id <= 127)
emcy_handler[node_id](m);
break;
case 0x03: /* TPDO1 */
if (node_id >= 1 && node_id <= 127)
pdo_handler[node_id][0](m);
break;
case 0x0B: /* SDOtx */
if (node_id >= 1 && node_id <= 127)
sdo_response_handler[node_id](m);
break;
case 0x0E: /* Heartbeat */
if (node_id >= 1 && node_id <= 127)
heartbeat_handler[node_id](m);
break;
default:
break; /* 忽略 */
}
}
6.2 FDCAN 128 元素分配建议
| 元素范围 | 用途 | 数量 |
|---|---|---|
| 0--11 | 标准帧: 从站功能码段 Range 滤波器 | 12 |
| 12--19 | 标准帧: 保留 / 用户扩展 | 8 |
| 20--49 | 扩展帧: 按需分配 | 30 |
| 50 | 标准帧: 全局 Reject 非 CANopen ID | 1 |
| 51--127 | 保留/未使用 | 77 |
7. 调试与验证
7.1 验证清单
| 检查项 | 方法 |
|---|---|
| 滤波器配置成功 | HAL_CAN_ConfigFilter 返回 HAL_OK |
| 滤波方向正确 | 从站能收到 NMT Start (0x000, data=01+node) |
| 广播帧不丢 | 用 CAN 分析仪发 SYNC (0x080),确认从站收到 |
| SDO 通信正常 | 主站读从站 0x1000 (Device Type),有应答 |
| 误接收已拒帧 | 从另一节点发从站不应接收的帧,确认驱动未投递 |
7.2 bxCAN 滤波器寄存器调试
c
void can_print_filter_registers(CAN_HandleTypeDef *hcan)
{
/* 读取 Filter Bank 0 的寄存器值 */
uint32_t fr1 = hcan->Instance->sFilterRegister[0].FR1;
uint32_t fr2 = hcan->Instance->sFilterRegister[0].FR2;
printf("CAN Filter Bank 0: FR1=0x%08lX FR2=0x%08lX\r\n", fr1, fr2);
/* 32-bit 模式:
* FR1[31:16] 通过标准帧: ((FR1>>16)&0xFFFF)>>5 → 期望的 11-bit ID
* FR2[31:16] 通过标准帧掩码: ((FR2>>16)&0xFFFF)>>5 → 关心的 bit 位
*/
}
7.3 常见配置错误
| 错误 | 现象 | 根因 | 修正 |
|---|---|---|---|
| ID 没有左移 | 滤波器不生效,收不到任何帧 | bxCAN 需要 ID << 5 对齐 bit15:5 | (id << 5) |
| 扩展帧 ID 错位 | 扩展帧收不到 | 扩展帧 ID 需 (id << 3) 对齐 bit31:3 |
`(id << 3) |
| Bank 编号冲突 | 配置失败 (返回 HAL_ERROR) | 同一 Bank 被配置两次 | 递增或维护 Bank 分配表 |
| FIFO 分配错误 | 帧进了错误的 FIFO | FilterFIFOAssignment 设反了 |
从站全用 FIFO0 |
| Mask 过宽 | 收到不应接收的帧 | MaskIdHigh 位宽不够 |
收紧 mask, 或双 Bank 覆盖 |
| 滤波器顺序 | Reject 不生效 | 序列问题 | 先配置 Reject filter, 后配置 Pass filter |