第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一次就过了。
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%,计算起来很舒服。
例如:80MHz / (16 × (1 + 15 + 4)) = 5Mbps
2.3 FlexCAN配置流程
配置FlexCAN其实不复杂,但步骤不能乱。我一般按这个流程走:
- 使能模块时钟:在CCM寄存器中打开FlexCAN时钟门控
- 进入冻结模式:设置MCR[FRZ]和MCR[HALT],让模块停止工作
- 配置全局参数:设置波特率、采样点、最大数据长度(64字节)
- 配置消息缓冲区:设置每个MB的ID、方向、数据长度
- 退出冻结模式:清除MCR[HALT],模块开始工作
- 使能中断(可选):设置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);
}
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计算这些。到时候咱们再细聊。