第2章:NXP CANFD控制器:收发器选型、S32K1xx CANFD模块与FlexCAN配置流程

各位同学,咱们今天聊聊NXP的CANFD硬件选型和配置。说实话,这块内容我当年刚接触时也踩过不少坑。你想想看,CANFD虽然和经典CAN长得像,但收发器选型、模块配置这些细节,稍不注意就会翻车。

2.1 NXP TJA系列收发器选型

收发器是CAN网络的物理层接口。说白了,它就是把控制器发出的数字信号,转换成差分信号在总线上跑。NXP的TJA系列我用得最多,这里给大家梳理一下。

2.1.1 主流型号对比

型号 速率支持 待机功耗 典型应用
TJA1043 经典CAN / CANFD (5Mbps) 极低(< 5μA) 车身控制、网关
TJA1057 经典CAN / CANFD (5Mbps) 低(< 10μA) 动力总成、ADAS
TJA1145 经典CAN / CANFD (8Mbps) 超低(< 2μA) 部分网络唤醒、低功耗节点
TJA1463 CANFD (8Mbps) / CAN SIC 低(< 5μA) 高速骨干网、多节点拓扑

我个人习惯,做车身控制首选TJA1043。为什么?因为它支持待机模式下的远程唤醒,而且功耗控制得特别好。我在一个BCM项目中,整机待机电流要求小于100μA,选TJA1043一次就过了。

小提示: 如果你的网络节点超过10个,或者线缆长度超过5米,建议用TJA1463这种带SIC(信号改善能力)的收发器。它能有效抑制振铃,减少误码。

2.1.2 选型避坑指南

我曾经在一个项目中,为了省几毛钱选了TJA1050(经典CAN收发器)。结果CANFD跑2Mbps时,总线波形惨不忍睹。后来换成TJA1057,问题立刻解决。所以记住:跑CANFD,必须选支持CANFD的收发器

另外,注意收发器的共模电压范围。车载环境干扰大,TJA系列一般能扛±12V的共模电压。但如果你用在商用车或工程机械上,建议选TJA1145,它支持±36V的耐压。

2.2 S32K1xx系列CANFD模块

S32K1xx系列是NXP主推的车规级MCU。它的CANFD模块叫FlexCAN,支持最多3路CANFD。嗯,这里要注意,不同型号的S32K,FlexCAN的实例数量不一样。

2.2.1 FlexCAN模块特性

  • 支持CAN 2.0B和CANFD:可配置为经典CAN或CANFD模式
  • 64个消息缓冲区(MB):每个MB可独立配置为发送或接收
  • 支持DMA传输:减少CPU负载,适合大数据量场景
  • 可编程的采样点:支持80%采样点配置(CANFD推荐)
  • 硬件时间戳:精度可达1μs,用于网络同步

我记得第一次用S32K146做CANFD网关时,被它的MB配置搞晕了。64个MB,每个都要设置ID、数据长度、方向。后来我总结了一个规律:发送MB用轮询,接收MB用中断。这样既保证实时性,又不会让CPU忙死。

2.2.2 时钟与波特率配置

FlexCAN的时钟源有两种:

  • 系统时钟(SYS_CLK):通常为80MHz或120MHz
  • 外部晶振(OSC_CLK):通常为8MHz或16MHz

我个人建议用系统时钟。为什么?因为系统时钟频率高,分频后更容易得到精确的波特率。比如你要跑5Mbps,用80MHz时钟,分频系数设为16,采样点设在80%,计算起来很舒服。

关键公式: 波特率 = 时钟频率 / (分频系数 × (1 + 时间段1 + 时间段2))
例如:80MHz / (16 × (1 + 15 + 4)) = 5Mbps

2.3 FlexCAN配置流程

配置FlexCAN其实不复杂,但步骤不能乱。我一般按这个流程走:

  1. 使能模块时钟:在CCM寄存器中打开FlexCAN时钟门控
  2. 进入冻结模式:设置MCR[FRZ]和MCR[HALT],让模块停止工作
  3. 配置全局参数:设置波特率、采样点、最大数据长度(64字节)
  4. 配置消息缓冲区:设置每个MB的ID、方向、数据长度
  5. 退出冻结模式:清除MCR[HALT],模块开始工作
  6. 使能中断(可选):设置IMASK寄存器,开启接收或错误中断

这里有个坑:配置MB时,一定要在冻结模式下进行。我曾经在运行模式下改MB配置,结果总线直接报错。后来查手册才知道,FlexCAN的MB配置只能在冻结模式下修改。

2.3.1 代码示例:初始化FlexCAN

// 假设使用S32K144,FlexCAN0,时钟80MHz,波特率5Mbps
void FlexCAN0_Init(void) {
    // 1. 使能时钟
    PCC->PCCn[PCC_FlexCAN0_INDEX] |= PCC_PCCn_CGC_MASK;
    
    // 2. 进入冻结模式
    CAN0->MCR |= CAN_MCR_FRZ_MASK | CAN_MCR_HALT_MASK;
    while(!(CAN0->MCR & CAN_MCR_LPMACK_MASK));
    
    // 3. 配置波特率:80MHz / (16 * (1 + 15 + 4)) = 5Mbps
    CAN0->CTRL1 = CAN_CTRL1_PRESDIV(15) |   // 分频系数16
                   CAN_CTRL1_PROPSEG(7)  |   // 时间段1 = 8
                   CAN_CTRL1_PSEG1(7)    |   // 时间段2 = 8
                   CAN_CTRL1_PSEG2(3);       // 采样点 = 80%
    
    // 4. 使能CANFD,设置64字节数据场
    CAN0->MCR |= CAN_MCR_FDEN_MASK;
    CAN0->CTRL2 |= CAN_CTRL2_ISOCANFDEN_MASK;
    CAN0->CTRL2 |= CAN_CTRL2_BOFFDONEMASK_MASK;
    
    // 5. 配置MB0为发送,标准ID 0x123
    CAN0->MB[0].CS = CAN_CS_CODE(0x0C);  // 发送MB,数据长度8字节
    CAN0->MB[0].ID = CAN_ID_STD(0x123);
    
    // 6. 退出冻结模式
    CAN0->MCR &= ~CAN_MCR_HALT_MASK;
    while(CAN0->MCR & CAN_MCR_LPMACK_MASK);
}
注意: 上面的代码只是演示核心逻辑。实际项目中,你还需要配置引脚复用(比如PTA0/PTA1作为CAN0_TX/RX),以及处理错误状态寄存器。这些细节我后面章节会展开讲。

2.3.2 发送与接收流程

发送CANFD帧,我习惯用轮询方式:

void CANFD_Send(uint8_t mb_idx, uint8_t *data, uint8_t len) {
    // 等待MB空闲
    while(CAN0->MB[mb_idx].CS & CAN_CS_CODE_MASK);
    
    // 填充数据
    for(int i=0; i<len; i++) {
        CAN0->MB[mb_idx].DATA[i] = data[i];
    }
    
    // 设置数据长度并触发发送
    CAN0->MB[mb_idx].CS = CAN_CS_CODE(0x0C) | CAN_CS_DLC(len);
    CAN0->IFLAG1 |= (1 << mb_idx);  // 清除标志位
}

接收的话,我建议用中断。配置好MB后,在中断服务函数里读取数据:

void FLEXCAN0_IRQHandler(void) {
    uint32_t flags = CAN0->IFLAG1;
    
    if(flags & (1 << 0)) {  // MB0接收中断
        uint8_t len = (CAN0->MB[0].CS & CAN_CS_DLC_MASK) >> 16;
        // 读取数据到缓冲区
        for(int i=0; i<len; i++) {
            rx_buffer[i] = CAN0->MB[0].DATA[i];
        }
        CAN0->IFLAG1 = (1 << 0);  // 清除中断标志
    }
}

你想想看,如果不用中断,CPU就得一直轮询接收标志位,多浪费算力。尤其是网关节点,同时处理多路CANFD,中断方式能省不少事。

2.4 小结

这一章咱们聊了收发器选型、FlexCAN模块特性,还有配置流程。说白了,CANFD硬件这块,收发器选对了,时钟配准了,MB配置别搞错,基本就稳了。

下一章我会讲CANFD的协议细节,包括数据场长度编码、CRC计算这些。到时候咱们再细聊。

课后练习: 试着用S32K144的FlexCAN0,配置一个5Mbps的CANFD通道,发送一个标准ID为0x456、数据为8字节的帧。如果遇到问题,欢迎在课程群里讨论。