3. 基于FlexCAN的网关多路CAN通道初始化

任务目标

在 S32G 上同时初始化 3 路 FlexCAN 实例(CAN0/CAN1/CAN2),分别配置为 500 kbps、250 kbps、1 Mbps,并为每路设置独立的接收滤波规则,为后续网关多路转发提供可用的收发通道。

环境与依赖

  • S32G274A 开发板,S32 Design Studio for S32 Platform(S32DS)+ S32 SDK(RTD 或 Classic SDK 均可,本例使用 Classic SDK 的 FlexCAN 驱动接口)。
  • 时钟:CAN 模块时钟 CAN_CLK = 40 MHz(由 MC_CGM 提供,需在时钟初始化中使能)。
  • 引脚:CAN0 使用 PTA6/PTA7(CAN0_TX/RX),CAN1 使用 PTA8/PTA9,CAN2 使用 PTA10/PTA11,均配置为 ALT1 复用功能。

实现思路

用一张表描述每路 CAN 的差异参数(波特率、采样点、滤波规则),初始化函数遍历该表,逐路调用 FlexCAN 驱动完成:时钟使能 → 引脚复用 → 位时序计算 → 邮箱/滤波配置 → 进入正常模式。这样新增通道只需扩展表项,无需改动逻辑。

多路 FlexCAN 初始化流程 配置表 canCfg[3] 遍历每路通道 时钟 + 引脚复用 位时序计算 滤波规则配置 进入 Normal 模式 3 路 CAN 就绪

关键完整代码

#include "flexcan_driver.h"
#include "flexcan_hw_access.h"
#include "clock.h"
#include "pins_port_hw_access.h"

#define CAN_CLK_HZ      40000000U   /* CAN 模块时钟 40 MHz */

/* 每路 CAN 的差异参数:波特率、采样点、滤波 ID/掩码 */
typedef struct {
    uint8_t  inst;          /* FlexCAN 实例号 0/1/2 */
    uint32_t baud;          /* 目标波特率 */
    uint8_t  samplePoint;   /* 采样点百分比,如 75 */
    uint32_t rxId;          /* 接收滤波 ID */
    uint32_t rxMask;        /* 接收滤波掩码 */
} CanChannelCfg;

static const CanChannelCfg canCfg[3] = {
    { 0, 500000U, 75, 0x100U, 0x7FFU },  /* CAN0: 500k, 只收 0x100 */
    { 1, 250000U, 75, 0x200U, 0x7F0U },  /* CAN1: 250k, 收 0x200~0x20F */
    { 2, 1000000U,75, 0x300U, 0x7FFU },  /* CAN2: 1M,   只收 0x300 */
};

/* 根据波特率与采样点计算位时序,写入 CTRL1 */
static void can_set_bit_timing(CAN_Type *base, uint32_t baud, uint8_t sp)
{
    uint32_t presDiv, tq, propSeg, phase1, phase2;
    /* 先取一个合适的预分频,使每比特 Tq 数落在 8~25 之间 */
    for (presDiv = 1; presDiv <= 256; presDiv++) {
        tq = CAN_CLK_HZ / (presDiv * baud);
        if (tq >= 8U && tq <= 25U) break;
    }
    phase1 = (tq * sp / 100U) - 1U;   /* 含传播段 */
    phase2 = tq - phase1 - 1U;        /* 剩余为相位缓冲段2 */
    if (phase1 < 1U) phase1 = 1U;
    if (phase2 < 1U) phase2 = 1U;

    base->CTRL1 = CAN_CTRL1_PRESDIV(presDiv - 1U)
                | CAN_CTRL1_PSEG1(phase1 - 1U)
                | CAN_CTRL1_PSEG2(phase2 - 1U)
                | CAN_CTRL1_PROPSEG(0U)
                | CAN_CTRL1_RJW(0U)
                | CAN_CTRL1_SMP(1U);   /* 采样一次,位于采样点 */
}

/* 配置一个接收邮箱作为滤波入口 */
static void can_set_rx_filter(CAN_Type *base, uint8_t mbIdx,
                              uint32_t id, uint32_t mask)
{
    base->RAMn[mbIdx * 4 + 0] = (id << 18);          /* 标准 ID 左移 18 位 */
    base->RAMn[mbIdx * 4 + 1] = (mask << 18);        /* 掩码 */
    base->RXIMR[mbIdx]       = mask;                 /* 个体掩码寄存器 */
    base->IFLAG1 = (1U << mbIdx);                    /* 清标志 */
    base->IMASK1 = (1U << mbIdx);                    /* 使能中断 */
}

/* 初始化单路 FlexCAN */
static void can_init_one(const CanChannelCfg *cfg)
{
    CAN_Type *base = (CAN_Type *)(CAN0_BASE + cfg->inst * 0x4000U);

    /* 1. 使能时钟(假设 MC_ME 已使能 CAN 模块,此处仅解除冻结) */
    base->MCR &= ~CAN_MCR_MDIS_MASK;                 /* 退出禁用 */
    while (base->MCR & CAN_MCR_FRZACK_MASK) {}       /* 等待冻结确认 */

    /* 2. 进入冻结模式以配置 */
    base->MCR |= CAN_MCR_FRZ_MASK | CAN_MCR_HALT_MASK;
    while (!(base->MCR & CAN_MCR_FRZACK_MASK)) {}

    /* 3. 位时序 */
    can_set_bit_timing(base, cfg->baud, cfg->samplePoint);

    /* 4. 滤波:使用邮箱 0 作为接收入口 */
    can_set_rx_filter(base, 0U, cfg->rxId, cfg->rxMask);

    /* 5. 退出冻结,进入正常模式 */
    base->MCR &= ~(CAN_MCR_FRZ_MASK | CAN_MCR_HALT_MASK);
    while (base->MCR & CAN_MCR_FRZACK_MASK) {}
}

/* 引脚复用:CAN0~2 使用 PTA6~PTA11,ALT1 */
static void can_pins_init(void)
{
    /* 实际工程中通过 PORT 驱动设置 MSCR,此处示意关键寄存器操作 */
    for (uint32_t pin = 6U; pin <= 11U; pin++) {
        PORTA->PCR[pin] = PORT_PCR_MUX(1U);   /* ALT1 = CAN */
    }
}

/* 对外入口:初始化全部通道 */
void gateway_can_init_all(void)
{
    can_pins_init();
    for (uint8_t i = 0U; i < 3U; i++) {
        can_init_one(&canCfg[i]);
    }
}

运行与验证

  1. 在 S32DS 中新建 S32G274A 工程,将上述代码加入 main.c 或独立 can_init.c,在 main() 中调用 gateway_can_init_all()。
  2. 编译烧录后,用 CAN 分析仪(如 PCAN、CANoe)分别接入 CAN0/CAN1/CAN2 总线。
  3. 验证波特率:分析仪设为 500k/250k/1M,能正常收发即说明位时序正确;若报错帧,检查 CAN_CLK_HZ 与实际时钟是否一致。
  4. 验证滤波:向 CAN0 发送 ID=0x100 的帧应被接收,发送 0x101 应被过滤;CAN1 发送 0x200~0x20F 应被接收,0x210 应被过滤。
  5. 通过条件:三路均能进入 Normal 模式(MCR.FRZACK=0),且滤波行为符合上表规则。

常见问题

  • 若 FRZACK 一直不置位,检查 CAN 模块时钟是否使能、MDIS 是否正确清除。
  • 波特率偏差大时,优先核对 CAN_CLK_HZ 与 MC_CGM 实际输出,再调整预分频。
  • 滤波不生效时,确认 RXIMR 与 RAMn 中的掩码一致,且邮箱已通过 IMASK1 使能。