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的位时序和波特率计算——这可是让很多新手头疼的地方。到时候我会分享一个我常用的计算小工具,保证你一看就懂。