3. STM32 CAN外设介绍:bxCAN特性、工作模式与滤波器配置

好,咱们进入正题。STM32的CAN外设,官方叫它bxCAN——基本扩展CAN。说白了,就是ST在CAN控制器基础上做了不少优化。我最早接触它是在做一款车载网关的时候,当时被一堆报文搞得头大,后来摸透了bxCAN的脾气,才发现它其实挺贴心的。

3.1 bxCAN的核心特性

bxCAN不是那种“能用就行”的外设。它有几个让我印象深刻的点:

  • 支持CAN协议2.0A和2.0B——标准帧和扩展帧都能处理。我建议你直接统一用扩展帧,省得后面兼容性问题找上门。
  • 三个发送邮箱——可以缓存三帧待发报文。硬件自动调度优先级,你只管往里塞数据就行。
  • 两个接收FIFO——每个FIFO深度3级。嗯,这里要注意:FIFO满了新报文会被丢弃,所以读取要及时。
  • 28个可配置的筛选器组——这是bxCAN的灵魂。后面我会重点讲。
  • 位时序可编程——波特率、采样点都能调。我在项目里吃过亏,后面会给你避坑指南。

我个人习惯:拿到新板子第一件事,先确认bxCAN的时钟源是APB1(通常36MHz或45MHz)。时钟不对,后面所有配置都是白搭。

3.2 bxCAN的工作模式

bxCAN有三种工作模式,你想想看,其实就对应了开发调试的三个阶段:

模式 说明 典型场景
正常模式 收发都正常,参与总线通信 产品正式运行
静默模式 只收不发,不产生ACK 监听总线、分析报文
环回模式 自发自收,不经过总线 调试发送逻辑、验证滤波器

我刚开始做CAN开发时,经常在环回模式下调试滤波器配置。为什么?因为不会干扰总线上的其他节点。等滤波器调好了,再切到正常模式联调。

一个小技巧:调试阶段可以用“静默环回模式”——既能自发自收,又不影响总线。这个模式在STM32参考手册里叫“Loopback combined with Silent”。

3.3 bxCAN滤波器配置——这才是重点

滤波器,说白了就是硬件帮你做报文过滤。你想想看,总线上每秒几百帧报文,如果每帧都进中断处理,CPU就别干别的了。滤波器能让你只关心你想要的报文。

bxCAN有28个筛选器组,每个组可以配置为:

  • 标识符列表模式——只接收指定的几个ID
  • 标识符掩码模式——接收符合某类模式的ID

每个筛选器组可以配置为:

  • 32位模式——处理标准帧或扩展帧的完整ID
  • 16位模式——处理两个标准帧ID

我一般这样选:

  • 如果只关心几个固定ID → 用列表模式,32位
  • 如果关心一组ID(比如0x100~0x1FF)→ 用掩码模式,32位
  • 如果标准帧ID很多 → 用16位模式,一个筛选器组管两个ID

3.3.1 滤波器配置示例

假设我们要接收ID为0x123和0x456的扩展帧。用列表模式,32位:

// 使能CAN1的时钟和滤波器组
CAN_FilterInitTypeDef  CAN_FilterInitStructure;

CAN_FilterInitStructure.CAN_FilterNumber = 0;       // 使用筛选器组0
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdList;  // 列表模式
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; // 32位
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x1234;  // 这里存ID的高16位
CAN_FilterInitStructure.CAN_FilterIdLow = 0x5678;   // 这里存ID的低16位
// 注意:实际ID需要左移,具体看参考手册

CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x4567;  // 第二个ID的高16位
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x89AB;   // 第二个ID的低16位

CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FIFO0; // 关联到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;        // 激活

CAN_FilterInit(&CAN_FilterInitStructure);

我曾经踩过的坑:滤波器配置时,ID的存储格式不是你想的那样。标准帧ID是11位,扩展帧ID是29位,它们在寄存器里的位置和移位方式不同。我建议你直接看STM32参考手册的“Filter bank”章节,对照寄存器位定义来写。别问我为什么知道——有一次我调了两天才发现是ID移位搞错了。

3.3.2 掩码模式实战

假设我们要接收所有ID高8位为0x1A的扩展帧(即0x1Axxxxxx)。用掩码模式:

CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;

// 期望的ID值:高8位为0x1A,其余位任意
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x1A00;  // 高16位
CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;   // 低16位

// 掩码:高8位必须匹配(掩码位为1),其余位忽略(掩码位为0)
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0xFF00;  // 高8位必须匹配
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;   // 低16位忽略

CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FIFO0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);

你看,掩码模式就是“你想让哪几位必须匹配,就把掩码对应位置1”。这个逻辑其实很直观。

3.4 滤波器配置的几点建议

  • 优先使用FIFO0——FIFO0的中断优先级可以设得高一些,处理紧急报文。FIFO1放普通报文。
  • 筛选器组从0开始用——硬件会按编号从小到大匹配,匹配到了就不再往下查。所以把最常用的ID放在前面的筛选器组。
  • 调试时先关滤波器——我习惯先设成“接收所有报文”,等通信调通了再慢慢加滤波器。否则你都不知道是发送问题还是滤波问题。

总结一下:bxCAN的滤波器是硬件加速的利器。用好了,CPU负载能降一大半。用不好,你可能会在中断里疯狂丢帧。我个人建议:先花半小时把滤波器配置吃透,后面能省你三天调试时间。

下一章咱们聊聊CAN的位时序和波特率计算——这可是让很多新手头疼的地方。到时候我会分享一个我常用的计算小工具,保证你一看就懂。