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 驱动完成:时钟使能 → 引脚复用 → 位时序计算 → 邮箱/滤波配置 → 进入正常模式。这样新增通道只需扩展表项,无需改动逻辑。
关键完整代码
#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]);
}
}
运行与验证
- 在 S32DS 中新建 S32G274A 工程,将上述代码加入
main.c或独立can_init.c,在main()中调用gateway_can_init_all()。 - 编译烧录后,用 CAN 分析仪(如 PCAN、CANoe)分别接入 CAN0/CAN1/CAN2 总线。
- 验证波特率:分析仪设为 500k/250k/1M,能正常收发即说明位时序正确;若报错帧,检查
CAN_CLK_HZ与实际时钟是否一致。 - 验证滤波:向 CAN0 发送 ID=0x100 的帧应被接收,发送 0x101 应被过滤;CAN1 发送 0x200~0x20F 应被接收,0x210 应被过滤。
- 通过条件:三路均能进入 Normal 模式(
MCR.FRZACK=0),且滤波行为符合上表规则。
常见问题
- 若
FRZACK一直不置位,检查 CAN 模块时钟是否使能、MDIS是否正确清除。 - 波特率偏差大时,优先核对
CAN_CLK_HZ与 MC_CGM 实际输出,再调整预分频。 - 滤波不生效时,确认
RXIMR与RAMn中的掩码一致,且邮箱已通过IMASK1使能。