STM32-CAN
一、CAN 是什么
CAN 是一种面向现场总线的串行通信协议。和普通 UART 点对点通信不同,CAN 更强调多个节点挂在同一条总线上进行可靠通信。
CAN 总线通常由以下部分组成:
-
CAN 控制器:芯片内部的 CAN 外设,例如 STM32 内部的 bxCAN
-
CAN 收发器:外部电平转换芯片,例如 TJA1050、SIT1050T 等
-
CAN_H 和 CAN_L:两根差分信号线
-
终端电阻:用于阻抗匹配,减少信号反射
STM32 内部 CAN 外设负责协议层处理,比如报文收发、仲裁、过滤、错误管理等;外部 CAN 收发器负责把控制器的逻辑信号转换成 CAN_H/CAN_L 差分信号。
常见 CAN 类型可以简单分为:
| 类型 | 速率范围 | 典型特点 |
|---|---|---|
| 低速 CAN | 10Kbps ~ 125Kbps |
通信距离较远,低速场景使用 |
| 高速 CAN | 125Kbps ~ 1Mbps |
经典 CAN 常用形式,应用最广 |
| CAN FD | 可达数 Mbps | 数据段速率更高,兼容经典 CAN 的部分机制 |
对于 STM32F1 这类带 bxCAN 的芯片,主要学习和使用的是经典 CAN,也就是 CAN 2.0A / CAN 2.0B。
二、CAN 总线特点
CAN 总线有几个非常重要的特点:
1. 多主控制
CAN 总线不是传统“一主多从”的结构。每个节点在总线空闲时都可以主动发送数据。
这意味着 CAN 总线上没有固定主机,任何节点都可以成为发送节点,也可以成为接收节点。
2. 无节点地址,靠报文 ID 区分数据
CAN 报文里没有类似 IIC 从机地址那样的设备地址。CAN 更关注“这条消息是什么”,而不是“这条消息发给谁”。
接收节点通过报文 ID 判断自己是否关心这条消息。这样新增节点时,通常不会破坏原有总线结构。
3. 速度快,距离远
CAN 使用差分信号传输,抗干扰能力强。通信速率和传输距离需要平衡:
-
速率越高,可稳定传输的距离通常越短
-
距离越远,通常需要降低速率并做好布线和终端匹配
4. 错误处理能力强
CAN 协议内置错误检测、错误通知和错误恢复机制。节点可以检测位错误、CRC 错误、格式错误、应答错误等,并通过错误帧通知其他节点。
这也是 CAN 适合汽车和工业现场的重要原因。
5. 支持较多节点
CAN 总线可以连接多个节点,但节点数量、通信速度、线缆长度和收发器性能之间需要综合权衡。
三、CAN 物理层:差分信号与显性/隐性电平
CAN 使用 CAN_H 和 CAN_L 两根线进行差分传输。接收端根据两根线之间的电压差判断总线电平。
CAN 总线电平分为两种:
| 总线电平 | 逻辑值 | 特点 |
|---|---|---|
| 显性电平 | 逻辑 0 | 优先级高,可以覆盖隐性电平 |
| 隐性电平 | 逻辑 1 | 总线空闲时通常处于隐性状态 |
显性电平具有优先权,这一点非常关键。它使 CAN 可以实现非破坏性仲裁:多个节点同时发送时,低 ID 报文可以继续发送,其他节点自动退出,不会把总线数据撞坏。
高速 CAN 和低速 CAN 的物理拓扑不同:
-
低速 CAN 通常属于开环总线
-
高速 CAN 通常属于闭环总线,两端需要终端电阻
实际硬件设计中,高速 CAN 总线两端常接 120Ω 终端电阻,用于阻抗匹配,减少回波反射。
四、CAN 收发器
STM32 的 CAN 控制器不能直接连接 CAN_H/CAN_L 总线,需要外接 CAN 收发器。常见芯片包括:
-
TJA1050
-
TJA1042
-
SIT1050T
以 SIT1050T / TJA1050 这类高速 CAN 收发器为例,常见引脚含义如下:
| 引脚 | 作用 |
|---|---|
| D / TXD | CAN 发送输入端,连接 CAN 控制器发送引脚 |
| R / RXD | CAN 接收输出端,连接 CAN 控制器接收引脚 |
| CANH | 高电位 CAN 总线端 |
| CANL | 低电位 CAN 总线端 |
| Vref | 参考电压输出 |
| RS | 模式控制,可用于高速或静音模式 |
简单理解:
STM32 bxCAN_TX -> CAN 收发器 D/TXD -> CAN_H/CAN_L 总线
STM32 bxCAN_RX <- CAN 收发器 R/RXD <- CAN_H/CAN_L 总线
CAN 控制器处理协议,CAN 收发器处理电气信号,两者配合才能完成真实总线通信。
五、CAN 协议层:5 种帧类型
CAN 总线以“帧”为单位进行通信。CAN 协议定义了 5 种帧:
| 帧类型 | 作用 |
|---|---|
| 数据帧 | 发送节点向接收节点传输数据,最常用 |
| 遥控帧 | 接收节点向具有相同 ID 的发送节点请求数据 |
| 错误帧 | 节点检测到错误时通知其他节点 |
| 过载帧 | 接收节点通知自己暂时没有准备好接收 |
| 间隔帧 | 用于分隔数据帧或遥控帧 |
日常应用里最常见的是数据帧。
六、CAN 数据帧结构
CAN 数据帧由 7 个段组成:
| 数据帧字段 | 作用 |
|---|---|
| 帧起始 | 表示数据帧开始,使用显性信号 |
| 仲裁段 | 包含报文 ID 和 RTR 位,用于决定总线优先级 |
| 控制段 | 表示数据长度和保留位 |
| 数据段 | 真正传输的数据,经典 CAN 一帧可发送 0~8 字节 |
| CRC 段 | 用于检测帧传输错误 |
| ACK 段 | 接收节点确认正确接收 |
| 帧结束 | 表示数据帧结束,通常为 7 个隐性位 |
CAN 数据帧又分为标准帧和扩展帧:
| 帧格式 | 标识符长度 | 对应版本 |
|---|---|---|
| 标准帧 | 11 位 ID | CAN 2.0A |
| 扩展帧 | 29 位 ID | CAN 2.0B |
STM32 bxCAN 支持 CAN 2.0A 和 CAN 2.0B Active,因此可以处理标准数据帧和扩展数据帧。
七、CAN 位时序
CAN 通过位时序机制完成采样和同步。一个 CAN 位时间由多个时间量子 Tq 组成。
经典 CAN 协议中,一个位时间可以理解为以下几段:
| 时间段 | 含义 |
|---|---|
| SS | 同步段,通常为 1Tq |
| PTS | 传播时间段,用于补偿总线传播延迟 |
| PBS1 | 相位缓冲段 1 |
| PBS2 | 相位缓冲段 2 |
在 STM32 bxCAN 中,位时序配置常见为 3 个部分:
| bxCAN 字段 | 对应理解 |
|---|---|
SYNC_SEG |
同步段,固定为 1Tq |
BS1 |
相当于协议中的 PTS + PBS1 |
BS2 |
相位缓冲段 2 |
CAN 波特率计算可以按下面的思路理解:
CAN波特率 = CAN时钟 / 分频系数 / (1 + BS1 + BS2)
其中 1 对应同步段 SYNC_SEG。
例如 CAN 时钟为 36MHz,分频系数为 9,BS1 = 5Tq,BS2 = 2Tq,则:
波特率 = 36MHz / 9 / (1 + 5 + 2)
= 500Kbps
位时序不仅影响波特率,也会影响采样点位置。采样点就是 CAN 控制器读取总线电平并把它判定为当前位值的位置。
八、CAN 同步机制
CAN 总线上的每个节点都有自己的时钟。由于晶振误差、线缆传播延迟等因素,不同节点对同一位的理解可能出现偏移,所以 CAN 需要同步机制。
CAN 同步可以分为:
1. 硬同步
节点检测到帧起始边沿时,会将自己的同步段调整到该边沿位置,从而让内部位时序和总线重新对齐。
2. 再同步
在普通数据位传输过程中,如果检测到边沿偏移,CAN 控制器会通过调整 PBS1 或 PBS2 进行再同步。
再同步调整的最大范围由 SJW 决定。SJW 的范围通常为 1~4Tq。
SJW 越大,吸收时钟误差和相位偏差的能力越强;但配置也要配合整体位时序,不能脱离通信速率和采样点随意设置。
九、CAN 总线仲裁
CAN 支持多个节点同时尝试发送数据。为了避免总线冲突,CAN 使用基于报文 ID 的仲裁机制。
仲裁规则可以概括为:
-
总线空闲时,最先开始发送的节点获得发送权
-
多个节点同时发送时,从仲裁段的第一位开始比较
-
显性电平
0优先于隐性电平1 -
谁先发送隐性电平但检测到总线为显性电平,谁就失去仲裁
-
失去仲裁的节点自动转为接收状态,并在总线空闲后再次尝试发送
由于显性位可以覆盖隐性位,因此 CAN 仲裁不会破坏正在发送的高优先级报文,这叫非破坏性仲裁。
报文 ID 数值越小,仲裁时越容易保持显性位,因此优先级越高。
十、STM32 bxCAN 控制器
STM32 中常见的 CAN 外设叫 bxCAN。它支持 CAN 2.0A 和 CAN 2.0B Active 协议。
bxCAN 的主要特点:
-
最高波特率可达
1Mbps -
支持标准帧和扩展帧
-
支持时间触发通信
-
具有 3 个发送邮箱
-
具有 2 个接收 FIFO,每个 FIFO 具备 3 级深度
-
支持可变过滤器组,部分系列最多 28 个过滤器组
-
支持正常、睡眠、初始化等工作模式
-
支持静默、环回、环回静默等测试模式
1. 工作模式
| 工作模式 | 作用 |
|---|---|
| 初始化模式 | 用于配置 CAN 寄存器 |
| 正常模式 | CAN 与总线同步,开始收发数据 |
| 睡眠模式 | 降低功耗 |
2. 测试模式
| 测试模式 | 作用 |
|---|---|
| 静默模式 | 只监听总线,不主动影响总线,适合统计总线流量 |
| 环回模式 | 发送数据直接回到接收路径,适合自检 |
| 环回静默模式 | 内部自收自发且不影响外部总线 |
学习 CAN 时,环回模式很适合先验证 CAN 外设配置是否正确;真正接入总线前,再切换到正常模式。
十一、bxCAN 内部结构
bxCAN 内部主要由以下部分组成:
| 模块 | 作用 |
|---|---|
| CAN 内核 | 包含控制、状态、配置寄存器,可配置模式和波特率 |
| 发送邮箱 | 缓存待发送报文,最多可缓存 3 个 |
| 接收 FIFO | 缓存接收到的有效报文 |
| 接收过滤器 | 判断哪些报文可以进入接收 FIFO |
发送过程大致是:
应用层准备报文
-> 写入发送邮箱
-> bxCAN 根据总线状态参与仲裁
-> 仲裁成功后发送到 CAN 总线
接收过程大致是:
CAN 总线收到报文
-> bxCAN 检查帧格式和 CRC
-> 通过过滤器筛选
-> 合格报文进入接收 FIFO
-> 软件读取 FIFO 中的报文
十二、CAN 接收过滤器
CAN 总线上所有节点都能看到总线上的报文。如果每条报文都交给软件处理,CPU 压力会很大。因此 bxCAN 提供硬件接收过滤器。
每个过滤器组都有两个 32 位寄存器 CAN_FxR1 和 CAN_FxR2。过滤器的具体含义由两类配置决定:
-
位宽:16 位或 32 位
-
模式:屏蔽位模式或标识符列表模式
1. 屏蔽位模式
屏蔽位模式用于“匹配一类 ID”。
可以理解为:
FilterId :希望匹配的 ID
FilterMask :哪些位需要比较,哪些位可以忽略
这种模式适合接收一段范围或一类规则相近的报文。
2. 标识符列表模式
标识符列表模式用于“精确匹配某些 ID”。
适合只接收几个固定 ID 的场景,例如只关心 0x100、0x101、0x200 这类指定报文。
3. FIFO 关联
过滤器命中的报文可以配置进入 FIFO0 或 FIFO1。项目中可以按消息类型划分:
-
重要实时消息进入 FIFO0
-
普通状态消息进入 FIFO1
这样软件处理时会更清晰。
十三、CAN 常用寄存器
理解 HAL 库时,知道关键寄存器的职责很有帮助。
| 寄存器 | 作用 |
|---|---|
CAN_MCR |
CAN 主控制寄存器,主要配置工作模式 |
CAN_BTR |
位时序寄存器,配置分频、BS1、BS2、SJW 和测试模式 |
CAN_(T/R)IxR |
标识符寄存器,存放发送或接收报文 ID、IDE、RTR 等 |
CAN_(T/R)DTxR |
数据长度和时间戳寄存器,保存 DLC 等信息 |
CAN_(T/R)DLxR |
低位数据寄存器,保存 Data0~Data3 |
CAN_(T/R)DHxR |
高位数据寄存器,保存 Data4~Data7 |
CAN_FM1R |
过滤器模式寄存器,设置列表模式或屏蔽位模式 |
CAN_FS1R |
过滤器位宽寄存器,设置 16 位或 32 位 |
CAN_FFA1R |
FIFO 关联寄存器,设置过滤后进入哪个 FIFO |
CAN_FA1R |
过滤器激活寄存器,开启对应过滤器组 |
CAN_FxR1 / CAN_FxR2 |
过滤器组寄存器,保存具体过滤规则 |
实际开发时不一定直接操作这些寄存器,但它们能帮助理解 HAL 初始化结构体背后的含义。
十四、CAN 常用 HAL 接口
STM32 HAL 库中,CAN 常用接口如下:
| HAL 接口或宏 | 作用 |
|---|---|
__HAL_RCC_CANx_CLK_ENABLE |
使能 CAN 外设时钟 |
HAL_CAN_Init |
初始化 CAN 外设 |
HAL_CAN_ConfigFilter |
配置 CAN 接收过滤器 |
HAL_CAN_Start |
启动 CAN 外设 |
HAL_CAN_ActivateNotification |
使能 CAN 通知中断 |
__HAL_CAN_ENABLE_IT |
使能指定 CAN 中断 |
HAL_CAN_AddTxMessage |
添加待发送报文到发送邮箱 |
HAL_CAN_GetTxMailboxesFreeLevel |
查询空闲发送邮箱数量 |
HAL_CAN_GetRxFifoFillLevel |
查询接收 FIFO 中的报文数量 |
HAL_CAN_GetRxMessage |
从接收 FIFO 读取报文 |
常见结构体:
| 结构体 | 作用 |
|---|---|
CAN_InitTypeDef |
配置 CAN 工作模式、位时序、自动重传等 |
CAN_FilterTypeDef |
配置接收过滤器 |
CAN_TxHeaderTypeDef |
配置发送报文头 |
CAN_RxHeaderTypeDef |
保存接收报文头 |
十五、CAN 基本驱动配置思路
CAN 的配置流程可以按下面几步理解。
1. 初始化 CAN 参数
通过 HAL_CAN_Init 配置 CAN 外设基础参数,包括:
-
工作模式
-
自动重传
-
自动唤醒
-
自动离线管理
-
位时序分频
-
BS1 -
BS2 -
SJW
其中位时序参数直接决定 CAN 波特率,必须和总线上其他节点一致。
2. 初始化底层硬件
在 HAL_CAN_MspInit 中通常完成:
-
使能 CAN 外设时钟
-
使能对应 GPIO 时钟
-
配置 CAN_TX 引脚为复用推挽输出
-
配置 CAN_RX 引脚为输入或复用输入
-
如需中断,配置 NVIC
如果使用外部收发器,还要保证 CAN 收发器供电、模式控制引脚和终端电阻配置正确。
3. 配置接收过滤器
通过 HAL_CAN_ConfigFilter 配置硬件过滤器。至少需要保证目标报文能够通过过滤器进入 FIFO,否则总线上有数据也可能读不到。
调试初期可以先配置成接收所有 ID,确认通信链路正常后,再收紧过滤规则。
4. 启动 CAN 外设
调用 HAL_CAN_Start 后,CAN 才真正进入可通信状态。
如果只是初始化了参数但没有启动外设,发送和接收都不会正常进行。
5. 发送和接收数据
发送时通常使用:
HAL_CAN_AddTxMessage
接收时通常使用:
HAL_CAN_GetRxFifoFillLevel
HAL_CAN_GetRxMessage
如果使用中断方式,还需要开启通知并实现对应回调函数。
6. 可选:使能中断
如果项目需要及时接收报文,可以使能 FIFO 消息挂起中断。典型流程是:
HAL_CAN_ActivateNotification
-> NVIC 配置
-> 中断回调中读取 FIFO 报文
中断中建议尽量只做数据搬运和状态标记,复杂解析留到主循环或任务中处理。
更多推荐


所有评论(0)