从零到1详细剖析STM32的CAN架构【以STM32F407为例】
1 概览:bxCAN 在 STM32F407 中的位置与作用
- bxCAN(Basic extended CAN) 是 STM32F4 系列内部实现的 CAN 控制器硬件 IP,用来在物理 CAN 差分总线上收/发 CAN 帧(支持 CAN2.0A/2.0B)。bxCAN 负责仲裁、位填充、CRC、ACK、FIFO/邮箱管理等,极大减轻 CPU 负担。(STMicroelectronics)
- STM32F407 通常有 CAN1 和 CAN2 两个控制器(注意:CAN1是主设备,CAN2无法直接访问存储区域,因此使用CAN2的时候必须使能CAN1外设的时钟)。在 F4 系列中,滤波器-bank 在 CAN1/CAN2 之间分配(例如 28 个 bank 分割)。(community.st.com)
2 物理层
- STM32 只实现了 控制器(bxCAN),不包含物理收发器(transceiver)。因此必须外接常见的收发器芯片(例如 TJA1050、MCP2551 等),且需配用差分信号线(CAN_H,CAN_L)和两端的 120Ω 终端电阻。
- MCU 的 CAN 引脚需配置为复用 AF(Alternate Function),并保持合适的 IO 电气设置(推挽/开漏、上拉/下拉 )。
3 外设时钟与寄存器映射总结
- 启用外设:在 RCC_APB1ENR 中使能
CAN1EN(与 CAN2 相关的使能也在 RCC),确保 APB1 时钟给 CAN 外设。
- 重要寄存器组:
- 控制/状态/模式:
CAN_MCR、CAN_MSR
- 传输状态:
CAN_TSR
- 收发邮箱寄存器:
CAN_TI0R/TDTR/TDLR/TDHR(Tx mailbox0,1,2 对应)与 CAN_RI0R/RDTR/RDLR/RDHR(Rx)
- 接收 FIFO 状态:
CAN_RF0R、CAN_RF1R
- 中断/错误:
CAN_IER、CAN_ESR、CAN_ERR(ESR 包含 TEC/REC/LEC)
- 位时序:
CAN_BTR(BRP、SJW、TS1、TS2)
- 过滤器控制:
CAN_FMR、CAN_FM1R、CAN_FS1R、CAN_FFA1R、CAN_FA1R、滤波器寄存器数组(CAN_FilterRegister[x])。
4 模式与关键位
- 工作模式(通过
CAN_MCR / CAN_MSR 控制):
- 初始化模式(Initialization Mode,init)—— 在配置寄存器(位时序、滤波器)前必须进入 init。设
INRQ 并等待 INAK 被置位。(STMicroelectronics)
- 正常模式(Normal)—— 退出 init 后开始正常通信。
- 回环(Loopback)/静听(Silent/Listen-only)模式用于测试。
CAN_MCR 中常用控制位:
INRQ(请求进入初始化)、SLEEP、AWUM(自动唤醒)、ABOM(自动总线恢复/自动bus-off管理)、NART(禁止自动重发)、RFLM(Rx FIFO 锁模式)、TXFP(Tx FIFO 优先级或 Mailbox 优先)等。
5 位时序(Bit Timing)
CAN_BTR 决定比特率与采样点:包含 BRP(波特率预分频器)、SJW(同步跳宽 SJW)、BS1(TimeSeg1,PhaseSeg1)和 BS2(TimeSeg2,PhaseSeg2)。正确设置 BRP/BS1/BS2/SJW 对保证总线稳定和避免位错误至关重要。
6 发送(Tx)结构与流程(寄存器级)
- STM32 bxCAN 提供 3 个 Tx Mailboxes(Tx0,Tx1,Tx2),每个 mailbox 有 4 个寄存器:
TIR(Identifier)、TDTR(DLC+timestamp)、TDLR(data low)、TDHR(data high)。
- 发送步骤(寄存器级伪流程):
- 在可用邮箱上检查
CAN_TSR 的 TME(Tx mailbox empty)位或 CAN_TI[x]R.TXREQ。选择空的 mailbox。
- 配置
TIR:设置 IDE、RTR、并写入 11-bit 或 29-bit ID 到相应位域。
- 配置
TDTR:写入 DLC(0~8),可以包含时间戳相关位(若使用)。
- 写数据到
TDLR 和 TDHR(每寄存器 4 字节)。
- 发起发送:在
TIR 写入 TXRQ(Transmit Request)位。硬件将开始仲裁并在总线上发送。
- 发送完成/状态:查询
CAN_TSR 或中断(CAN_IER)查看 RQCPx / TXOK 等位,清除相应标志并释放 mailbox(硬件自动清 TXRQ 或需软件清某些位)。
7 接收(Rx)结构与流程(寄存器级)
- 接收使用 FIFO0 与 FIFO1,滤波器决定把哪条消息送到哪个 FIFO。每个 FIFO 的状态由
CAN_RF0R / CAN_RF1R 管理(包含 FMP0/1 = FIFO message pending)。
- 接收步骤(寄存器级伪流程):
- 检查
CAN_RF0R.FMP0(或 FIFO1)是否 >0(有消息)。
- 读取
RIR(ID、IDE、RTR)、RDTR(DLC、时间戳)、RDLR、RDHR(数据)。
- 处理完后,释放该 FIFO 消息:在
CAN_RF0R 写入 RFOM0(release FIFO output mailbox)位(或相应位),FIFO 指针前移。
- 可通过中断(
FMPIE0/FMPIE1)在新消息到达时触发 CPU。
8 过滤器(Filter)机制(STM 的特色)
- STM32 的 bxCAN 提供灵活的 硬件滤波器 bank(例如 28 个 bank,在 CAN1/CAN2 之间分配)。每个滤波器可以配置为 16-bit list / 16-bit mask / 32-bit list / 32-bit mask(scale),并可选择将匹配消息放入 FIFO0 或 FIFO1。配置前需将 Filter Master 置入初始化(在
CAN_FMR 设置 FINIT)。
9 错误处理与状态
CAN_ESR 提供错误统计与类型:TEC(Transmit Error Counter)、REC(Receive Error Counter)、LEC(Last Error Code)等。依计数器,节点可处于 Error Active、Error Passive 或 Bus-Off。当发生严重错误,硬件可自动发送 Error Flag(错误帧),并进入错误恢复(若启用 ABOM 等)。(STMicroelectronics)
10 中断与事件
CAN_IER:可使能的中断包括 Tx mailbox empty / Tx OK / Rx FIFO message pending / Error / Wake-up / Sleep ack 等。
11 常见配置坑与注意事项(实践经验)
- 必须在初始化模式下配置位时序与滤波器,否则设置不会生效(先写
INRQ,等待 INAK)。(STMicroelectronics)
- 滤波器组(Filter Bank)分配:当使用 CAN2 时要注意滤波器分配(通常 lower banks 分给 CAN1,upper banks 给 CAN2,需在 FMR 中配置 split)。若配置不当会导致 CAN2 无法接收消息。
- GPIO 与收发器:IO 上拉/下拉、速率及终端必须按硬件参考设计做,不正确的物理连接会导致总线抖动/错误帧频发。
- NART 位:若设了 NART(禁止自动重发),发送失败将不会重试;用于诊断时方便。
- ABOM 位:若启用自动 bus-off 管理,MCU 能在 bus-off 后自动恢复,生产系统中请根据需求慎重选择。
- 时钟源:确保 APB1 的频率与 BRP 配比能得到所需比特率(1Mbps、500kbps 等)。若 BRP 取整后误差过大,会导致位错误。
12 寄存器级伪代码
// 1. 使能 RCC APB1 CAN 时钟
RCC->APB1ENR |= RCC_APB1ENR_CAN1EN; // 若使用 CAN2, 也启 CAN2EN(视芯片)// 2. 进入初始化模式
CAN1->MCR |= (1<<0); // INRQ
while(!(CAN1->MSR & (1<<0))) {} // wait INAK set// 3. 配置位时序:BRP, SJW, BS1, BS2(例:1Mbps 需计算 BRP)
CAN1->BTR = (SJW<<24)|(BS1<<16)|(BS2<<20)|(BRP-1);// 4. 配置过滤器 (进入 filter init)
CAN1->FMR |= (1<<0); // FINIT
// 设置 filter bank i:模式(16/32bit list/mask), FIFO assignment, ID/mask 值...
// 例如 CAN1->sFilterRegister[0].FR1 = ...; CAN1->sFilterRegister[0].FR2 = ...;
CAN1->FMR &= ~(1<<0); // 出 filter init (FINIT=0)// 5. 配置 MCR 其他位:ABOM/AWUM/NART/RFLM/TXFP as needed
CAN1->MCR |= (ABOM|AWUM|...);// 6. 退出初始化(normal mode)
CAN1->MCR &= ~INRQ;
while(CAN1->MSR & INAK) {} // wait cleared// 7. 发送一帧(寄存器级)
if (CAN1->TSR & TME0) { // mailbox0 emptyCAN1->sTxMailBox[0].TIR = (IDE?ID_29bit:ID_11bit) | (RTR?RTR_BIT:0);CAN1->sTxMailBox[0].TDTR = (DLC & 0xF);CAN1->sTxMailBox[0].TDLR = data_low32;CAN1->sTxMailBox[0].TDHR = data_high32;CAN1->sTxMailBox[0].TIR |= TXRQ; // 请求发送
}// 8. 接收(poll 或 ISR)
if (CAN1->RF0R & FMP0) {id = CAN1->sFIFOMailBox[0].RIR;dlc = CAN1->sFIFOMailBox[0].RDTR & 0xF;data_low = CAN1->sFIFOMailBox[0].RDLR;data_high = CAN1->sFIFOMailBox[0].RDHR;CAN1->RF0R |= RFOM0; // 释放 FIFO0 输出
}
13 调试技巧与建议
- 先用 loopback 模式或静听模式在板上自测,不用外接收发器即可验证寄存器配置与位时序。
- 若两片 STM32 互联测试,先用单端(同板) loopback,再连收发器到真实总线做全链路测试。
- 若收不到数据,检查:GPIO AF 配置→收发器供电→终端电阻→滤波器 bank 分配(CAN2 特别容易错)→是否在 init 模式配置滤波器。
- 观察总线波形(示波器)验证边沿干净、终端与差分对称性;若抖动多,降低波特率或检查物理接线。