【双 MCU 架构车载仪表终端(CAN 互联)】STM32F1 bxCAN 外设
1. 引言
STM32 内置 bxCAN 外设(CAN 控制器),bx = Basic eXtended,支持 CAN 2.0A/B(标准+扩展格式全支持)。
2. bxCAN 外设简介
| 参数 | 说明 |
|---|---|
| 波特率最高 1Mbps | 高速 CAN 范围 125k~1M,完美支持 |
| 3 个发送邮箱 | 可缓存 3 个待发报文 |
| 2 个 3 级深度接收 FIFO | 共缓存 2×3=6 个报文 |
| 14 个过滤器组 | (互联型才有 28 个) 按 ID 过滤接收报文,无关报文硬件直接丢弃,减轻软件负担 |
| 特色功能 | 时间触发通信、自动离线恢复、自动唤醒、禁止自动重传、FIFO 溢出方式可配、发送优先级可配、双 CAN 模式(了解即可,一般只用核心功能) |
F103C8T6 只有 CAN1 一个外设。
3. CAN 框图

手册框图分上下两部分:上半是主 CAN(CAN1),下半是从 CAN(CAN2,仅互联型有,且 CAN2 不是独立资源,是辅助 CAN1 共用一条总线)。本芯片只有上半部分,含三大块:CAN2.0B 内核(一堆寄存器,程序读写寄存器操纵电路)、3 个发送邮箱、接收过滤器 + 2 个 FIFO。
4. CAN 基本结构

4.1 发送路径
报文参数(ID、Data、IDE、RTR 等)→ 写入一个空置发送邮箱 → 请求发送 → “管理员”(发送控制电路)等总线空闲自动广播,位同步/仲裁等全自动。
- 为什么 3 个邮箱:防止总线繁忙时发送拥堵——上一个报文滞留邮箱时,CPU 可以换另一个空邮箱继续写;三个都满才需要 CPU 等待(那说明总线已重度拥堵,一般不会发生);
- 发送策略可配:多个报文同时待发时,先请求先发送(先来后到)或按 ID 优先级(ID 小的先发)。
4.2 接收路径
总线上任何数据帧/遥控帧都会被硬件收下 → 过接收过滤器(14 个,按 ID 比对)→ 通过的进入 FIFO0 或 FIFO1 排队 → CPU 轮询或中断读取。过滤器可指定通过的报文进哪个 FIFO;未配置的过滤器默认禁用。硬件过滤替软件省下了逐条比对 ID 的工作。
4.2.1 FIFO 排队规则
FIFO = 先进先出存储器 = 排队。每个 FIFO 深 3 级:报文从队尾进、CPU 从队首读走,读完释放邮箱、后面整体前移。队满后再来报文,由 RFLM 位配置两种丢法:锁定(丢新来的)或不锁定(新来的踢掉队尾旧报文)。
为什么要 2 个 FIFO:分流。不重要报文数量大、重要报文数量少——若全排一个队,队满时大概率排到的都是不重要报文,重要的反而排不上丢失;分两个队(重要/不重要分开),重要报文丢失概率大降。官方不给两个 FIFO 分优先级(平级),如何分流由开发者按重要性/种类/频率自行决定。
5. 发送过程(寄存器)

发送邮箱状态流转(细节):
选空邮箱 → 写报文 → 请求发送(TXRQ=1)。状态机:
空置(TME=1) ──请求发送──> 挂号(数据就绪,排队中)
挂号 ──优先级最高──> 预定(下次就轮到我)
预定 ──按ID优先级被超──> 退回挂号(按请求次序则不会退回)
预定 ──总线空闲──> 发送中 ──成功──> 空置(RQCP=1,TXOK=1,TME=1)
└─失败─┬─ NART=0(自动重传)→回预定,重发直到成功
└─ NART=1(禁止重传)→空置(RQCP=1,TXOK=0)
挂号/预定 ──ABRQ=1 终止发送──> 空置(同发送失败)
6. 接收过程(寄存器)

接收 FIFO 状态流转(细节):
- 空状态(FMP=0)→ 收 1 个报文 → 挂号1(FMP=1)→ 挂号2(FMP=10B,UP 主指出手册此处写成 0x10 是笔误,应为二进制 10)→ 挂号3(FMP=11B,FULL 置 1)→ 再收 → 溢出(FOV=1,FMP 保持 3);
- 读取规则:CPU 每读走一个报文必须释放邮箱(RFOM=1),队伍前移;
- 手册图上“溢出→释放→挂号3→再释放→挂号2”看起来要释放两次,实际溢出和挂号3 是同一个状态(队伍长度都是 3),释放一次直接进挂号2。
7. 发送和接收寄存器配置位
| 位 | 名称 | 置 1 | 置 0 |
|---|---|---|---|
| NART | 禁止自动重传 | 报文只发一次,无论成败 | 失败自动重传直到成功 |
| TXFP | 发送优先级 | 按请求顺序(先来后到) | 按标识符(ID 小先发;ID 相同邮箱号小先发) |
| RFLM | 接收 FIFO 锁定 | 溢出时丢弃新报文 | 溢出时新报文覆盖队尾旧报文 |
8. 标识符过滤器

每个过滤器(共 14 个,各一套、互不影响,x=0~13)拥有:
- 两个 32 位核心寄存器 FR1、FR2(FxR131:0、FxR231:0)——存放 ID 过滤关键信息;
- FSCx 位宽:0=16 位,1=32 位;
- FBMx 模式:0=屏蔽模式,1=列表模式;
- FFAx 关联 FIFO:0=通过的报文存 FIFO0,1=存 FIFO1(对应 P6 图里每个过滤器指向两个队列的二选一箭头);
- FACTx 激活:0=禁用,1=启用(不用的过滤器关掉,防误动作)。
FSC × FBM 组合出四种:32 位列表 / 32 位屏蔽 / 16 位列表 / 16 位屏蔽。
8.1 32 位列表模式(最直接)
FR1、FR2 各写一个目标 ID(一个过滤器最多 2 个),报文 ID 在列表中就通过、不在就丢弃。
32 位寄存器映像(左对齐):STID[10:0](高11位)| EXID[17:0] | IDE | RTR | 保留1位:
- 写标准 ID:ID 写入高 11 位 STID,后面全 0,IDE=0;
- 写扩展 ID:前 29 位(STID+EXID 连起来)整体写入,STID 的 11 位作为扩展 ID 的高 11 位,IDE 必须给 1;
- RTR:0=数据帧,1=遥控帧;
- 易错点:写入的 ID 类型必须与 IDE 位严格对应——写了扩展 ID 却让 IDE=0,硬件只会拿高 11 位当标准 ID 匹配。
8.2 16 位列表模式(只为标准 ID 优化)
很多项目只用标准 ID,32 位映像里 18 位 EXID 空间就浪费了。于是把 FR1、FR2 各拆成两个 16 位寄存器,一个过滤器可写 4 个标准 ID,资源利用率翻倍。
16 位映像:STID[10:0] | RTR | IDE | EXID[17:15](3位废位写0);IDE 固定写 0(16 位根本装不下扩展 ID)。
8.3 屏蔽模式(过滤一组 ID)
示例场景:一个传感器有 100 个温度探头 + 100 个湿度探头,ID 规划成 0x1000x1FF 全是温度、0x2000x2FF 全是湿度。某设备要收全部温度数据——列表模式把 14 个过滤器用光也写不下 100 个 ID → 需要“部分匹配”:ID 高位是 0x1 的全部通过。
- 32 位屏蔽模式:FR1 写 ID 模板,FR2 写屏蔽码(Mask,掩码)——FR2 某位=1 表示 FR1 对应位必须匹配;=0 表示该位无关(1/0 均可)。
例:过滤 0x1 开头的标准数据帧:FR1 = ID 模板(001 后随意填 0)且 IDE=0、RTR=0;FR2 = ID 高 3 位置 1(高 3 位必须为 001)、后面 ID 位全 0(随意),IDE 屏蔽位必须给 1(否则格式无所谓,扩展 ID 会误入),RTR 屏蔽位按需(只要数据帧→RTR 匹配;数据/遥控都要→RTR 屏蔽位给 0)。 - 16 位屏蔽模式:FR1 低 16 位=ID 模板、高 16 位=对应屏蔽码;FR2 同理——一个过滤器可设两组“ID+屏蔽码”。
- ID 规划经验:同类数据的 ID 高位固定、低位作子类区分,正好配合屏蔽模式;把一组 ID 随机分配毫无规律,“属于自己折磨自己”,一般不会这么干。
8.4 配置示例

例 1(16 位列表):总线有 0x123/234/345/456/567/678,要收 0x234、0x345、0x567。
全标准 ID → 选 16 位位宽;ID 无规律 → 只能列表模式。4 个槽写 3 个 ID,第 4 个写 0(前提:总线上不会出现 ID=0 的帧)。
关键细节:写入时 ID 必须左移 5 位——映像是左对齐的,而程序写值默认右对齐,STID+RTR+IDE+3 位 EXID 共占低 5 位;要收遥控帧再或上 0x10(RTR 位)。
例 2(16 位屏蔽):总线有 0x1000x1FF、0x2000x2FF、0x3100x31F、0x3200x32F 四组,要收 0x2000x2FF(256 个)+ 0x3200x32F(16 个)。
全标准 → 16 位;数量多 → 屏蔽模式:
- 第一组:ID 部分写 0x200<<5(0x2 关键、低 8 位填 0 无所谓),屏蔽码写 0x700<<5(高 3 位必须匹配;写 0xF00 也一样,反正左移 5 位后等效)| 0x10(RTR 匹配=数据帧)| 0x8(IDE 必须匹配为 0,只收标准帧,防扩展 ID 误入);
- 第二组:ID=0x320<<5,屏蔽码=0x7F0<<5(高 7 位匹配、低 4 位随意)| 0x10 | 0x8 → 0x320~0x32F。
例 3(32 位列表):总线上有标准 0x123/234/345/456 和扩展 0x12345678、0x0789ABCD,要收 0x123 + 0x12345678。
选 16 还是 32 位由想要的 ID 决定(不是由总线上存在什么决定)——要收的含扩展 ID → 必须 32 位;两 ID 无法分组 → 列表:
- FR1 = 0x123<<21(标准 ID 在 32 位映像左移 21 位;IDE=0、RTR=0 默认收数据帧;要遥控帧或 0x2);
- FR2 = 0x12345678<<3 | 0x4(扩展 ID 左移 3 位,或 0x4 置 IDE=1;要遥控帧再或 0x2)。
例 4(32 位屏蔽,扩展成组):总线有 0x123456000x123456FF 和 0x0789AB000x0789ABFF 各 256 个扩展 ID,要收前一组:
- FR1 = 0x12345600<<3 | 0x4(RTR=0 数据帧);
- FR2 = 0x1FFFF00<<3 | 0x4 | 0x2(ID 高 21 位必须匹配、低 8 位无关;IDE 匹配;RTR 匹配)→ 0x12345600~0x123456FF 扩展数据帧。
例 5(只要遥控帧):任意 ID、只要遥控帧 → 32 位屏蔽:FR1=0x2(RTR=1),FR2=0x2(只匹配 RTR,其余全无所谓)。
例 6(通配/全部通过):调试初期常用——32 位屏蔽,FR1 随便给,FR2 全 0(所有位无关)→ 所有帧全通过,避免过滤器干扰接收程序的调试。
9. CAN 测试模式
调试双端通信的痛点:数据没收对时,不知道是发送程序错、接收程序错还是接线错,无法定位。先单设备自发自收,成功后再互联,程序才稳健。正常模式下 TX 直通发送引脚、RX 直通接收端;三种测试模式改的是内部连接:

注意:环回模式的“自发自收”是报文层面的(自己发的报文自己收回来),不是底层回读电平机制,别钻牛角尖。模式切换只改代码、不用改接线,非常方便。
10. CAN 工作模式

- 初始化模式:配置 CAN 外设参数时使用,禁止报文收发——防止参数配一半、外设就带着错误参数运行干扰总线。配完再切正常;
- 正常模式:正常收发;
- 睡眠模式:低功耗,CAN 外设时钟停止。唤醒方式由 AWUM 位配置:AWUM=1 硬件自动唤醒(一检测到总线活动,硬件自动清 SLEEP 位唤醒,可“处理完就睡、有报文自动醒”,省电不误事);AWUM=0 软件手动唤醒。
状态位与切换:SLAK(睡眠确认)、INAK(初始化确认)。复位后默认睡眠模式(SLAK=1, INAK=0);初始化模式 SLAK=0、INAK=1;正常模式两者都 0。切换靠 SLEEP / INRQ 位发起请求,但不会立刻生效:请求睡眠要等到当前报文应答(ACK)之后(报文发一半不能睡);进入正常模式要等总线空闲(sync 信号,防误操作)。
11. 波特率和采样时序
CAN 规范里一位分四段:SYNC + PTS + PBS1 + PBS2。STM32 的设计者认为 PTS 和 PBS1 之间没啥事干、都是可配置段,干脆合并为一个 BS1 段,少一个参数:

- STM32 位时序三段:SYNC 固定 1Tq、BS1 = 1~16Tq(TS1+1)、BS2 = 1~8Tq(TS2+1);SJW = 1~4Tq;采样点在 BS1/BS2 之间,其余与规范相同。
- 波特率公式:TQ = (BRP+1) × T_PCLK;F103 的 CAN 挂在 APB1(PCLK1 = 36MHz):
波特率 = 36MHz ÷ (BRP+1) ÷ (1 + (TS1+1) + (TS2+1)) ——连除式,和定时器算频率一个套路。
12. 中断

| 中断 | 触发源 |
|---|---|
| 发送中断 | 发送邮箱空(如 RQCP0/1 置 1) |
| FIFO0 中断 | 收到报文(FMP0≠0) / 队满(FULL) / 溢出(FOV0) |
| FIFO1 中断 | 同 FIFO0,完全独立 |
| 状态改变错误中断 | 出错、唤醒、进入睡眠 |
信号流向:各标志位 → 与门(中断允许位 IE 当开关)→ 或门汇总 → NVIC 的 4 个中断向量(4 个中断函数)。分成 4 路的好处:各类事务独立处理,还能配不同优先级——配置中断时需注意。
13. 错误处理和离线恢复

TEC/REC 计数器的增减、主动错误 →(TEC/REC>127)→ 被动错误 →(TEC>255)→ 离线(总线关闭)→(128 次 11 个隐性位)→ 恢复主动错误。
STM32 的个性化设计:ABOM(自动离线恢复)位:
- ABOM=1:进入离线后自动开启恢复过程(与协议行为一致);
- ABOM=0:离线后不能直接恢复,必须软件先请求进入初始化再退出(INRQ 置 1 再清 0),之后检测到 128 次 11 个隐性位才恢复。
博客导航
本文来自博客园,作者:膝盖中箭卫兵,转载请注明原文链接:https://www.cnblogs.com/hai-feng-dev/p/23508575

浙公网安备 33010602011771号
https://orcid.org/0000-0001-5102-772X