4541 字
23 分钟
CAN 通信协议

CAN Protocols - BOSCH (控制器局域网络协议)#

  • CAN 通信, 全称: 控制器局域网 (Controller Area Network), 是一种功能丰富的车用总线标准, 有效支持分布式控制系统的串行通信网络.
  • CAN 数据使用帧结构进行传输.
  • CAN 的物理层: CAN-H, CAN-L, 一般是差分信号, 根据两根线上的电位差来判断总线电平, 总线电平分为显性电平隐性电平, 二者必居其一, 发送方通过使总线电平发生变化, 将消息发送给接收方.
  • CAN 总线两端有两个 120Ω120\Omega终端电阻, 用作阻抗匹配, 抑制信号反射.
  • CAN 非破坏性仲裁机制(基于标识符优先级): 多个同时的回复不会造成乱码, 高优先级消息通过, 低优先级消息被忽略.
  • CAN 过滤器: 对特定信息进行过滤, 只接收指定的有效信息.

特性#

  1. 多主控制与仲裁:

    • 所有节点都可发送 (多主).
    • 非破坏性仲裁: 通过 标识符(ID)优先级 决定优先级. 高优先级消息通过, 低优先级消息主动退出并转为接收.
  2. 系统柔性与扩展性:

    • 无地址信息: 报文基于内容 (ID) 发送, 而非地址.
    • 易于扩展: 新增节点不影响现有节点软硬件及应用层.
  3. 高性能传输:

    • 通信速率快: 最高可达 1 Mbps1\text{ Mbps} (短距离).
    • 通信距离远: 速率降低时, 最远可达 10 KM10\text{ KM}.
  4. 强大的错误处理:

    • 具备 错误检测、通知和恢复 机制.
    • 自动重发: 发送单元检测到错误后, 会强制终止并自动重新发送消息, 直到成功.
  5. 故障隔离 (封闭):

    • 故障封闭功能: 能区分暂时错误和持续性故障.
    • 对于持续性故障, 能将引起故障的单元 从总线上隔离, 避免影响整个网络.
  6. 连接节点数量:

    • 连接节点多: 理论上无限制, 但实际数量受限于通信速率、总线延迟和电气负载.

物理层特征与时序同步#

CAN 协议经过 ISO 标准化后有两个标准: ISO11898 标准和 ISO11519-2 标准. 其中 ISO11898 是针对通信速率为 125 Kbps1 Mbps125\text{ Kbps}\sim 1\text{ Mbps} 的高速通信标准, 而 ISO11519-2 是针对通信速率为 125 Kbps125\text{ Kbps} 以下的低速通信标准. 这里使用 ISO11898 标准, 450 Kbps450\text{ Kbps} 的通信速率.

CAN 物理层特征

位时序 (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 种类型的帧进行通信:

  1. 数据帧 (Data Frame): 用于发送单元向接收单元传送数据. (有数据段)
  2. 遥控帧 (远程帧) (Remote Frame): 用于接收单元向具有相同 ID 的发送单元请求数据. (无数据段)
  3. 错误帧 (Error Frame): 用于当检测出错误时向其他单元通知错误.
  4. 过载帧 (Overload Frame): 用于接收单元通知其尚未做好接收准备.
  5. 帧间隔 (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 个字节的数据.
5CRC 段 (CRC Field)用于检查帧的传输错误的段.
6ACK 段 (Acknowledge Field)用于表示确认正常接收的段. 接收到正确消息的单元在此段发送显性电平, 通知发送单元正常接收.
7帧结束 (End of Frame, EOF)表示数据帧结束的段.

数据帧的构成

TIP

图中 D 表示显性电平, R 表示隐性电平 (下同)

  1. 帧起始 (Start of Frame, SOF): 标准帧和扩展帧都是由 1 个位的显性电平表示帧起始.
  2. 仲裁段 (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 位.
  3. 控制段 (Control Field): 由 6 个位构成, 表示数据段的字节数. 标准帧和扩展帧的控制段稍有不同. 控制段 r0 和 r1 为保留位, 必须全部以显性电平发送, 但是接收端可以接收显性、隐性及任意组合的电平. DLC 段为数据长度表示段, 高位在前, DLC 段有效值为 08, 但是接收方接收到 915 的时候并不认为是错误.
  4. 数据段 (Data Field): 从最高位 (MSB) 开始输出, 标准帧和扩展帧在这个段的定义都是一样的. 数据段
  5. CRC 段 (CRC Field): 用于检查帧的传输错误的段. 由 15 个位的 CRC 顺序和 1 个位的 CRC 界定符 (用于分隔的位) 组成, 标准帧和扩展帧在这个段的格式也是相同的. CRC 段 此段 CRC 的值计算范围包括: 帧起始、仲裁段、控制段、数据段. 接收方以同样的算法计算 CRC 值并进行比较, 不一致时会通报错误.
  6. ACK 段 (Acknowledge Field): 用于表示确认正常接收的段. 由 ACK 槽 (ACK Slot) 和 ACK 界定符 2 个位组成. 标准帧和扩展帧在这个段的格式也是相同的. ACK 段 发送单元的 ACK, 发送 2 个位的隐性位, 而接收到正常消息的单元在 ACK 槽 (ACK Slot) 发送显性位, 通知发送单元正常接收结束, 这个过程叫发送 ACK/返回 ACK. 发送 ACK 的是在既不处于总线关闭态也不处于休眠态的所有接收单元中, 接收到正常消息的单元 (发送单元不发送 ACK). 所谓正常消息是指不含填充错误、格式错误、CRC 错误的消息.
  7. 帧结束 (End of Frame, EOF): 标准帧和扩展帧在这个段格式一样, 由 7 个位的隐性位组成.

编程开发#

CAN 协议的编程开发主要围绕微控制器(如 STM32)内部的 CAN 控制器模块(例如 bxCAN)进行. 开发的核心在于初始化配置控制器, 使其能够正确地与物理总线同步, 并高效地处理报文.

硬件初始化与总线配置#

这是 CAN 通信开始前必须完成的基础工作, 主要使用 CAN_InitTypeDef 结构体进行配置.

  1. 时钟与 GPIO 配置: 使能 CAN 控制器(如 CAN1)及相关 GPIO 口的时钟, 并配置 CAN_TX 和 CAN_RX 引脚(例如 STM32 需要配置 APB1 时钟).
  2. 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 结构体配置硬件过滤器, 确保接收效率.

  1. 过滤器初始化: 设置过滤器号、规模、工作模式和帧类型.
  2. ID 和屏蔽器设置: 确定接收规则(标识符列表模式或屏蔽模式).
  3. FIFO 绑定: 指定匹配成功的报文进入哪个接收邮箱(FIFO0 或 FIFO1).
// 承接上一段代码,设置过滤器结构体
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CAN_FilterInitStructure.CAN_FilterNumber = 0; // 过滤器 0
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; // 屏蔽模式 (用于接收某一范围 ID)
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; // 32 位规模
// 过滤器 ID 寄存器: 设定目标 ID
CAN_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; // 匹配消息放入 FIFO0
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; // 激活过滤器
CAN_FilterInit(&CAN_FilterInitStructure);

数据发送流程#

数据发送通过配置 CanTxMsg 结构体和调用 CAN_Transmit() 函数实现.

  1. 配置发送参数: 确定 ID、帧类型、数据长度(DLC, 0~8)和实际数据.
  2. 发送请求: 写入空闲的发送邮箱并设置发送请求标志.
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): 主要用于总线监测和诊断.

参考:

STM32F1开发指南-库函数版本_V3.3
零死角玩转stm32
Wikipedia-控制器區域網路

CAN 通信协议
https://blog.chuwu.top/posts/2025-11-03/can/
作者
ChuwuYo
发布于
2025-11-03
许可协议
CC BY-NC-SA 4.0