CANopen 主从站过滤器配置笔记

CANopen 过滤器配置


目录

  1. [CANopen 预定义连接集 COB-ID 分配表](#CANopen 预定义连接集 COB-ID 分配表)
  2. 从站过滤器配置
  3. 主站过滤器配置
  4. [STM32 bxCAN 代码 (F4)](#STM32 bxCAN 代码 (F4))
  5. [STM32 FDCAN 代码 (H7/G4)](#STM32 FDCAN 代码 (H7/G4))
  6. 多从站系统滤波器银行优化
  7. 调试与验证

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

最少配置 来看,使用 0x0000x080 单独匹配 + 一条 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