CAN Protocols - BOSCH (控制器局域网络协议)
- CAN 通信, 全称: 控制器局域网 (Controller Area Network), 是一种功能丰富的车用总线标准, 有效支持分布式控制系统的串行通信网络.
- CAN 数据使用帧结构进行传输.
- CAN 的物理层: CAN-H, CAN-L, 一般是差分信号, 根据两根线上的电位差来判断总线电平, 总线电平分为显性电平和隐性电平, 二者必居其一, 发送方通过使总线电平发生变化, 将消息发送给接收方.
- CAN 总线两端有两个 的终端电阻, 用作阻抗匹配, 抑制信号反射.
- CAN 非破坏性仲裁机制(基于标识符优先级): 多个同时的回复不会造成乱码, 高优先级消息通过, 低优先级消息被忽略.
- CAN 过滤器: 对特定信息进行过滤, 只接收指定的有效信息.
特性
-
多主控制与仲裁:
- 所有节点都可发送 (多主).
- 非破坏性仲裁: 通过 标识符(ID)优先级 决定优先级. 高优先级消息通过, 低优先级消息主动退出并转为接收.
-
系统柔性与扩展性:
- 无地址信息: 报文基于内容 (ID) 发送, 而非地址.
- 易于扩展: 新增节点不影响现有节点软硬件及应用层.
-
高性能传输:
- 通信速率快: 最高可达 (短距离).
- 通信距离远: 速率降低时, 最远可达 .
-
强大的错误处理:
- 具备 错误检测、通知和恢复 机制.
- 自动重发: 发送单元检测到错误后, 会强制终止并自动重新发送消息, 直到成功.
-
故障隔离 (封闭):
- 故障封闭功能: 能区分暂时错误和持续性故障.
- 对于持续性故障, 能将引起故障的单元 从总线上隔离, 避免影响整个网络.
-
连接节点数量:
- 连接节点多: 理论上无限制, 但实际数量受限于通信速率、总线延迟和电气负载.
物理层特征与时序同步
CAN 协议经过 ISO 标准化后有两个标准: ISO11898 标准和 ISO11519-2 标准. 其中 ISO11898 是针对通信速率为 的高速通信标准, 而 ISO11519-2 是针对通信速率为 以下的低速通信标准. 这里使用 ISO11898 标准, 的通信速率.

位时序 (Bit Timing)
位时序是实现 CAN 网络同步的核心机制. 它将一个位时间 (Bit Time) 分解为 4 个时间段, 每个时间段由若干个时间量子 (Time Quantum, Tq) 构成. 通过设定位时序, 可以实现多个单元同时采样, 并任意设定采样点. 由发送单元在非同步的情况下发送的每秒钟的位数称为位速率 (Bit Rate).
一个位时间由以下 4 个段组成:
| 段名称 | 作用说明 | Tq 数范围 |
|---|---|---|
| 同步段 (SS: Synchronization Segment): | 多个连接在总线上的单元通过此段实现时序调整, 同步进行接收和发送的工作. 由隐性到显性或由显性到隐性电平的边沿最好出现在此段中. | 恒定为 1Tq |
| 传播时间段 (PTS: Propagation Time Segment): | 用于吸收网络上的物理延迟 (包括发送单元的输出延迟、总线上传播延迟、接收单元的输入延迟) 的段. | 1~8Tq |
| 相位缓冲段 1 (PBS1: Phase Buffer Segment 1): | 当信号边沿不能被包含于 SS 段中时, 可在此段进行补偿. | 1~8Tq |
| 相位缓冲段 2 (PBS2: Phase Buffer Segment 2): | 用于吸收细微的时钟误差. 可通过对该段加减 SJW (再同步补偿宽度) 来吸收误差. | 2~8Tq |
关键概念:
- 再同步补偿宽度 (SJW: reSynchronization Jump Width): SJW 是用于补偿因时钟频率偏差、传送延迟等导致的同步误差的最大值. 其 Tq 数范围为 1~4Tq
- 采样点 (Sampling Point): 位于 PBS1 结束处. 它是指读取总线电平, 并将读到的电平作为位值的点.

TIP上图的采样点, 是指读取总线电平, 并将读到的电平作为位值的点. 位置在 PBS1 结束处. 根据这个位时序, 可以计算 CAN 通信的波特率.
协议核心机制
CAN 总线仲裁机制
CAN 总线仲裁机制是一种基于标识符优先级的非破坏性仲裁机制, 确保高优先级消息能够优先使用总线.
1. 仲裁基础
- 多主控制: 当总线处于空闲状态时, 任何连接到总线的单元 (节点) 都可以尝试开始发送消息.
- 非破坏性: 仲裁过程不会中断或破坏总线上正在进行的传输, 低优先级的消息会主动退出, 高优先级的消息不受影响地继续传输.
2. 优先级确定
- 基于标识符 (ID): 消息的标识符优先级由其在仲裁段中的标识符 (ID) 决定. ID 并不是设备的地址, 而是消息内容的标识和优先级.
- 优先级规则:
- 显性电平 (Dominant/逻辑 0): 具有优先权, 它会覆盖 隐性电平 (Recessive/逻辑 1).
- ID 值越低 (即 ID 前导的显性电平越多): 消息的优先级就越高.
3. 仲裁过程

- 逐位比较: 所有同时发送消息的单元从仲裁段的第一位开始, 逐个位地将自己发送的位电平与总线上的实际电平进行比较.
- 仲裁获胜:
- 当一个单元发送显性电平 (Dominant), 且总线上也为显性电平, 则该单元继续发送.
- 连续输出显性电平最多的单元将获胜, 并获得总线的控制权.
- 仲裁失利 (退出):
- 当一个单元发送隐性电平 (Recessive/逻辑 1), 但同时检测到总线上是显性电平 (Dominant/逻辑 0) 时, 表明总线上有更高优先级的消息正在发送.
- 该单元判定仲裁失利, 立即停止发送当前消息, 并转为接收状态, 等待总线再次空闲后重新尝试发送.
TIP上图中, 单元 1 和单元 2 同时开始向总线发送数据, 开始部分他们的数据格式是一样的, 故无法区分优先级, 直到 T 时刻, 单元 1 输出隐性电平, 而单元 2 输出显性电平, 此时单元 1 仲裁失利, 立刻转入接收状态工作, 不再与单元 2 竞争, 而单元 2 则顺利获得总线使用权, 继续发送自己的数据. 这就实现了仲裁, 让连续发送显性电平多的单元获得总线使用权.
4. 结果
- 仲裁段结束后, 总线上只有仲裁获胜 (标识符优先级最高) 的消息得以继续完整传输, 确保了实时性和高效性.
消息过滤机制 (Filter)
CAN 过滤器是 CAN 控制器硬件的重要组成部分, 其核心作用在于对总线上的报文 ID 进行高效筛选, 决定哪些报文需要被接收和处理, 哪些报文应该被丢弃.
1. 目的
- 减轻 CPU 负担: 硬件过滤器在接收消息进入 FIFO (接收缓冲区) 之前就完成 ID 匹配, 避免 CPU 被大量无关数据中断和处理, 从而专注于业务逻辑.
- 提高效率: 确保节点只接收与其功能相关的消息, 实现了高效的数据管理.
2. 工作原理:筛选模式
CAN 过滤器通过两个核心参数: 过滤器标识符寄存器 (ID Register) 和 过滤器屏蔽寄存器 (Mask Register) 来配置筛选规则.
| 模式 | 描述 | 应用场景 |
|---|---|---|
| 标识符列表模式 (List Mode) | 屏蔽寄存器中的位全部设置为不关心 (0). 过滤器寄存器中存储的是完整的目标 ID. 只有当接收到的报文 ID 与过滤器 ID 完全匹配时才会被接收. | 需要精确接收少数几个固定 ID 的报文时. |
| 屏蔽模式 (Mask Mode) | 屏蔽寄存器中的位用于指定 ID 中哪些位必须匹配 (设置为 1), 哪些位可以忽略 (设置为 0). 接收到的报文 ID 只有在”必须匹配”的位上与过滤器 ID 相符时才会被接收. | 需要接收某一范围 ID 的报文时, 或者需要区分标准帧和扩展帧时. |
3. 硬件实现(以 STM32 bxCAN 为例)
在如 STM32 等微控制器中, CAN 控制器通常集成多达 28 个(或更多)独立的过滤器组,这些过滤器组可以配置为:
- 16 位模式: 每个过滤器组可配置为两个 16 位的过滤器(可以容纳 11 位标准帧 ID 和部分功能位).
- 32 位模式: 每个过滤器组可配置为一个 32 位的过滤器(可以容纳 29 位扩展帧 ID).
每个过滤器最终都会将匹配成功的报文路由到指定的接收 FIFO(通常是 FIFO0 或 FIFO1)中, 等待 CPU 读取.
帧结构
CAN 协议的五种帧类型
CAN 协议通过以下 5 种类型的帧进行通信:
- 数据帧 (Data Frame): 用于发送单元向接收单元传送数据. (有数据段)
- 遥控帧 (远程帧) (Remote Frame): 用于接收单元向具有相同 ID 的发送单元请求数据. (无数据段)
- 错误帧 (Error Frame): 用于当检测出错误时向其他单元通知错误.
- 过载帧 (Overload Frame): 用于接收单元通知其尚未做好接收准备.
- 帧间隔 (Interframe Space): 用于将数据帧及遥控帧与前面的帧分离开来.
其中, 数据帧和遥控帧有标准格式 (11 位标识符 ID) 和扩展格式 (29 位 ID) 两种标识符长度.
数据帧 (Data Frame) 结构
CAN 数据帧是协议中最核心的帧类型, 它由以下 7 个段构成:
| 序号 | 段名称 | 作用说明 |
|---|---|---|
| 1 | 帧起始 (Start of Frame, SOF) | 表示数据帧开始的段. |
| 2 | 仲裁段 (Arbitration Field) | 表示该帧优先级的段, 包含报文的标识符 (ID), 用于总线仲裁. |
| 3 | 控制段 (Control Field) | 表示数据段的字节数及保留位的段. |
| 4 | 数据段 (Data Field) | 数据的内容, 一帧可发送 0 到 8 个字节的数据. |
| 5 | CRC 段 (CRC Field) | 用于检查帧的传输错误的段. |
| 6 | ACK 段 (Acknowledge Field) | 用于表示确认正常接收的段. 接收到正确消息的单元在此段发送显性电平, 通知发送单元正常接收. |
| 7 | 帧结束 (End of Frame, EOF) | 表示数据帧结束的段. |

TIP图中 D 表示显性电平, R 表示隐性电平 (下同)
- 帧起始 (Start of Frame, SOF): 标准帧和扩展帧都是由 1 个位的显性电平表示帧起始.
- 仲裁段 (Arbitration Field): 仲裁段, 表示数据优先级的段, 标准帧和扩展帧格式在本段有所区别.
- 标准格式的 ID 有 11 个位. 从 ID28 到 ID18 被依次发送. 禁止高 7 位都为隐性 (禁止设定: ID=1111111XXXX).
- 扩展格式的 ID 有 29 个位. 基本 ID 从 ID28 到 ID18, 扩展 ID 由 ID17 到 ID0 表示. 基本 ID 和标准格式的 ID 相同. 禁止高 7 位都为隐性 (禁止设定: 基本 ID=1111111XXXX). 其中 RTR 位用于标识是否是远程帧 (0, 数据帧; 1, 远程帧), IDE 位为标识符选择位 (0, 使用标准标识符; 1, 使用扩展标识符), SRR 位为代替远程请求位, 为隐性位, 它代替了标准帧中的 RTR 位.
- 控制段 (Control Field): 由 6 个位构成, 表示数据段的字节数. 标准帧和扩展帧的控制段稍有不同.
r0 和 r1 为保留位, 必须全部以显性电平发送, 但是接收端可以接收显性、隐性及任意组合的电平. DLC 段为数据长度表示段, 高位在前, DLC 段有效值为 08, 但是接收方接收到 915 的时候并不认为是错误. - 数据段 (Data Field): 从最高位 (MSB) 开始输出, 标准帧和扩展帧在这个段的定义都是一样的.

- CRC 段 (CRC Field): 用于检查帧的传输错误的段. 由 15 个位的 CRC 顺序和 1 个位的 CRC 界定符 (用于分隔的位) 组成, 标准帧和扩展帧在这个段的格式也是相同的.
此段 CRC 的值计算范围包括: 帧起始、仲裁段、控制段、数据段. 接收方以同样的算法计算 CRC 值并进行比较, 不一致时会通报错误. - ACK 段 (Acknowledge Field): 用于表示确认正常接收的段. 由 ACK 槽 (ACK Slot) 和 ACK 界定符 2 个位组成. 标准帧和扩展帧在这个段的格式也是相同的.
发送单元的 ACK, 发送 2 个位的隐性位, 而接收到正常消息的单元在 ACK 槽 (ACK Slot) 发送显性位, 通知发送单元正常接收结束, 这个过程叫发送 ACK/返回 ACK. 发送 ACK 的是在既不处于总线关闭态也不处于休眠态的所有接收单元中, 接收到正常消息的单元 (发送单元不发送 ACK). 所谓正常消息是指不含填充错误、格式错误、CRC 错误的消息. - 帧结束 (End of Frame, EOF): 标准帧和扩展帧在这个段格式一样, 由 7 个位的隐性位组成.
编程开发
CAN 协议的编程开发主要围绕微控制器(如 STM32)内部的 CAN 控制器模块(例如 bxCAN)进行. 开发的核心在于初始化和配置控制器, 使其能够正确地与物理总线同步, 并高效地处理报文.
硬件初始化与总线配置
这是 CAN 通信开始前必须完成的基础工作, 主要使用 CAN_InitTypeDef 结构体进行配置.
- 时钟与 GPIO 配置: 使能 CAN 控制器(如 CAN1)及相关 GPIO 口的时钟, 并配置 CAN_TX 和 CAN_RX 引脚(例如 STM32 需要配置 APB1 时钟).
- CAN 控制器初始化: 设置 CAN 工作模式、自动重传、离线管理等功能, 并配置位时序(Bit Timing)确定所需的通信波特率.
void CAN_Config(void){ // 1. 配置 GPIO 时钟和引脚 (略) // 2. 配置 CAN 时钟 (略)
CAN_InitTypeDef CAN_InitStructure;
// CAN 模块工作参数配置 CAN_InitStructure.CAN_TTCM = DISABLE; // 非时间触发通信模式 CAN_InitStructure.CAN_ABOM = ENABLE; // 自动离线管理 CAN_InitStructure.CAN_AWUM = ENABLE; // 自动唤醒模式 CAN_InitStructure.CAN_NART = DISABLE; // 自动重传 CAN_InitStructure.CAN_RFLM = DISABLE; // 不锁定 FIFO CAN_InitStructure.CAN_TXFP = DISABLE; // 优先级由 ID 决定
// 工作模式选择: // CAN_Mode_Normal (正常模式) 或 CAN_Mode_LoopBack (回环模式) CAN_InitStructure.CAN_Mode = CAN_Mode_LoopBack; // 示例使用回环模式进行自测
// 位时序配置 (例如: 波特率 500Kbps) CAN_InitStructure.CAN_SJW = CAN_SJW_1tq; // 再同步跳转宽度 1Tq CAN_InitStructure.CAN_BS1 = CAN_BS1_8tq; // 时间段 1: 8Tq CAN_InitStructure.CAN_BS2 = CAN_BS2_7tq; // 时间段 2: 7Tq CAN_InitStructure.CAN_Prescaler = 6; // 预分频器 (决定 Tq 长度)
CAN_Init(CAN1, &CAN_InitStructure); // ... 后续进行过滤器配置 ... CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE); // 开启 FIFO0 接收中断}消息过滤机制配置
使用 CAN_FilterInitTypeDef 结构体配置硬件过滤器, 确保接收效率.
- 过滤器初始化: 设置过滤器号、规模、工作模式和帧类型.
- ID 和屏蔽器设置: 确定接收规则(标识符列表模式或屏蔽模式).
- FIFO 绑定: 指定匹配成功的报文进入哪个接收邮箱(FIFO0 或 FIFO1).
// 承接上一段代码,设置过滤器结构体CAN_FilterInitTypeDef CAN_FilterInitStructure;
CAN_FilterInitStructure.CAN_FilterNumber = 0; // 过滤器 0CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; // 屏蔽模式 (用于接收某一范围 ID)CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; // 32 位规模
// 过滤器 ID 寄存器: 设定目标 IDCAN_FilterInitStructure.CAN_FilterIdHigh = 0x0120;CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
// 过滤器屏蔽寄存器: 决定 ID 中哪些位必须匹配CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0xFFF0;CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; // 匹配消息放入 FIFO0CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; // 激活过滤器CAN_FilterInit(&CAN_FilterInitStructure);数据发送流程
数据发送通过配置 CanTxMsg 结构体和调用 CAN_Transmit() 函数实现.
- 配置发送参数: 确定 ID、帧类型、数据长度(DLC, 0~8)和实际数据.
- 发送请求: 写入空闲的发送邮箱并设置发送请求标志.
void CAN_Send_Msg(u8 *buf, u8 len){ CanTxMsg TxMessage; TxMessage.StdId = 0x12; // 消息 ID TxMessage.ExtId = 0x00; TxMessage.IDE = CAN_Id_Standard; // 标准帧 TxMessage.RTR = CAN_RTR_Data; // 数据帧 (非遥控帧) TxMessage.DLC = len; // 数据长度 (0-8)
for (int i = 0; i < len; i++) TxMessage.Data[i] = buf[i];
CAN_Transmit(CAN1, &TxMessage); // 写入发送邮箱并设置发送请求}数据接收流程
数据接收通常通过中断方式实现, 需要配置 NVIC 并在中断服务函数(ISR)中调用 CAN_Receive() 读取报文.
// CAN FIFO0 接收中断服务函数 (STM32F103 的中断向量)void USB_LP_CAN1_RX0_IRQHandler(void){ CanRxMsg RxMessage; if (CAN_GetITStatus(CAN1, CAN_IT_FMP0) != RESET) // 检查 FIFO0 中是否有新消息 { CAN_Receive(CAN1, CAN_FIFO0, &RxMessage); // 读取消息到 RxMessage 结构体
// 在这里处理接收到的数据, 例如: // u32 id = RxMessage.StdId; // u8 data0 = RxMessage.Data[0];
// 示例:将接收到的数据在 LCD 上显示 (参考文档中的逻辑) // LCD_ShowxNum(..., RxMessage.Data[i], ...); } CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0); // 清除中断标志位}模式选择
在开发和测试中, 不同的工作模式用于不同的目的:
- 回环模式(LoopBack Mode): 主要用于自测试和调试. 在这种模式下, CAN 控制器将自己发送的消息当作接收消息立即回传给自己, 不涉及外部 CAN 收发器和物理总线.
- 正常模式(Normal Mode): 用于正常通信, 此时 CAN 控制器将数据发送到总线, 并与其他节点进行交互.
- 静默模式(Silent Mode): 主要用于总线监测和诊断.
参考: