news 2026/7/15 14:17:47

CAN 总线协议深度解析——从仲裁机制到工程实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CAN 总线协议深度解析——从仲裁机制到工程实战

一、引言

CAN(Controller Area Network)总线是汽车电子和工业控制领域最重要的通信协议之一。它诞生于 1983 年(Bosch 公司),至今已有 40 年历史,却仍然是现代汽车的核心通信骨架。从一辆普通家用轿车的 30+ 个 ECU(电子控制单元)到工业机器人关节的实时通信,CAN 总线的身影无处不在。

本文将全面解构 CAN 总线:

  • CAN 的物理层与差分信号

  • 仲裁机制与优先级策略

  • 帧格式(标准帧、扩展帧、CAN FD)

  • 位时序与波特率计算

  • 过滤器配置的深入理解

  • STM32 bxCAN 的实战配置

  • CAN 总线的工程陷阱与排查方法

平台:STM32F103(bxCAN,基本 CAN 控制器) 收发器:TJA1050 测试工具:CAN 分析仪(USB-CAN)


二、CAN 总线基础

2.1 为什么用 CAN?——对比其他总线

┌─────────────────────────────────────────────────────────┐ │ CAN vs 其他工业通信协议 │ ├──────────┬──────────┬──────────┬──────────┬─────────────┤ │ 特性 │ CAN │ RS-485 │ I2C │ SPI │ ├──────────┼──────────┼──────────┼──────────┼─────────────┤ │ 通信方式 │ 多主 │ 主从 │ 主从 │ 主从 │ │ 错误检测 │ ★★★★★ │ ★☆☆☆☆ │ ★☆☆☆☆ │ ★☆☆☆☆ │ │ 最大节点 │ 无限制 │ 256 │ 127 │ 仅 1 从机 │ │ 实时性 │ ★★★★★ │ ★★☆☆☆ │ ★☆☆☆☆ │ ★★★★☆ │ │ 距离/速率 │ 40m@1M │ 1200m@100k│ 1m@100k │ 0.1m@10M │ │ 硬件成本 │ 中 │ 低 │ 极低 │ 极低 │ │ 适用场景 │ 汽车/工业│ 仪表 │ 板内通信 │ 板内高速 │ └──────────┴──────────┴──────────┴──────────┴─────────────┘ ​ CAN 的核心优势: 1. 多主模式——任何节点都可以主动发送,不需要主机轮询 2. 仲裁机制——多个节点同时发送时,高优先级自动胜出 3. 错误检测——5 种错误检测机制,错误率极低 4. 帧内 ACK——所有接收方在帧内确认,无需额外应答帧

2.2 CAN 物理层——差分信号

CAN_H ──══════════════════── ╲ ╱ ╲ ╱ ╲ ╱ ╲ ╱ 差分电压 = CAN_H - CAN_L CAN_L ─────╲╱──────╲╱────── ​ 显性位 (Dominant, 逻辑 0): CAN_H ≈ 3.5V, CAN_L ≈ 1.5V 差分电压 ≈ 2.0V(驱动状态) ​ 隐性位 (Recessive, 逻辑 1): CAN_H ≈ 2.5V, CAN_L ≈ 2.5V 差分电压 ≈ 0V(总线上所有节点释放驱动) ​ ★ 关键原理:显性位覆盖隐性位! - 任何节点驱动显性位 → 总线为显性 - 所有节点驱动隐性位 → 总线为隐性 这是 CAN 仲裁机制的基础!

三、CAN 仲裁机制——最优雅的设计

3.1 位仲裁(Bit-wise Arbitration)

CAN 的核心设计:CSMA/CD-AMP(载波侦听多路访问/冲突检测-通过仲裁的优先级消息) ​ 三个节点同时开始发送: ​ 起始 ID 位 数据... ─────┬────┬────┬────┬────┬────┬────┬── │SOF │ ID10│ID9 │ID8 │ID7 │ID6 │ ... NodeA│ 0 │ 0 │ 0 │ 1 │ 1 │ 0 │ ... (ID=0x0C...) NodeB│ 0 │ 0 │ 0 │ 1 │ 1 │ 1 │ ... (ID=0x0E...) NodeC│ 0 │ 0 │ 1 │ 0 │ 0 │ 0 │ ... (ID=0x20...) │ │ │ │ │ │ │ 总线 │ 0 │ 0 │ 0══│ 1 │ 1 │ 0══│ ... │ │ │ ║ │ │ │ ║ │ │ │ │ ║ │ │ │ ║ │ │ │ │NodeC 发送隐性(1) │ │ │ │但总线是显性(0)→ │ │ │ │NodeC 检测到冲突→ │ │ │ │★ NodeC 退出仲裁! │ │ │ │ NodeB 发送隐性(1) │ │ │ 但总线是显性(0)→ │ │ │ ★ NodeB 退出仲裁! │ │ │ │ │ │ ★ NodeA 赢得仲裁,继续发送 │ │ │ 仲裁结果:NodeA(ID=0x0C) > NodeB(ID=0x0E) > NodeC(ID=0x20) ID 越小,优先级越高!

3.2 仲裁的关键规则

规则 1:发送方同时监听总线 → 发送隐性位(1)时,检测到总线为显性(0) → 说明有更高优先级的节点在发送 → 立即退出! ​ 规则 2:仲裁失败方自动变为接收方 → 不丢失数据,只是发送权被抢占 → 等当前帧传输完毕后自动重试 ​ 规则 3:ID 越小,优先级越高 → CAN ID 不是"地址",而是"优先级标识" → 设计系统时需要为紧急消息分配小 ID

四、CAN 帧格式详解

4.1 标准数据帧(11 位 ID)

┌────────────────────────── 标准数据帧 ──────────────────────────────┐ │ │ │ ┌──┐┌─────┐┌──┐┌──┐┌──────────┐┌──┐┌──┐┌──┐┌───────┐┌──┐┌──┐ │ │ │SOF││ ID ││RTR││IDE││ 控制字段 ││数据││CRC││ACK││EOF/IFS│ │ │ │ 1 ││11位 ││ 1 ││ 1 ││ DLC(4) ││0-8B││16 ││ 2 ││ 7+3 │ │ │ └──┘└─────┘└──┘└──┘└──────────┘└──┘└──┘└──┘└───────┘└──┘└──┘ │ │ │ └───────────────────────────────────────────────────────────────────┘ ​ 各字段含义: SOF — 起始位(Start Of Frame),固定为显性位(0),用于同步所有节点 ID — 11 位标识符(标准格式),同时用于仲裁和过滤 RTR — 远程帧请求位:0=数据帧,1=远程帧(请求数据) IDE — 标识符扩展位:0=标准帧,1=扩展帧 r0 — 保留位 DLC — 数据长度码(4 位,0~8,表示数据字节数) DATA — 0~8 字节数据(长度由 DLC 指定) CRC — 15 位 CRC + 1 位分隔符(校验从 SOF 到 DATA 的所有位) ACK — 1 位 ACK 槽 + 1 位分隔符(接收方在 ACK 槽发显性位确认) EOF — 7 位帧结束(End Of Frame) IFS — 3 位帧间间隔(Inter Frame Space)

4.2 扩展帧(29 位 ID)vs CAN FD

标准帧 vs 扩展帧(关键差异): ┌──────────┬────────────┬──────────────┐ │ │ 标准帧(2.0A)│ 扩展帧(2.0B) │ ├──────────┼────────────┼──────────────┤ │ ID 位数 │ 11 位 │ 29 位 │ │ IDE 位 │ 0 (显性) │ 1 (隐性) │ │ SRR 位 │ 无 │ 在 RTR 位置 │ │ 最大 ID │ 0x7FF │ 0x1FFFFFFF │ │ 兼容性 │ 所有节点 │ 需要节点支持 │ └──────────┴────────────┴──────────────┘ ​ CAN FD (Flexible Data-rate): ┌──────────┬────────────┬──────────────┐ │ │ CAN 2.0 │ CAN FD │ ├──────────┼────────────┼──────────────┤ │ 数据速率 │ 最高 1Mbps │ 仲裁段 1Mbps │ │ 数据段 │ 同一个速率 │ ★ 最高 8Mbps │ │ 数据长度 │ 0~8 字节 │ ★ 0~64 字节 │ │ 兼容性 │ — │ 需 FD 控制器 │ └──────────┴────────────┴──────────────┘

五、CAN 位时序——正确计算波特率

5.1 位时间的组成

┌─────────────────────────────────────────────┐ │ 一个位时间 (Time Quanta) │ │ │ │ ← SYNC_SEG →←── PROP_SEG ──→←── PHASE_SEG1 ──→← PHASE_SEG2 →│ │ 1 TQ 1~8 TQ 1~8 TQ 1~8 TQ │ │ │ │ ←──────────────── 采样点 ──────────────────→│ │ (一般在 75%~87% 处) │ └─────────────────────────────────────────────┘ ​ CAN 位时间 = (1 + PROP_SEG + PHASE_SEG1 + PHASE_SEG2) × TQ TQ = BRP / CAN_CLK ​ 波特率 = 1 / 位时间

5.2 STM32F103 CAN 波特率计算

// CAN 波特率计算(APB1 = 36MHz) // 目标:500kbps(工业控制最常用) ​ // CAN 时钟 = APB1 = 36MHz // 1 TQ = (BRP + 1) / 36MHz ​ // 位时间 = (1 + BS1 + BS2) TQ // 选取:BRP=3, BS1=11, BS2=4 // TQ = (3+1)/36MHz = 111.1ns // 位时间 = (1+11+4) × 111.1ns = 16 × 111.1ns = 1.778μs // 波特率 = 1/1.778μs ≈ 562kbps(接近 500kbps,满足需求) ​ // 精确 500kbps 的配置: // BRP=5, BS1=6, BS2=4, SJW=1 // TQ = (5+1)/36MHz = 166.67ns // 位时间 = (1+6+4) × 166.67ns = 12 × 166.67ns = 2.0μs // 波特率 = 1/2.0μs = 500kbps ✓ ​ void CAN_BaudRate_500k(void) { CAN_InitTypeDef can; can.CAN_Prescaler = 5; // BRP can.CAN_Mode = CAN_Mode_Normal; can.CAN_SJW = CAN_SJW_1tq; // 同步跳转宽度 can.CAN_BS1 = CAN_BS1_7tq; // BS1 = 7(包含 PROP_SEG) can.CAN_BS2 = CAN_BS2_4tq; // BS2 = 4 can.CAN_TTCM = DISABLE; can.CAN_ABOM = ENABLE; // ★ 自动离线恢复 can.CAN_AWUM = ENABLE; // ★ 自动唤醒 can.CAN_NART = DISABLE; // 自动重传 can.CAN_RFLM = DISABLE; can.CAN_TXFP = DISABLE; CAN_Init(CAN1, &can); }

六、CAN 过滤器——理解才能用好

6.1 过滤器的工作原理

CAN 过滤器用于硬件级别的消息筛选——过滤发生在硬件层,CPU 完全不用处理不感兴趣的消息。 STM32F103 的 bxCAN 有 14 个过滤器组(28 个过滤器), 可在"标识符列表模式"和"掩码模式"之间选择。 掩码模式(Mask Mode): ┌─────────────────────────────────────────────┐ │ 滤波器寄存器 0 (CAN_FxR1): 期望的 ID 值 │ │ 滤波器寄存器 1 (CAN_FxR2): 掩码 │ │ │ │ 掩码 bit=1 → 该位必须匹配 │ │ 掩码 bit=0 → 该位不关心(Don't Care) │ │ │ │ 示例:接收 0x100~0x1FF 的所有帧 │ │ FxR1 = 0x100 (期望 ID=0x100) │ │ FxR2 = 0xF00 (掩码,只关心高 4 位) │ │ → 匹配:0x100, 0x101, ..., 0x1FF │ └─────────────────────────────────────────────┘ 标识符列表模式(List Mode): ┌─────────────────────────────────────────────┐ │ 滤波器寄存器 0 (CAN_FxR1): ID 1 │ │ 滤波器寄存器 1 (CAN_FxR2): ID 2 │ │ │ │ 只接收这两个具体 ID 的帧 │ │ → 精确匹配,不处理无关帧 │ └─────────────────────────────────────────────┘

6.2 过滤器配置代码

// 场景:接收 ID=0x100, 0x200, 0x300~0x3FF 的帧 void CAN_Filter_Config(void) { CAN_FilterInitTypeDef filter; // === 过滤器 0:精确匹配 ID=0x100 和 ID=0x200(列表模式)=== filter.CAN_FilterNumber = 0; filter.CAN_FilterMode = CAN_FilterMode_IdList; // 列表模式 filter.CAN_FilterScale = CAN_FilterScale_32bit; // 32 位模式 filter.CAN_FilterIdHigh = (0x100 << 5); // STDID[10:3] filter.CAN_FilterIdLow = (0x200 << 5); filter.CAN_FilterMaskIdHigh = 0; // 列表模式不用掩码 filter.CAN_FilterMaskIdLow = 0; filter.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; filter.CAN_FilterActivation = ENABLE; CAN_FilterInit(&filter); // === 过滤器 1:接收 0x300~0x3FF(掩码模式)=== filter.CAN_FilterNumber = 1; filter.CAN_FilterMode = CAN_FilterMode_IdMask; // 掩码模式 filter.CAN_FilterScale = CAN_FilterScale_32bit; filter.CAN_FilterIdHigh = (0x300 << 5); // 期望 ID filter.CAN_FilterIdLow = 0; filter.CAN_FilterMaskIdHigh = (0xF00 << 5); // 掩码:只关心高 4 位 filter.CAN_FilterMaskIdLow = 0; filter.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; filter.CAN_FilterActivation = ENABLE; CAN_FilterInit(&filter); }

七、CAN 发送与接收实战

7.1 发送 CAN 消息

CanTxMsg tx_msg; void CAN_SendMessage(uint32_t id, uint8_t *data, uint8_t len) { tx_msg.StdId = id; // 标准 ID(11 位) tx_msg.ExtId = 0; // 扩展 ID(不用) tx_msg.IDE = CAN_Id_Standard; // 标准帧 tx_msg.RTR = CAN_RTR_Data; // 数据帧 tx_msg.DLC = len; // 数据长度 for (int i = 0; i < len && i < 8; i++) { tx_msg.Data[i] = data[i]; } // 选择空闲的发送邮箱 uint8_t mailbox = CAN_Transmit(CAN1, &tx_msg); // 等待发送完成(可改为中断方式) while (CAN_TransmitStatus(CAN1, mailbox) != CAN_TxStatus_Ok); }

7.2 中断方式接收

void CAN_RX_Init(void) { CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE); // FIFO0 消息挂起中断 NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn); // CAN1 RX0 中断 } void USB_LP_CAN1_RX0_IRQHandler(void) { CanRxMsg rx_msg; // 读取接收到的消息 CAN_Receive(CAN1, CAN_FIFO0, &rx_msg); // 处理消息 uint32_t msg_id = rx_msg.IDE == CAN_Id_Standard ? rx_msg.StdId : rx_msg.ExtId; CAN_MessageHandler(msg_id, rx_msg.Data, rx_msg.DLC); }

八、CAN 工程陷阱与排查

8.1 常见问题速查

现象原因解决方法
发送失败,错误计数增加总线无其他节点应答 ACK至少需要 2 个节点才能通信
TEC > 255,进入 Bus-Off严重错误累积检查终端电阻、波特率、接线
接收数据乱码波特率不匹配用示波器测位时间,确认配置一致
偶尔丢帧过滤器配置过严/CPU 太忙加宽过滤器或提高中断优先级
CAN 一直报错未连接终端电阻120Ω 终端电阻必不可少!

8.2 终端电阻的物理原理

没有终端电阻时: CAN_H ──○──────────────○── │ 信号反射! │ CAN_L ──○──────────────○── 总线的两端:信号到达端点时发生反射! 反射信号与入射信号叠加 → 电平畸变 → 通信失败 加上 120Ω 终端电阻后: CAN_H ──○──────┬──────○── │ 120Ω │ CAN_L ──○──────┴──────○── 120Ω 电阻匹配双绞线特性阻抗 → 无反射 → 信号干净 ★ 规则:总线两端各接一个 120Ω 终端电阻(总计 60Ω 并联)

8.3 错误状态处理

// 监控 CAN 错误状态 void CAN_ErrorMonitor(void) { uint8_t tec = (CAN1->ESR >> 16) & 0xFF; // 发送错误计数器 uint8_t rec = CAN1->ESR & 0xFF; // 接收错误计数器 if (tec > 96) { printf("CAN WARNING: TEC=%d (接近 Bus-Off)\r\n", tec); } // Bus-Off 恢复 if (CAN_GetFlagStatus(CAN1, CAN_FLAG_BOF)) { printf("CAN BUS-OFF! 尝试恢复...\r\n"); // 清除 Bus-Off 标志,重新初始化 CAN1->MCR |= CAN_MCR_ABOM; // 自动离线恢复 // 或手动:CAN_Init(CAN1, &can_config); } }

九、CAN 高层协议简介

CAN 本身只定义了物理层和数据链路层。实际应用中需要高层协议:

协议应用领域特点
CANopen工业自动化、机器人对象字典、PDO/SDO、NMT 状态机
J1939商用车、柴油机29 位 ID、PGN 参数组、多包传输
DeviceNet工厂自动化基于 CIP 协议
ISO 15765 (UDS)汽车诊断27/29 位 ID + 多帧传输
自定义协议简单系统固定 ID → 功能映射表

十、总结

要点核心理解
仲裁ID 越小优先级越高,硬件自动仲裁,无需主机
帧结构从 SOF 到 EOF 的每一段都有明确功能
波特率位时间 = (1+BS1+BS2)×(BRP+1)/CAN_CLK
过滤器硬件过滤——CPU 只处理有意义的帧
终端电阻总线两端各 120Ω——没有它 CAN 不工作!
错误处理5 种错误检测 + 自动重传 + Bus-Off 保护

一句话总结:CAN 总线的核心价值在于实时性、可靠性和多主通信。理解仲裁机制是理解 CAN 的钥匙,用好过滤器是工程实践的关键。


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/15 14:17:29

aws-inventory完全指南:如何快速发现并管理你的AWS账户资源

aws-inventory完全指南&#xff1a;如何快速发现并管理你的AWS账户资源 【免费下载链接】aws-inventory Discover resources created in an AWS account. 项目地址: https://gitcode.com/gh_mirrors/aw/aws-inventory 想要快速掌握AWS账户中的所有资源吗&#xff1f;aws…

作者头像 李华
网站建设 2026/7/15 14:17:11

商业保理系统上线部署怎么做?融信云全流程培训+历史数据迁移+7x24运维一站式交付

商业保理系统上线部署远非"安装软件"那么简单。历史数据迁移的完整性风险、团队对新系统操作的适应周期、上线后的持续运维保障——每一个环节的疏漏都可能导致业务中断或风险。据统计&#xff0c;保理企业系统替换项目中数据迁移环节常出现问题&#xff0c;新系统上…

作者头像 李华
网站建设 2026/7/15 14:15:14

Three.js Canvas贴图教程

Canvas贴图 Canvas Texture ▶ 在线运行案例 案例合集&#xff1a; 三维可视化功能案例&#xff08;threehub.cn&#xff09;开源仓库github地址&#xff1a; https://github.com/z2586300277/three-cesium-examples400个案例代码: 网盘链接 你将学到什么 onBeforeCompile…

作者头像 李华
网站建设 2026/7/15 14:15:01

TectonicDB故障排除:常见问题与解决方案大全

TectonicDB故障排除&#xff1a;常见问题与解决方案大全 【免费下载链接】tectonicdb Database for L2 orderbook 项目地址: https://gitcode.com/gh_mirrors/te/tectonicdb TectonicDB作为专为L2订单簿设计的数据库&#xff0c;在高并发交易场景中可能会遇到各类运行问…

作者头像 李华