4. CAN报文过滤与接收中断处理

任务说明

在 S32G 的 FlexCAN 模块上配置接收滤波器(Rx FIFO 全局掩码 + 单个 ID 过滤),使 CAN 控制器只接收 ID 落在 [0x100, 0x1FF] 范围内的标准帧,并在接收中断回调中读取报文。本案例基于 S32 SDK 的 FlexCAN 驱动,使用 FlexCAN0,波特率 500 kbps。

环境与依赖

  • S32 Design Studio + S32K3xx / S32G 系列 SDK(含 flexcan_driver、flexcan_hw_access、osif)。
  • FlexCAN0 时钟已使能,CAN_H/CAN_L 已接总线(或回环测试)。
  • 中断向量 FlexCAN0_0_IRQn 已在启动文件中注册。

实现思路

FlexCAN 的接收过滤分两层:先由 Rx FIFO 全局掩码(RXFGMASK)决定哪些 ID 进入 FIFO,再由 FIFO 内的单个 ID 过滤表(RXFIR)做精确匹配。本案例用全局掩码把高 7 位固定为 0x1(即 ID 0x100~0x1FF),再在中断回调中读取 FIFO 报文。

总线报文 RXFGMASK 全局掩码 ID[10:4]=0x1 RXFIR 单 ID 过滤 匹配 0x100~0x1FF Rx FIFO 中断回调 读 MB / 处理

关键完整代码

#include "flexcan_driver.h"
#include "flexcan_hw_access.h"
#include "osif.h"

#define INSTANCE        0U
#define RX_MB_IDX       1U
#define RX_FIFO_IDX     0U
#define ID_MIN          0x100U
#define ID_MAX          0x1FFU

static flexcan_driver_state_t g_flexcanState;
static flexcan_msgbuff_t       g_rxMsg;
static volatile uint32_t       g_rxCount = 0U;

/* 接收中断回调:从 Rx FIFO 读取报文 */
static void FlexCAN0_RxCallback(uint8_t instance,
                                flexcan_event_type_t eventType,
                                flexcan_msgbuff_t *msgBuff)
{
    (void)instance;
    if (eventType == FLEXCAN_EVENT_RX_COMPLETE) {
        g_rxMsg = *msgBuff;
        g_rxCount++;
        /* 在此处理报文:g_rxMsg.data / g_rxMsg.msgId / g_rxMsg.dataLen */
    }
}

/* 配置 Rx FIFO 全局掩码 + 单 ID 过滤,仅接收 0x100~0x1FF */
static void FlexCAN0_ConfigFilter(void)
{
    /* 全局掩码:ID[10:4] 必须等于 0x1,即 0x100~0x1FF */
    uint32_t mask = 0x7F0U;   /* 高 7 位参与比较 */
    FLEXCAN_SetRxFifoGlobalMask(INSTANCE, mask);

    /* 单 ID 过滤表:匹配 0x100 */
    flexcan_id_table_t idTable[1];
    idTable[0].isRemoteFrame  = false;
    idTable[0].isExtendedFrame = false;
    idTable[0].id             = ID_MIN;
    FLEXCAN_SetRxFifoFilter(INSTANCE, FLEXCAN_RX_FIFO_ID_FORMAT_A,
                            idTable, 1U);
}

void FlexCAN0_Init(void)
{
    flexcan_user_config_t cfg;
    FLEXCAN_DRV_GetDefaultConfig(&cfg);
    cfg.fd_enable        = false;
    cfg.pe_clock         = 40000000U;   /* 外设时钟 40 MHz */
    cfg.max_num_mb       = 16U;
    cfg.num_fifo_mb      = 6U;          /* 使用 6 个 FIFO 邮箱 */
    cfg.is_rx_fifo_needed = true;
    cfg.flexcanMode      = FLEXCAN_NORMAL_MODE;
    cfg.bitrate.bitrate  = 500000U;
    cfg.bitrate.samplePoint = 800U;     /* 80% 采样点 */

    FLEXCAN_DRV_Init(INSTANCE, &g_flexcanState, &cfg);

    FlexCAN0_ConfigFilter();

    /* 注册接收回调并启动接收 */
    FLEXCAN_DRV_InstallEventCallback(INSTANCE, FlexCAN0_RxCallback, NULL);
    FLEXCAN_DRV_ConfigRxFifo(INSTANCE, FLEXCAN_RX_FIFO_START,
                             &g_rxMsg);
    FLEXCAN_DRV_StartRxFifo(INSTANCE);
}

int main(void)
{
    FlexCAN0_Init();

    for (;;) {
        /* 主循环可做其他任务;报文在中断回调中处理 */
    }
}

运行与验证

  1. 编译烧录后,用 CAN 分析仪向总线发送 ID 为 0x150 的标准帧,观察 g_rxCount 递增、g_rxMsg.msgId 为 0x150。
  2. 发送 ID 为 0x050 或 0x300 的帧,g_rxCount 应保持不变,说明过滤生效。
  3. 若使用回环模式(FLEXCAN_LOOPBACK_MODE),可在 main 中调用 FLEXCAN_DRV_Send 自发自收验证。

通过条件:仅 ID 在 [0x100, 0x1FF] 范围内的标准帧触发回调并更新 g_rxCount,范围外报文被硬件过滤丢弃。

常见问题

  • 回调不触发:检查 FlexCAN0_0_IRQn 是否使能、num_fifo_mb 是否大于 0。
  • 过滤不生效:确认 RXFGMASK 与 RXFIR 的 ID 格式(标准/扩展)一致,扩展帧需改用 FLEXCAN_RX_FIFO_ID_FORMAT_B。
  • 波特率不匹配:pe_clock 必须与实际外设时钟一致,否则采样点偏移导致接收错误。