第二章:STM32 bxCAN 控制器详解:从内存到总线的“中转站”

STM32 内部集成的 CAN 控制器被称为 bxCAN (Basic Extended CAN)。它的设计目标是以最小的 CPU 负荷来处理大量的报文收发。我们要想写出高效的代码,必须先理解它内部的"物流体系"。

1. 核心架构:三大核心组件

bxCAN 外设主要由以下三个部分组成:

  • 发送邮箱 (Transmit Mailboxes): 用于暂存等待发送的报文。

  • 接收 FIFO (Receive FIFOs): 用于暂存从总线上接收到的、且通过了过滤器筛选的报文。

  • 验收筛选器 (Acceptance Filters): 这是 CAN 的"安检员",决定哪些报文该进 FIFO,哪些该直接丢弃。

1.1 发送邮箱:谁先发,硬件说了算

STM32 提供了 3 个发送邮箱。这意味着 CPU 可以一次性"一口气"丢给 CAN 控制器 3 条报文。

  • 优先级判定: 如果 3 个邮箱都塞满了,硬件如何决定发送顺序?

    • ID 优先级: 默认情况下,CAN 硬件会根据报文的标识符(ID)来决定。ID 越小(显性位多),优先级越高,越先被推向总线。

    • 时间顺序: 你也可以通过设置 CAN_MCR 寄存器的 TXFP 位,强制让邮箱按照"先进先出(FIFO)"的顺序发送。

  • 工程陷阱: 如果你的系统中存在大量高频率、低优先级的报文,可能会导致高优先级的邮箱一直被挂起。理解邮箱调度是解决"总线拥堵"的第一步。

1.2 接收 FIFO:防止丢包的缓冲带

STM32 有 2 个接收 FIFO(FIFO0 和 FIFO1) ,每个 FIFO 都有 3 级深度

这意味着,即便 CPU 忙于处理其他中断,暂时没空理会 CAN,硬件也能帮你缓存 6 条(2x3)完整的报文。

  • 溢出风险: 如果 3 个位置都占满了,第 4 条报文又来了,会发生什么?

    • 默认:新报文会被丢弃。

    • 可配置:新报文覆盖旧报文(RFLM 位配置)。

    • 建议: 永远通过中断方式及时把 FIFO 里的数据读走,不要在主循环里轮询,这是保证不丢包的金科玉律。

2. 时间份额(Time Quantum)与波特率的艺术

这是本章最硬核的部分。很多新手配置 CAN 失败,90% 都是因为波特率算错了。CAN 协议不是简单的"位传输",它把一个"位(Bit)"拆成了多个细小的时间份额(Tq)

2.1 位时间(Bit Time)的四个段

一个完整的位时间由以下四部分组成:

  1. 同步段 (Sync_Seg): 固定为 1 个 Tq。用于信号同步。

  2. 传播段 (Prop_Seg): 补偿电缆中的物理延迟。

  3. 相位缓冲段 1 (Phase_Seg1): 用于补偿信号边缘的相位提前。

  4. 相位缓冲段 2 (Phase_Seg2): 用于补偿信号边缘的相位滞后。

在 STM32 的 HAL 库中,传播段相位段 1 被合并成了 TimeSeg1(TS1),相位段 2 被称为 TimeSeg2(TS2)。

2.2 采样点(Sample Point):决定稳定性的生死线

采样点是控制器读取总线电平的时刻,它位于 TS1 结束、TS2 开始的地方。

  • 采样点计算公式: SamplePoint = (1 + TS1) / (1 + TS1 + TS2)

  • 工业标准: * 对于 500k、1M 的高速 CAN,建议采样点在 75% ~ 80%

    • 对于低速或超长距离,采样点可以适当靠后。

    • 如果采样点设置不一致: 不同的设备之间即便波特率一样,也可能出现周期性的 CRC 错误或丢包。

2.3 实战:手把手教你计算波特率

假设你使用的是 STM32F103 ,APB1 时钟(f_pclk1)为 36MHz ,你需要配置 500kbps 的波特率。

  1. 确定分频系数 (Prescaler): 我们先试分频系数为 4。

    Tq = Prescaler / f_pclk1 = 4 / 36,000,000 = 111.11 ns

  2. 确定位总时长:

    一个位的总时长 = 1 / 500,000 = 2000 ns。

    总 Tq 数 = 2000 / 111.11 = 18 个 Tq。

  3. 分配 TS1 和 TS2:

    根据"总 Tq = 1 (Sync_Seg) + TS1 + TS2 = 18",得出 TS1 + TS2 = 17。

    为了让采样点接近 80%:

    设 TS1 = 13,TS2 = 4。

    采样点 = (1+13) / 18 = 77.7% 完美!

3. STM32 CAN 的工作模式:调试中的"救命稻草"

bxCAN 有四种工作模式,在开发专栏时,一定要教会读者善用它们。

  • 正常模式 (Normal): 接入总线,收发自如。

  • 回环模式 (Loopback): 控制器发出的数据不经过物理总线,直接在芯片内部传给接收端。

    • 用途: 你的代码死活收不到数?先切到回环模式。如果能收到,说明软件逻辑和波特率配置是对的,问题出在收发器、线缆或电阻上。
  • 静默模式 (Silent): 只收不发。

    • 用途: 也就是"监听模式"。在不干扰总线的前提下抓包。
  • 静默回环模式: 用于自我测试,不干扰总线。

4. 软件实现:基于 HAL 库的初始化模板

在这一节,我们要展示如何将上述理论转化为代码。

复制代码
CAN_HandleTypeDef hcan;

void MX_CAN_Init(void) {
    hcan.Instance = CAN1;
    hcan.Init.Prescaler = 4;           // 分频系数
    hcan.Init.Mode = CAN_MODE_NORMAL;  // 正常模式
    hcan.Init.SyncJumpWidth = CAN_SJW_1TQ; 
    hcan.Init.TimeSeg1 = CAN_BS1_13TQ; // TS1 = 13
    hcan.Init.TimeSeg2 = CAN_BS2_4TQ;  // TS2 = 4
    hcan.Init.TimeTriggeredMode = DISABLE;
    hcan.Init.AutoBusOff = ENABLE;     // 自动离线恢复(重要!)
    hcan.Init.AutoWakeUp = DISABLE;
    hcan.Init.AutoRetransmission = ENABLE; // 自动重发
    hcan.Init.ReceiveFifoLocked = DISABLE;
    hcan.Init.TransmitFifoPriority = DISABLE;

    if (HAL_CAN_Init(&hcan) != HAL_OK) {
        Error_Handler();
    }
}

重点参数解析:AutoBusOff = ENABLE

在工业现场,如果总线受到强干扰,CAN 核心可能会因为错误计数器溢出而进入"Bus-Off(离线)"状态。

  • 如果设为 DISABLE,你需要手动写代码去重启 CAN 模块。

  • 如果设为 ENABLE ,一旦总线空闲,硬件会自动尝试重新加入网络。这对于提高系统的自愈能力至关重要。

5. 常见工程痛点与避坑指南

5.1 时钟源的坑

STM32 的 CAN 通常挂在 APB1 总线上。注意,F4 或 H7 系列的时钟树非常复杂,APB1 的频率可能并不是主频。配置波特率前,必须确认 HAL_RCC_GetPCLK1Freq() 的真实返回值。

5.2 忘记启动 CAN

很多初学者调用了 HAL_CAN_Init 后就去发数据,结果报错。

记住:STM32 的 CAN 初始化后处于"初始化模式",必须调用 HAL_CAN_Start(&hcan) 才能进入工作状态。

5.3 滤波器没开,万事皆空

即便你只想接收所有数据,也必须至少配置并激活一个过滤器。如果没有配置过滤器,STM32 会默认拦截总线上所有的报文,你的 FIFO 永远是空的。(这是我们下一章要深挖的主题)。

本章总结

理解了邮箱、FIFO 以及精细到 Tq 的时间段配置,你就已经掌握了 STM32 CAN 的"内功"。波特率不再是盲目尝试的数字,而是基于采样点和时钟频率的严谨推导。

相关推荐
jucat2 小时前
定时排气扇
嵌入式硬件
Dunkle.T3 小时前
DC-DC PCB设计要点说明——拓扑、走线、选型、铺铜详解
单片机·嵌入式硬件·pcb·dc-dc
Lugas Luo3 小时前
DVR 存储工具深度分析报告 (测试与产品视角)
linux·嵌入式硬件·测试工具
charlie1145141913 小时前
嵌入式C++工程实践——第13篇:第一次重构 —— enum class取代宏,类型安全的开始
开发语言·c++·vscode·stm32·安全·重构·现代c++
JAVA+C语言4 小时前
R+Python 双语言开发首选:RStudio 2025 下载安装详细教程
性能优化·软件工程
菜鸟的学习日记、4 小时前
电气元件介绍(一)——电阻
嵌入式硬件·上拉电阻·下拉电阻·电阻·电气元件
三品吉他手会点灯4 小时前
STM32F103 学习笔记-21-串口通信(第4节)—串口发送和接收代码讲解(上)
笔记·stm32·单片机·嵌入式硬件·学习
weifengdq5 小时前
SJA1124 SPI转4路LIN STM32 测试笔记
stm32·spi·nxp·lin·sja1124·spi4lin
史蒂芬_丁5 小时前
STM32平台原子操作详解:原理与实践
stm32·单片机·嵌入式硬件