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 外设基础参数,包括:
工作模式
自动重传
自动唤醒
自动离线管理
位时序分频
BS1BS2SJW
其中位时序参数直接决定 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 报文中断中建议尽量只做数据搬运和状态标记,复杂解析留到主循环或任务中处理。
