CAN 总线协议深度解析——从仲裁机制到工程实战 一、引言CANController Area Network总线是汽车电子和工业控制领域最重要的通信协议之一。它诞生于 1983 年Bosch 公司至今已有 40 年历史却仍然是现代汽车的核心通信骨架。从一辆普通家用轿车的 30 个 ECU电子控制单元到工业机器人关节的实时通信CAN 总线的身影无处不在。本文将全面解构 CAN 总线CAN 的物理层与差分信号仲裁机制与优先级策略帧格式标准帧、扩展帧、CAN FD位时序与波特率计算过滤器配置的深入理解STM32 bxCAN 的实战配置CAN 总线的工程陷阱与排查方法平台STM32F103bxCAN基本 CAN 控制器 收发器TJA1050 测试工具CAN 分析仪USB-CAN二、CAN 总线基础2.1 为什么用 CAN——对比其他总线┌─────────────────────────────────────────────────────────┐ │ CAN vs 其他工业通信协议 │ ├──────────┬──────────┬──────────┬──────────┬─────────────┤ │ 特性 │ CAN │ RS-485 │ I2C │ SPI │ ├──────────┼──────────┼──────────┼──────────┼─────────────┤ │ 通信方式 │ 多主 │ 主从 │ 主从 │ 主从 │ │ 错误检测 │ ★★★★★ │ ★☆☆☆☆ │ ★☆☆☆☆ │ ★☆☆☆☆ │ │ 最大节点 │ 无限制 │ 256 │ 127 │ 仅 1 从机 │ │ 实时性 │ ★★★★★ │ ★★☆☆☆ │ ★☆☆☆☆ │ ★★★★☆ │ │ 距离/速率 │ 40m1M │ 1200m100k│ 1m100k │ 0.1m10M │ │ 硬件成本 │ 中 │ 低 │ 极低 │ 极低 │ │ 适用场景 │ 汽车/工业│ 仪表 │ 板内通信 │ 板内高速 │ └──────────┴──────────┴──────────┴──────────┴─────────────┘ ​ 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 ArbitrationCAN 的核心设计CSMA/CD-AMP载波侦听多路访问/冲突检测-通过仲裁的优先级消息 ​ 三个节点同时开始发送 ​ 起始 ID 位 数据... ─────┬────┬────┬────┬────┬────┬────┬── │SOF │ ID10│ID9 │ID8 │ID7 │ID6 │ ... NodeA│ 0 │ 0 │ 0 │ 1 │ 1 │ 0 │ ... (ID0x0C...) NodeB│ 0 │ 0 │ 0 │ 1 │ 1 │ 1 │ ... (ID0x0E...) NodeC│ 0 │ 0 │ 1 │ 0 │ 0 │ 0 │ ... (ID0x20...) │ │ │ │ │ │ │ 总线 │ 0 │ 0 │ 0══│ 1 │ 1 │ 0══│ ... │ │ │ ║ │ │ │ ║ │ │ │ │ ║ │ │ │ ║ │ │ │ │NodeC 发送隐性(1) │ │ │ │但总线是显性(0)→ │ │ │ │NodeC 检测到冲突→ │ │ │ │★ NodeC 退出仲裁 │ │ │ │ NodeB 发送隐性(1) │ │ │ 但总线是显性(0)→ │ │ │ ★ NodeB 退出仲裁 │ │ │ │ │ │ ★ NodeA 赢得仲裁继续发送 │ │ │ 仲裁结果NodeA(ID0x0C) NodeB(ID0x0E) NodeC(ID0x20) ID 越小优先级越高3.2 仲裁的关键规则规则 1发送方同时监听总线 → 发送隐性位1时检测到总线为显性0 → 说明有更高优先级的节点在发送 → 立即退出 ​ 规则 2仲裁失败方自动变为接收方 → 不丢失数据只是发送权被抢占 → 等当前帧传输完毕后自动重试 ​ 规则 3ID 越小优先级越高 → 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 ││ 73 │ │ │ └──┘└─────┘└──┘└──┘└──────────┘└──┘└──┘└──┘└───────┘└──┘└──┘ │ │ │ └───────────────────────────────────────────────────────────────────┘ ​ 各字段含义 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 Space4.2 扩展帧29 位 IDvs 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 // 选取BRP3, BS111, BS24 // TQ (31)/36MHz 111.1ns // 位时间 (1114) × 111.1ns 16 × 111.1ns 1.778μs // 波特率 1/1.778μs ≈ 562kbps接近 500kbps满足需求 ​ // 精确 500kbps 的配置 // BRP5, BS16, BS24, SJW1 // TQ (51)/36MHz 166.67ns // 位时间 (164) × 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): 掩码 │ │ │ │ 掩码 bit1 → 该位必须匹配 │ │ 掩码 bit0 → 该位不关心Dont Care │ │ │ │ 示例接收 0x100~0x1FF 的所有帧 │ │ FxR1 0x100 (期望 ID0x100) │ │ FxR2 0xF00 (掩码只关心高 4 位) │ │ → 匹配0x100, 0x101, ..., 0x1FF │ └─────────────────────────────────────────────┘ 标识符列表模式List Mode ┌─────────────────────────────────────────────┐ │ 滤波器寄存器 0 (CAN_FxR1): ID 1 │ │ 滤波器寄存器 1 (CAN_FxR2): ID 2 │ │ │ │ 只接收这两个具体 ID 的帧 │ │ → 精确匹配不处理无关帧 │ └─────────────────────────────────────────────┘6.2 过滤器配置代码// 场景接收 ID0x100, 0x200, 0x300~0x3FF 的帧 void CAN_Filter_Config(void) { CAN_FilterInitTypeDef filter; // 过滤器 0精确匹配 ID0x100 和 ID0x200列表模式 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; // 标准 ID11 位 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 的每一段都有明确功能波特率位时间 (1BS1BS2)×(BRP1)/CAN_CLK过滤器硬件过滤——CPU 只处理有意义的帧终端电阻总线两端各 120Ω——没有它 CAN 不工作错误处理5 种错误检测 自动重传 Bus-Off 保护一句话总结CAN 总线的核心价值在于实时性、可靠性和多主通信。理解仲裁机制是理解 CAN 的钥匙用好过滤器是工程实践的关键。