最近修改日期:2026-01-29
预计阅读时间:8-10 分钟
参与者:Jackrainman
CAN 总线通信原理与软件驱动架构
0. 引言
如果你从来没有了解过这个概念,这个视频能帮助你更好地了解本文档:https://www.bilibili.com/video/BV14k4y187e6/
1. 概述
CAN (Controller Area Network) 是一种广泛应用于汽车与工业控制的串行通信协议。在机器人开发中,CAN 总线因其走线简单(节省线束)和极高的抗干扰能力,成为电控系统(ECU)之间通信的首选方案。
节省线束的优势: CAN 总线最开始是为了减少铜线的长度而开发的。如果不使用 CAN 总线,每个 ECU 之间都需要单独的连线,这将使用数倍长的铜线,而且线束会非常杂乱。通过 CAN 总线,多个 ECU 只需要挂载到总线上就可以组成局域网通讯,大大减少了线束的长度和复杂度。
通信系统由三个层级构成:
- 物理层(硬件):包括单片机、CAN 收发器与双绞线。
- 数据链路层(控制器):STM32 内部集成的 bx CAN 或 FDCAN 外设。
- 应用层(软件):基于
can_list.c驱动的协议解析与业务处理。
1.1 核心术语表
| 术语 | 全称 / 含义 | 说明 |
|---|---|---|
| CAN | Controller Area Network | 控制器局域网络,一种多主串行通信协议 |
| ECU | Electronic Control Unit | 电子控制单元,可以看作是一台超小型的计算机,它内部集成了供电系统、单片机驱动系统,是汽车里面最小的控制模块 |
| bx CAN | Basic Extended CAN | STM32 传统的 CAN 外设模块,遵循 CAN 2.0B 协议 |
| FDCAN | Flexible Data-rate CAN | STM32 高性能 CAN 外设,支持更高数据速率和更长数据帧 |
| CAN_H / CAN_L | CAN High / Low | 差分信号线对,用于传输数据 |
| 显性电平 | Dominant Level | 逻辑 0,CAN_H=3.5V,CAN_L=1.5V,电压差约 2V |
| 隐性电平 | Recessive Level | 逻辑 1,CAN_H=CAN_L≈2.5V,电压差接近 0V |
| ID | Identifier | 报文标识符,用于标识消息内容和优先级 |
| 标准帧 | Standard Frame | 11 位 ID,最大数据长度 8 字节 |
| 扩展帧 | Extended Frame | 29 位 ID,最大数据长度 8 字节 (bx CAN) 或 64 字节 (FDCAN) |
| DLC | Data Length Code | 数据长度代码,指示数据字段的字节数 |
| FIFO | First In First Out | 先进先出队列,硬件接收缓冲区 |
| ISR | Interrupt Service Routine | 中断服务程序,硬件事件触发时执行的紧急代码 |
| HAL | Hardware Abstraction Layer | 硬件抽象层库,提供统一硬件操作接口 |
| RTOS | Real-Time Operating System | 实时操作系统,如 FreeRTOS |
| 哈希表 | Hash Table | 一种通过哈希函数快速查找的数据结构 |
| 掩码 | Mask | 位掩码,用于过滤 ID 中不关心的位 |
| 回调函数 | Callback Function | 预先定义的函数,在特定事件发生时由系统自动调用 |
2. CAN 协议基础
2.1 报文帧类型
CAN 协议定义了四种帧类型:
-
数据帧:携带实际数据,包含以下部分:
- 仲裁场:ID + RTR 位
- 控制场:IDE 位 + 保留位 + DLC
- 数据场:0-8/64 字节数据
- CRC 场:循环冗余校验码
- 应答场:ACK 位 + EOF
详细说明: - 起始位:必须是逻辑 0,标识帧的开始 - 识别码(ID):根据这 11 位识别码就能知道这一帧信息是发给哪一个设备的,每一个设备都有属于自己的 11 位识别码。ID 不仅是设备的唯一识别码,而且还代表了优先级。 - IDE 位:用于区分标准格式和扩展格式。在标准格式当中有 11 位识别码,这一位是逻辑 0;而在扩展格式中,它的识别码有 29 位,这一位是逻辑 1。 - 保留位:逻辑 0,用于未来扩展 - DLC 位(数据长度代码):4 位,二进制编码范围是 0~8。如果 DLC=1,则后面的数据位就只有 1 个字节(8 位);如果 DLC=8,则后面的数据位就是 8 个字节(64 位)。 - CRC 码:16 位循环冗余校验码,用于确保数据的准确性。首先是 15 位 CRC 校验码,设备接收端会根据数据计算出它的 CRC 位,如果计算出来的和接收到的 CRC 不一致,说明数据存在问题,就会重新发送一遍数据帧。 - CRC 界定符:逻辑 1,目的是为了把后面的信息隔开 - ACK 应答位:第一位是 ACK 确认槽,发送端发送的是逻辑 1,而接收端回复的是逻辑 0 来表示应答。第二位是 ACK 界定符,一定是逻辑 1,作用是把后面的数据隔开 - 7 位结束位(EOF):这 7 位都是逻辑 1,表示数据帧传输结束
-
远程帧:请求特定 ID 的数据,不含数据场
- 错误帧:节点检测到错误时发送
- 过载帧:请求延迟下一帧传输
目前代码中只有数据帧与远程帧,并没有错误帧和过载帧。远程帧简单来说,是用来请求数据的。
2.2 仲裁机制
CAN 采用非破坏性位仲裁: - 所有节点同时发送时,逐位比较 ID - 发送显性位(0)的节点覆盖隐性位(1) - ID 值越小优先级越高(显性位多) - 优先级高的节点继续发送,其他节点转为接收模式
仲裁详细过程: 当总线上同时有多个设备发送信息时,哪一个设备发送的信息优先呢?这就得看 11 位的识别码了,它不仅是设备的唯一识别码,而且还代表了优先级。比如两帧数据是同时发出的,那应该以哪一个为准呢?当总线上同时出现逻辑零和逻辑一的时候,总线会被视为逻辑 0。此后 ID 值较大的数据帧就不会再发送了,而 ID 值较小的数据帧会继续发送,从而实现非破坏性的仲裁。
2.3 错误检测与处理
CAN 具有多层错误检测机制: 1. 位错误:发送位与读取位不一致 2. 填充错误:违反位填充规则(连续 5 个相同位后必须插入相反位) 3. CRC 错误:循环冗余校验失败 4. 格式错误:固定格式位出现非法值 5. 应答错误:发送端未收到应答
每个 CAN 节点维护错误计数器,根据错误严重程度进入不同状态: - 主动错误:正常通信状态 - 被动错误:限制错误帧发送 - 总线关闭:停止发送和接收
3. 物理层:差分信号与抗干扰原理
CAN 收发器负责将单片机的逻辑信号转换为总线上的差分信号。
3.1 差分信号定义
CAN 总线使用两根信号线:CAN_H (High) 和 CAN_L (Low)。接收端通过比较两根线的电压差来判断逻辑状态。
- 显性电平 (Dominant, 逻辑 0)
- CAN_H 拉高 (约 3.5V),CAN_L 拉低 (约 1.5V)
- 电压差约为 2V
- 特性:总线上只要有一个节点发送"显性",总线即呈现"显性"状态(线与机制)
- 隐性电平 (Recessive, 逻辑 1)
- CAN_H 和 CAN_L 均维持在中间电压 (约 2.5V)
- 电压差近乎 0V
- 特性:只有所有节点都处于"隐性"时,总线才呈现"隐性"状态
3.2 位时序与同步
CAN 位时间由四个段组成: 1. 同步段 (Sync_Seg):用于同步各节点时钟 2. 传播段 (Prop_Seg):补偿物理延迟 3. 相位缓冲段 1 (Phase_Seg1):补偿上升沿误差 4. 相位缓冲段 2 (Phase_Seg2):补偿下降沿误差
再同步:当边沿出现在同步段之外时,通过延长或缩短相位缓冲段来调整采样点位置。
3.3 抗干扰机制
CAN 总线通过双绞线传输差分信号,能有效抵御电机启动等高噪环境下的共模干扰。
案例分析: 假设主控制器 (Master) 向电机 (Device) 发送逻辑"0"。
- 正常发送:Master 输出 CAN_H=3.5V,CAN_L=1.5V,差值 ΔV = 2.0V
-
干扰引入:外界产生 +1.0V 的共模干扰(如电压尖峰)。由于双绞线紧密缠绕,干扰同时作用于两线:
- CAN_H 变为 4.5V (3.5 + 1.0)
- CAN_L 变为 2.5V (1.5 + 1.0)
-
差分解码:接收端仅计算差值:ΔV = 4.5V - 2.5V = 2.0V
- 结论:尽管绝对电压发生偏移,但差值保持不变,数据仍被正确识别为"0"
4. STM32 CAN 外设
4.1 bxCAN (Basic Extended CAN),目前使用较多
- 支持标准:CAN 2.0A 和 2.0B
- 数据长度:最多 8 字节
- ID 长度:11 位标准帧,29 位扩展帧
4.2 FDCAN (Flexible Data-rate CAN)
- 支持标准:CAN FD (Flexible Data-rate)
- 数据长度:最多 64 字节
- ID 长度:11 位标准帧,29 位扩展帧
4.3 硬件过滤器
STM32 CAN 外设提供硬件过滤器,可在不占用 CPU 的情况下过滤不需要的报文: - 列表模式:只接收 ID 完全匹配的报文 - 掩码模式:接收 ID 在掩码范围内匹配的报文 - FIFO 分配:不同过滤器可指向不同 FIFO
4.4 硬件架构与数据流向
bxCAN 外设的内部逻辑组成主要分为发送、接收和过滤三个部分:

如图所示:
- 发送端:CPU 将待发送的报文写入 发送邮箱(Mailbox 0/1/2)。bxCAN 拥有 3 个发送邮箱,发送优先级由硬件根据标识符(ID)或写入顺序决定。硬件会自动进行仲裁:当多个邮箱同时有报文待发送时,硬件会优先发送 ID 值最小的报文(即优先级最高的报文)。如果同时出现逻辑 0 和逻辑 1,总线会被视为逻辑 0,ID 值较大的报文会停止发送。
- 接收端:总线上的报文通过 GPIO 和控制器进入 接收过滤器。
- 过滤器(Filter):bx CAN 共有 14 个过滤器(0-13)。过滤器决定哪些报文是本节点需要的。只有匹配成功的报文才会进入 接收 FIFO。
- 接收 FIFO:有两个独立的 FIFO(FIFO 0 和 FIFO 1),每个 FIFO 包含 3 个级联的邮箱。这种结构可以缓存最多 6 帧报文,防止 CPU 处理不及时导致丢包。
4.5 测试模式
在开发调试阶段,bxCAN 提供了三种特殊的测试模式:

如图所示:
- 静默模式 (Silent Mode):只能接收,不能发送(TX 引脚内部断开,只能监测总线数据)。用于总线监听或波特率自适应。
- 环回模式 (Loopback Mode):发送的报文不经过总线,直接反馈到接收端。用于在没有外部节点的情况下进行自测。
- 环回静默模式 (Silent Loopback):结合了前两者的特点,完全不影响总线,且能实现内部自测,常用于"热自检"。
4.6 工作模式与状态转换
bx CAN 的状态机由三个主要状态组成,通过控制寄存器的位(如 INRQ 和 SLEEP)进行切换:

如图所示:
- 初始化模式 (Initialization):配置波特率、过滤器等参数时必须进入此模式,此时禁止收发报文。
- 正常模式 (Normal):配置完成后进入,此时外设可以与总线进行正常的数据交换。
- 睡眠模式 (Sleep):低功耗状态,时钟停止。
- 唤醒机制 (AWUM):
- 自动唤醒 (AWUM=1):一旦检测到总线活动,硬件自动清零
SLEEP位并唤醒。- 手动唤醒 (AWUM=0):需要软件干预清零
SLEEP位。
5. 软件驱动架构 (can_list)
本战队使用的 can_list 驱动采用分层设计,旨在解决高频中断下的实时性问题及多设备 ID 的快速检索。
5.1 第一阶段:中断接收 (ISR)
此阶段为信号入口,位于 can_list.c 的中断服务函数中。
- 中断源区分:兼容 FDCAN (H7/G4 系列) 和 bx CAN (F1/F4 系列) 的中断接口。
- 处理模式:
- RTOS 模式 (推荐):为了保证系统实时性,ISR (Interrupt Service Routine) 不直接解析数据。它构建一个轻量级通知,通过 FreeRTOS 队列 (
xQueueSendFromISR) 发送至后台任务,随即退出中断。 - 非 RTOS 模式:在中断内直接调用处理函数,适用于简单应用,但可能阻塞其他高优先级任务。
- RTOS 模式 (推荐):为了保证系统实时性,ISR (Interrupt Service Routine) 不直接解析数据。它构建一个轻量级通知,通过 FreeRTOS 队列 (
5.2 第二阶段:数据分发 (核心逻辑)
此阶段由后台任务 (can_list_polling_task) 执行,负责将原始数据映射到具体设备。
- 读取寄存器:通过 HAL 库函数 (
HAL_CAN_GetRxMessage) 从硬件 FIFO 读取解码后的报文。 - 哈希查表 (Hash Lookup):
- 利用
ID % table_length计算哈希桶索引,快速定位链表头。 - 此算法将遍历搜索的时间复杂度大幅降低,适合挂载大量电机的场景。
- 利用
- 掩码匹配 (Masking):
- 遍历链表时,不仅对比 ID,还引入了 掩码 (Mask) 机制。
- 应用场景:若掩码设为
0xFF,则 ID0x12340201和0xAAAA0201均匹配注册 ID0x01。这允许驱动统一处理某一类设备的反馈(如提取低 8 位作为子 ID)。
5.3 第三阶段:业务回调 (Callback)
一旦匹配成功,驱动将执行最终的业务逻辑。
- 数据标准化:将不同硬件(bx CAN/FDCAN)的数据格式统一封装为
can_rx_header_t结构体。 - 执行回调:
- 调用用户注册的函数指针:
node->callback(node->can_data, ...)。 - 面向对象设计:
can_data指针通常指向具体的电机对象。用户在回调中直接操作该指针,无需通过全局变量查找电机,实现了代码的高内聚低耦合。
- 调用用户注册的函数指针:
6. 总结
CAN 总线是机器人电控系统的核心通信技术,掌握其原理和实际应用对电控工程师至关重要。本模块提供的 can_list 驱动通过哈希表和掩码机制,实现了高效的消息接收,特别适合多电机、多传感器的机器人系统。结合 RTOS 使用可进一步提升系统实时性和稳定性。