CAN总线
CAN 总线是什么?一篇讲清差分信号、帧结构、仲裁和错误处理
做嵌入式通信时,UART、I2C、SPI 解决的是“设备怎么连”,而 CAN 总线 解决的是“很多节点怎么稳定地挂在一根总线上,而且还能优先级仲裁、错误重发、长期可靠运行”。
这也是为什么 CAN 在汽车电子、工业控制、BMS、电机驱动、智能设备网络里一直很强势。它不是最快的,也不是最简单的,但它非常均衡:
- 抗干扰强
- 多节点能力好
- 仲裁机制清晰
- 错误检测和恢复能力强
对于做 MCU、工业设备、照明电源外围控制、分布式模块网络的人来说,CAN 是非常值得吃透的一条总线。
这篇文章就围绕 5 个核心点展开:
- CAN 为什么抗干扰
- 一帧 CAN 数据长什么样
- 仲裁到底是怎么赢的
- 出错了为什么还能继续跑
- MCU 落地时要注意什么
1. CAN 总线到底是什么?
CAN(Controller Area Network)最早是为汽车电子设计的多节点串行通信总线。
它的核心特点不是“能发数据”——所有总线都能发数据——而是:
在多节点共享同一条总线的情况下,仍然能高可靠地完成通信。
一个典型的 CAN 网络大概长这样:
ECU A ----+
|
ECU B ----+------ CAN_H / CAN_L 总线 ------ 120Ω
|
ECU C ----+
几个关键词先记住:
- 差分信号:CAN_H 与 CAN_L 配合传输
- 多主通信:不只有一个主站,多个节点都可以发
- 无损仲裁:谁优先级高谁先发,其他节点自动退出
- 错误重发:发现错误就丢弃本帧并自动重试
这四点基本就是 CAN 的灵魂。
2. 为什么 CAN 抗干扰?——先看物理层
CAN 最重要的底层特性,就是 差分传输。
总线上通常有两根线:
CAN_HCAN_L
接收端真正关心的,不是哪根线绝对电压是多少,而是两者的电压差。
2.1 隐性与显性
CAN 总线上的位分成两种状态:
- 隐性(Recessive,逻辑 1)
- 显性(Dominant,逻辑 0)
它们最直观的区别是:
| 状态 | CAN_H | CAN_L | 特点 |
|---|---|---|---|
| 隐性 1 | 接近中间电平 | 接近中间电平 | 两线差值很小 |
| 显性 0 | 往上拉 | 往下拉 | 两线差值明显增大 |
所以你可以把它简单理解成:
- 隐性:两线“几乎重合”
- 显性:一根上扬,一根下压,形成明显差分
这就带来一个很大的好处:如果外部噪声同时加到两根线上,接收器看到的差分值仍然比较稳定。
这也是 CAN 能在汽车、电机、继电器、强干扰工业现场里长期稳定跑的关键原因之一。
2.2 为什么两端还要 120Ω?
标准 CAN 总线通常要求在总线两端各接一个 120Ω 终端电阻。
作用是:
- 抑制信号反射
- 改善波形质量
- 保证高速通信时边沿不乱飞
如果你总线拓扑不对,或者终端不合适,最常见的问题就是:
- 波形失真
- 某些波特率下偶发错误
- 节点一多就不稳定
所以 CAN 的可靠,不只是协议设计得好,布线和匹配也非常关键。
3. 一帧 CAN 数据长什么样?
一帧经典 CAN 数据帧可以概括成下面这些字段:
SOF -> 仲裁段(ID) -> 控制段 -> 数据段 -> CRC -> ACK -> EOF
可以整理成表:
| 字段 | 作用 |
|---|---|
| SOF | 帧开始 |
| 仲裁段 | 包含 ID,决定优先级 |
| 控制段 | 数据长度等控制信息 |
| 数据段 | 真正载荷,经典 CAN 最多 8 字节 |
| CRC | 校验正确性 |
| ACK | 其他节点确认收到 |
| EOF | 帧结束 |
3.1 SOF
SOF(Start Of Frame)表示一帧开始,通常是显性位。
作用很简单:告诉全网“有人要开始发了”。
3.2 仲裁段
这是 CAN 最有辨识度的部分。
仲裁段里的 ID 不只是“地址”或者“消息编号”,它还决定了:
谁优先发。
3.3 数据段
经典 CAN 的数据段最多是 8 字节。这也是为什么很多设备会把消息拆得比较细。
如果你看到的是 CAN FD,那最大长度可以更大,但这篇先只讲经典 CAN 的核心逻辑。
3.4 CRC 与 ACK
CRC 用来校验整帧是否正确。
ACK 表示:
- 有其他节点成功收到了这一帧
- 并且愿意给出确认
这让 CAN 不只是“发了就完”,而是能知道“有没有真的被别人收到了”。
4. CAN 最厉害的地方:无损仲裁
很多总线在多节点同时发的时候会“打架”。
但 CAN 的设计很巧妙:
多个节点可以同时开始发,但优先级低的节点会自动退出,不会把高优先级帧破坏掉。
这就叫 无损仲裁。
4.1 基本规则
CAN 仲裁的核心规则非常简单:
- 显性 0 压过 隐性 1
- ID 越小,优先级越高
因为仲裁是逐位进行的,所以:
- 如果某个节点想发
1 - 但总线上实际读到了
0 - 它就知道自己输了
- 于是立刻停止发送,转成接收模式
4.2 举个直观点的例子
假设三个节点同时发:
- Node A:ID =
0x120 - Node B:ID =
0x080 - Node C:ID =
0x300
在逐位比较过程中,只要某一位上:
- Node 自己发的是隐性
1 - 总线上出现的是显性
0
它就会退出。
最终 ID 最小 的那个节点胜出。
这套机制有两个很大的好处:
- 高优先级消息一定更容易抢到总线
- 赢的那帧不会被碰坏
这就是为什么 CAN 特别适合:
- 制动/转向/电机控制这类实时性更高的消息
- 多个控制器共享同一条通信通道的场景
5. 为什么 CAN 可靠?——因为它会主动发现错误
CAN 的另一个强项,是错误检测非常全面。
常见错误包括:
- 位错误
- 填充错误
- CRC 错误
- 格式错误
- 应答错误
5.1 发现错误后会怎样?
如果某个节点发现当前帧不对,它不会“装作没看见”,而是会主动发出 Error Frame(错误帧)。
这相当于告诉全网:
这帧作废,别信,重新来。
然后发送节点会自动重发。
5.2 这意味着什么?
这意味着 CAN 在现场干扰出现时,不是“默默错掉”,而是:
- 主动检测
- 主动打断错误帧
- 自动触发重发
这对工业系统非常重要。
比如在电机驱动、BMS、工业模块通信里,比起偶尔丢一帧,更怕的是:
- 收到了错误数据却没发现
- 系统还继续按错数据执行
CAN 的错误机制,就是为了解决这类风险。
6. MCU 上怎么落地 CAN?
对嵌入式开发来说,理解协议只是第一步,真正能用起来还要落到硬件和驱动上。
典型结构:
MCU CAN 控制器 <-> CAN 收发器 <-> CAN_H / CAN_L 总线
6.1 MCU 里通常包含什么
很多 STM32、NXP、TI、GD、英飞凌等 MCU 会内置 CAN 控制器。
你真正还需要外接的是:
- CAN 收发器(比如 TJA1050、SN65HVD 系列等)
因为 MCU 内部 CAN 外设主要负责:
- 帧格式处理
- ID 过滤
- 仲裁时序
- 错误检测
而真正把逻辑电平变成总线差分信号的是收发器。
6.2 过滤器很关键
CAN 常见的一个特点是:总线上所有节点都能看到广播的帧。
所以 MCU 里一般要配:
- 标准帧/扩展帧过滤
- 接收 FIFO
- 中断回调或轮询处理
否则消息一多,CPU 很容易被无关帧淹没。
6.3 波特率与总线长度要匹配
CAN 也不是你想跑多快就跑多快。
经验上:
- 波特率越高,可允许的总线长度越短
- 总线越长,波特率就要更保守
这和 RS485 很像,本质上都涉及传输线和边沿质量问题。
7. CAN 在实际产品里常用来干什么?
7.1 汽车电子
这是 CAN 最经典的应用场景。
比如:
- 发动机 ECU
- ABS
- 仪表
- 车身控制模块
- 电池管理系统(BMS)
- 电机控制器
这些模块都可以挂在同一条总线上交换状态和指令。
7.2 工业现场
在工业系统里,CAN 也很常见,尤其是:
- 电机驱动器
- 伺服控制器
- PLC 扩展模块
- 传感器网络
- AGV / 机器人系统
因为它:
- 节点多
- 布线省
- 抗干扰比普通 UART 强
- 比较适合实时状态广播
7.3 电源 / 模块化设备网络
对于做电源或控制模块的人来说,CAN 适合用在:
- 多模块并联状态同步
- 主控与多个功率板通信
- 分布式设备告警广播
- 上位机批量读取运行数据
虽然照明电源里 RS485/Modbus 更常见,但在更复杂的多控制器系统里,CAN 也很有价值。
8. 调试 CAN 最常见的坑
下面这张表基本能覆盖大部分现场问题。
| 现象 | 常见原因 | 排查方向 |
|---|---|---|
| 完全没通信 | 收发器未工作、波特率不一致 | 先查硬件、时钟、位时序 |
| 偶发错误帧 | 终端电阻不对、布线反射 | 检查两端 120Ω 和拓扑 |
| 某节点收不到数据 | 过滤器配置错 | 检查 ID / 掩码设置 |
| 总线一直 busy | 某节点异常持续发显性 | 看是否节点损坏或软件卡死 |
| 一发就仲裁失败 | ID 优先级低 | 检查是否被高优先级帧压制 |
| 干扰大时掉线 | 接地/布线/屏蔽处理差 | 看供电、电缆、共地策略 |
排查顺序建议
建议按这个顺序查:
-
先查硬件层
- 收发器型号对不对
- CAN_H / CAN_L 有没有接反
- 两端终端电阻有没有配
-
再查位时序
- 波特率
- 采样点
- 时钟源配置
-
再查 ID 和过滤器
- 帧格式是否匹配
- 掩码是否把消息挡掉了
-
最后看错误计数器和错误状态
- 是否进入 error passive / bus off
9. 一个最小接收思路示例
下面给一个偏思路型的示例,不依赖具体 HAL 版本:
typedef struct
{
uint32_t id;
uint8_t dlc;
uint8_t data[8];
} can_msg_t;
void can_rx_callback(can_msg_t *msg)
{
switch (msg->id)
{
case 0x101:
// 电机转速反馈
break;
case 0x102:
// 电流反馈
break;
case 0x201:
// 告警状态
break;
default:
break;
}
}
工程里一般会再配合:
- 中断接收
- FIFO 缓冲
- ID 分类分发
- 超时监测
- 错误状态机处理
如果消息量比较大,最好不要在中断里做太重的解析逻辑,而是:
- 中断里只搬运
- 主循环或任务里做协议层处理
10. 一张表快速记住 CAN 总线
| 项目 | 说明 |
|---|---|
| 物理层 | 差分双线:CAN_H / CAN_L |
| 通信方式 | 多主广播 |
| 典型位值 | 显性 0、隐性 1 |
| 仲裁规则 | 显性压过隐性,ID 越小优先级越高 |
| 可靠性机制 | CRC、ACK、错误帧、自动重发 |
| 数据长度 | 经典 CAN 最多 8 字节 |
| 常见场景 | 汽车 ECU、BMS、工业控制、电机驱动 |
11. 总结
CAN 总线真正厉害的地方,不只是“能通信”,而是它在多节点、强干扰、实时性要求高的环境里,依然能保持可靠。
你可以把它的核心价值概括成四句话:
- 差分传输,抗干扰强
- 逐位仲裁,多主也不乱
- 错误检测完善,坏帧不硬扛
- 非常适合广播式实时控制系统

浙公网安备 33010602011771号