☰
CAN协议实战解析:从物理层帧结构到仲裁机制与硬件设计
2026/10/15 22:48:58 网站建设 项目流程

直接上干货。这篇聊 CAN 协议通信,重点不是照搬协议手册,而是把物理层、帧结构、仲裁机制、硬件设计这些和实际开发强相关的点串起来讲。搞嵌入式或者刚接触汽车电子通信的同学,看完应该能建立一条比较完整的知识链路,下次用 CAN 分析仪抓报文时,至少能看懂总线在做什么。

1. 为什么要用CAN:从RS-485轮询的憋屈体验说起

1.1 多点通信场景下,传统串口为什么越用越难受

早年做多节点采集项目,我最早用的是 RS-485。那套方案在节点少、数据量小的时候还能凑合,节点一多问题就来了。RS-485 本质上还是主从结构,主机按照地址挨个问,从机只能被动回答。假设总线上挂了 20 个节点,每个节点轮询一遍要几十毫秒,如果某个节点正好有紧急报警数据要上报,就得等主机轮询到它才能发,实时性完全取决于轮询周期。

更头疼的是故障隔离。RS-485 总线上一旦某个从机硬件异常、拉低了差分电平,主机和其他节点通信立刻受干扰,整个链路直接卡死。这时候排查故障节点只能一台一台往下拔,现场维护效率很低。而且 RS-485 没有内建的报文校验机制,CRC 得自己在应用层做,数据出错时主机只能靠超时重发来兜底,对实时性要求高的场景非常不友好。

我做过的那个项目后来就因为这个原因换成了 CAN。当时评估了几点:第一,CAN 是多主网络,任何节点都能主动发消息,紧急数据不用等轮询;第二,CAN 控制器硬件自带 CRC 校验和错误检测,软件层面省了一大堆事;第三,节点掉线不会拖垮整条总线,总线关闭机制能自动把故障节点隔离开。这些特性让 CAN 从硬件层面就解决了我之前靠软件硬扛的很多问题。

1.2 CAN 协议的设计目标与典型应用场景

CAN(Controller Area Network)是 Bosch 在上世纪八十年代末提出的串行通信协议,最初就是为了解决汽车内部线束越来越多、重量越来越大、信号互相干扰的问题。一辆车的动力系统、车身控制、诊断系统、娱乐系统连在一起,如果全部用点对点线束连接,线束数量和重量都不可接受。总线化的思路是让所有 ECU 挂到两根线上,按约定好的报文格式互相通信,线束成本大幅下降。

CAN 被 ISO 标准化为 ISO 11898 系列。它用两根线(CAN_H 和 CAN_L)组成差分传输,波特率最高支持 1 Mbps(经典 CAN),传输距离和波特率成反比。实时性上,CAN 的仲裁机制通过报文 ID 决定优先级,完全分布式,不需要主机参与调度。可靠性上,硬件层面有 CRC 校验、位填充、ACK 确认、位监控等多种错误检测手段,软件上还有错误状态机来做故障隔离。

所以现在除了汽车,工业控制、医疗设备、工程机械、船舶电子都在大规模用 CAN。比如伺服驱动器之间的同步、传感器采集节点的上报、PLC 和远程 I/O 模块的通信。对工程师来说,只要涉及多个 MCU 之间的可靠组网,CAN 几乎都是首选方案之一。

2. 总线上的电平与帧结构:CAN报文是怎样编码的

2.1 差分电平与显性/隐性位:两根线是怎么表示0和1的

CAN 物理层使用差分信号传输,这就意味着数据不是靠单根线对地电压的高低来决定 0/1,而是看 CAN_H 和 CAN_L 两根线之间的电压差。

总线只有两种逻辑状态:显性(Dominant)和隐性(Recessive)。显性对应逻辑 0,隐性对应逻辑 1。高速 CAN 的标准电平大致是这样:隐性状态下,CAN_H 和 CAN_L 都是 2.5 V 左右,差分电压约 0 V;显性状态下,CAN_H 被拉高到约 3.5 V,CAN_L 被拉低到约 1.5 V,差分电压约 2 V。收发器芯片负责把 MCU 的 TX/RX 逻辑电平转换成总线上的差分电平。

理解显性和隐性有一个关键点:显性位会覆盖隐性位。也就是说,总线上只要有一个节点发送显性位,不管其他节点发什么,总线最终呈现的就是显性。这个特性是后面仲裁机制的基础,一定要记住。

实际调试时,如果示波器直接接 CAN_H 和 CAN_L 看差分波形,能看到显性位是一个明显的跳变脉冲。判断总线工作正不正常,最简单的办法就是看静态电平:总线空闲时两端电压应该在 2.5 V 附近,如果 CAN_H 对地只有 2.5 V、CAN_L 也是 2.5 V,但差分始终是 0,大概率是收发器没工作或者没接终端电阻导致反射。

2.2 五种帧类型:数据帧、遥控帧、错误帧、过载帧、帧间隔

CAN 协议里一共有五种帧,工程上碰得最多的是数据帧,但另外四种也必须有概念,因为调试分析仪时经常会看到错误帧和过载帧。

  • 数据帧:承载实际数据,从发送节点发到接收节点。
  • 遥控帧:节点向总线上请求某个 ID 的数据。比如节点 A 想知道节点 B 的转速,就发一个相同 ID 的遥控帧,节点 B 收到后发送对应数据帧。
  • 错误帧:任何节点检测到总线错误时自动发出,由错误标志和错误界定符组成,用来通知全网。
  • 过载帧:接收节点来不及处理数据时请求延迟下一个数据帧。
  • 帧间隔:用于分隔两个连续的帧,保证总线同步。

数据帧里又有标准帧和扩展帧之分。标准帧的仲裁场是 11 位 ID,扩展帧是 29 位 ID。11 位 ID 在绝大多数工业场景够用,汽车诊断协议比如 UDS 里常用扩展帧。实际项目中帧 ID 的规划非常重要,因为 ID 直接决定仲裁优先级,ID 越小优先级越高。我之前一个项目里把周期性报文和事件性报文的 ID 段划分得很开,就是为了保证报警类报文永远优先抢占总线。

2.3 数据帧位场结构拆解:每个字段到底干什么用

一个标准数据帧从 SOF(帧起始)开始,依次是仲裁场、控制场、数据场、CRC 场、ACK 场和 EOF。我是这样拆解的:

  • SOF(Start of Frame):一个显性位,表示帧开始,同时用于同步所有节点。
  • 仲裁场:标准帧是 11 位 ID 加 1 位 RTR,扩展帧是 29 位 ID 加 SRR、IDE 位等。发送时逐位输出,优先级高的帧能够赢得仲裁。
  • 控制场:包含 IDE 位(区分标准/扩展帧)和 DLC(数据长度代码),表示数据场有多少字节。DLC 的范围是 0 到 8。
  • 数据场:真正要传的数据,最多 8 字节。经典 CAN 数据场很小,这是后来 CAN FD 要解决的核心痛点。
  • CRC 场:15 位 CRC 校验,覆盖从 SOF 到数据场的所有位,接收节点重新计算并比对,不一致就认为帧出错。
  • ACK 场:发送节点在 ACK 槽位释放总线为隐性,接收节点如果正确收到报文,就把 ACK 槽拉为显性,表示"我收到了"。这保证了发送节点至少知道总线上有一个节点成功接收。
  • EOF:7 个隐性位,表示帧结束。

数据场为什么最多只有 8 字节,这是很多新手会问的点。原因是经典 CAN 设计时主要面向控制和状态信号,单个信号通常只有几个字节,8 字节已经覆盖绝大多数场景。此外,较小的数据场也限制了单帧占用总线的时间,保证所有节点都有机会发消息。如果需要传大块数据,CAN 协议本身不管,需要靠应用层协议分包,比如 ISO-TP 或 CANopen 的 SDO 分段传输。

3. 逐位仲裁与错误状态机:CAN可靠性从哪来

3.1 CSMA/CA 与逐位仲裁:多个节点同时发数据,为什么不会乱

CAN 总线上的节点都遵循“先监听后发送”的规则,但即使如此,两个节点也可能在同一时刻发现总线空闲然后同时发送。这个时候就轮到仲裁机制上场了。

CAN 用的是一种叫 CSMA/CA(载波监听多路访问/冲突避免)的策略。和以太网的冲突检测后随机退避不同,CAN 是在发送过程中逐位仲裁,不破坏数据。原理非常简单:发送节点在输出每一位的同时,回读总线电平。如果自己发送的是隐性位(1),但总线上读到的是显性位(0),就说明有更高优先级的节点在同时发送,自己立刻停止发送、转为接收,等下次总线空闲再重发。

因为显性位覆盖隐性位,逐位比较下来,ID 数值越小的帧优先级越高。注意是二进制逐位比较,从最高位开始,一旦出现不同,发 0 的赢。比如 ID 0x100 和 ID 0x101 同时发送,从最高位一直比到低 7 位都一样,第 8 位一个是 0 一个是 1,发 0 的那个节点继续发,发 1 的被压下去。

很多人问仲裁会不会把两个帧弄乱。不会。关键在于所有节点回读时看到的总线电平是一致的,仲裁失败的那个节点在决定帧的边界之前就已经退出发送,最后总线上剩下的就是一个完整的、没有任何损坏的报文。这也是 CAN 为什么能在实时性要求很高的场合作为主力总线的原因。理解这个机制之后,分配 ID 的时候就要有意识地把高优先级不给周期性小报文,而留给报警、故障类的瞬时报文。

3.2 五种错误检测机制:CAN 防错是环环相扣的

CAN 之所以可靠,是因为错误检测不是靠单一手段,而是五层机制叠加。只要有一层发现异常,就会触发错误帧。

  • 位错误:发送节点在总线上回读每一位,如果回读到的电平和自己发出的不一致,除仲裁期间和 ACK 槽之外,都视为位错误。
  • 位填充错误:CAN 规定连续 5 个相同电平位之后,必须插入一个反相位。如果某个节点读到连续 6 个相同电平,就判定为填充错误。
  • CRC 错误:接收节点重新计算 CRC,与帧里的 CRC 字段不一致则报错。
  • 格式错误:帧里某些字段的固定格式(比如 EOF 必须是隐性位)不对,报格式错误。
  • ACK 错误:发送节点在 ACK 槽没有读到显性位,说明总线上没有节点成功接收,报 ACK 错误。

这些机制分别从编码、校验、应答层面兜底。实际使用中,偶尔看到一帧 CRC 错误不要太紧张,可能只是外界电磁干扰。但如果错误帧大量连续出现,就要认真查总线硬件了。

3.3 错误状态机:主动错误、被动错误和总线关闭

CAN 控制器内部有两个错误计数器:发送错误计数器(TEC)和接收错误计数器(REC)。出错时按规则增加,成功收发时减少。根据计数器的值,节点进入三种状态:

  • 主动错误(Error Active):TEC 和 REC 都小于 128。节点正常收发,检测到错误时发送主动错误标志(6 个显性位),可以积极影响总线。
  • 被动错误(Error Passive):TEC 或 REC 大于等于 128。节点还能收发,但检测到错误时只能发送被动错误标志(6 个隐性位),而且发送前要等待总线空闲。
  • 总线关闭(Bus Off):TEC 大于 255。节点彻底退出通信,既不发送也不接收,只有在总线上检测到 128 次 11 个连续的隐性位后,重新进入主动错误状态。

这套状态机的意义在于:一个故障节点不会无限破坏总线,而会被逐渐“降级”直至隔离。我之前处理过一次总线频繁错误帧的问题,最后排查出来就是某个节点收发器损坏,把总线电平一直拉在显性状态。由于它的错误计数器不断上涨,最后进入 Bus Off,其他节点才恢复正常通信。所以看到某个节点反复离线,可以考虑是不是因为它自己的错误计数器超限被协议隔离了,不一定是物理断开。

4. 硬件设计避坑:收发器、终端电阻和拓扑结构

4.1 收发器选型与电平适配

MCU 自带的 CAN 控制器输出的是逻辑电平(TX/RX),不能直接接到 CAN_H/CAN_L 物理总线上,中间必须加 CAN 收发器。收发器的作用就是把控制器送出的逻辑信号转换成差分电平信号,同时把总线上的差分信号还原成逻辑电平给控制器。

选型时最常遇到的问题是 MCU 的 I/O 电平是 3.3 V,而不少经典收发器是 5 V 供电。如果用 5 V 收发器接 3.3 V MCU,需要注意 MCU 的 RX 引脚能否容忍 5 V 输入。更稳妥的方案是直接用 3.3 V 供电的收发器,或者在 MCU 和收发器之间加电平转换。

我常用的是标准的 5 V 高速 CAN 收发器,但在 3.3 V MCU 上集成时,会仔细看收发器 RXD 输出的高电平是否超过 MCU 引脚耐压。不少新手在这里直接接上去,结果 MCU 烧了或者 RXD 一直读到异常电平。另一个重点是隔离:在电机驱动器、逆变器这类强电干扰多的场合,收发器和 MCU 之间最好加 CAN 隔离模块(集成隔离电源和数字隔离器),不然共地干扰和地环路会带来莫名其妙的总线错误帧。

4.2 终端电阻:为什么是 120 欧姆,为什么要放在两端

CAN 总线通信质量的一个关键因素就是终端电阻。CAN 规范要求在总线物理两端各接一个 120 欧姆电阻,不能多不能少。原因是高速 CAN 的传输线特性阻抗大约是 120 欧姆,终端电阻的作用就是匹配特性阻抗,尽量减小信号在总线末端产生的反射。

很多人只接了一个终端电阻,或者两个都放在总线同一端,这会导致反射波形叠加,严重时直接出现位错误。判断终端电阻是否正确的简单办法是:在总线空闲状态下,用万用表量 CAN_H 和 CAN_L 之间的电阻,一个完好的总线大约能测到 60 欧姆(两个 120 欧姆并联)。如果测到 120 欧姆,说明只有一端有终端电阻;如果测到接近 0,说明有短路。

实际项目中还有一个常见问题:为了调试方便,有人会用内置终端电阻的 USB-CAN 分析仪,同时又在外围板上接了终端电阻,结果变成 60 欧姆并联 120 欧姆,等效 40 欧姆。这会让总线差分电压偏低,导致近距离通信勉强正常,远距离通信就容易出错。所以要记住:总线上的终端电阻总等效值就是 60 欧姆,调试设备本身带不带终端电阻要先确认清楚。

4.3 波特率与总线长度、拓扑结构的取舍

CAN 的通信距离和波特率密切相关,这个表我整理过,实际项目中很好用:

波特率最大总线长度(约)典型场景
1 Mbps40 mECU 台架、机箱内通信
500 kbps100 m车辆内部网络、自动化产线
250 kbps250 m分布式 IO、楼宇控制
125 kbps500 m工业现场、设备间通信
50 kbps1000 m远程传感器采集

理论上距离还能更长,但需要降低波特率。距离限制的本质一方面来自信号衰减,另一方面来自 CAN 位定时要求。每个节点的采样点需要通过波特率配置里的同步段、传播段、相位段来计算,工程上最常用的做法是直接把采样点设置在 75% 到 87.5% 之间。如果遇到一对节点近距离通信正常、稍微拉长线缆就不稳定的情况,多半就是采样点位置不对,或者终端电阻没匹配。

拓扑结构上 CAN 推荐的是直线型总线(干线+支线),支线要尽量短。星型拓扑不是绝对不能用,但要特别小心分支导线的反射。在一条主线上用 T 型接头分出较长的支线,是导致信号反射和总线通信不稳定的常见原因。

5. 从CAN 2.0到CAN FD:新老协议怎么共存与选型

5.1 经典 CAN 的瓶颈和 CAN FD 带来的变化

经典 CAN 2.0 有两个硬伤:数据场最多 8 字节,总线波特率最高 1 Mbps。在自动驾驶、高带宽固件升级、大数据量日志记录这些场景下,8 字节一帧实在是太憋屈了。比如升级一个几百 KB 的固件,用 ISO-TP 一帧一帧拆,传输时间很长。

CAN FD(CAN with Flexible Data-rate)就是在经典 CAN 基础上做的扩展。它的关键改进有两个:一个是数据场最大可以到 64 字节;另一个是它采用双波特率机制——仲裁段仍然用较低的波特率(比如 500 kbps),保证和经典 CAN 节点兼容,进入数据段之后可以用更高速率(比如 2 Mbps 甚至更高)发送。这样既提升了带宽,又保留了和传统 CAN 节点在仲裁阶段的兼容性。

工程中选型很容易纠结。我的看法是:如果是全新设计,对带宽有明确需求,直接上 CAN FD 控制的 MCU 是划算的;如果系统里已经有大量挂载的老旧 CAN 节点,要新增节点并且需要通过同一个网络通信,那就要考虑混合组网的兼容性。CAN FD 节点可以和经典 CAN 节点在同一网络上运行,但要注意,混合网络中仲裁段的波特率必须一致,而且经典 CAN 节点无法理解 CAN FD 帧,所以报文过滤和 ID 规划要非常小心,避免经典 CAN 节点误判。

5.2 上层协议栈:CANopen、J1939 在里面扮演什么角色

裸 CAN 只负责数据帧能不能可靠地传过去,但"传过去的 8 个字节代表什么意思"需要应用层协议来规定。这就是 CANopen、J1939、DeviceNet 这些协议存在的意义。

  • CANopen:在工业控制领域非常流行,定义了对象字典(OD)、PDO(过程数据对象)、SDO(服务数据对象)等机制。PDO 适合周期性快速交换实时过程数据,SDO 适合按需读写参数。做伺服、变频器、IO 模块通信时经常遇到它。
  • J1939:主要用在卡车、工程机械和农业车辆上,是基于 CAN 的高层协议,定义了参数组编号(PGN)、源地址(SA)和专有报文。如果你做过重型车辆 ECU 之间的通信,肯定绕不开它。
  • DeviceNet:曾经在工厂自动化领域用得较多,基于 CAN 定义了一套完整的设备模型,美国和国内老产线里还能见到。

我的建议是不要重复造轮子。只要项目需要对接多种设备、需要标准化诊断和管理,就直接沿用 CANopen 这类成熟协议栈,而不是自己在应用层临时定义一套私有协议。私有协议在开发阶段看着简单,到后期扩展设备和跨团队协作时就容易成为维护噩梦。

5.3 项目选型路径:什么情况选经典 CAN,什么情况选 CAN FD

实际做方案时有个简单的判断流程。先统计单帧需要传输的数据量:如果绝大多数报文不超过 8 字节,总线上最大波特率 1 Mbps 够用,并且 MCU 选型时带经典 CAN 控制器的资源更便宜,那就没必要为 CAN FD 付溢价。如果某些功能(比如 OTA 升级、高速数据记录)已经明显扛不住 8 字节限制,或者总线负载在 500 kbps 下已经超过 50%,将来会继续加节点,那直接选 CAN FD 控制器更省事。

另外注意,选择 CAN FD 控制器不代表一定要启用 FD 格式,它完全可以向下兼容,用经典 CAN 格式发送报文。所以哪怕目前网络里都是老节点,也可以先买支持 CAN FD 的新硬件,等整个网络升级条件成熟,再分区域切换。这种平滑过渡策略在实际项目里很有效。

6. 实战调试:示波器、分析仪与典型故障排查链路

6.1 示波器测物理层波形:先确认总线是否健康

拿到一套有问题的 CAN 网络,我不会先急着抓报文,而是先拿示波器看物理层波形。示波器通道 1 接 CAN_H,通道 2 接 CAN_L,用差分模式观察。总线空闲时,两个通道都应该在 2.5 V 左右;总线上有数据时,能看到显性位的差分跳变。

正常情况下显性位的差分电压应该在 1.5 V 到 3 V 之间。如果看到差分电压偏低,比如只有 0.5 V,重点检查终端电阻是否等效为 60 欧姆,以及是不是有多余的负载。如果波形边沿出现明显过冲和振铃,说明阻抗不匹配,看看支线是不是太长、终端电阻位置对不对。

示波器还有一个用途是看波特率。量两个显性位之间的时间间隔,换算成波特率,可以快速验证总线配置是不是和设计一致。我之前排查过一个通信时好时坏的现场问题,最后就是靠示波器发现实际波特率是 480 kbps 而不是配置的 500 kbps,原因是某个节点的晶振精度太差,分频之后产生累计误差。

6.2 USB-CAN 分析仪抓报文:从 ID 过滤到报文分析

物理层正常之后,接下来就是用 USB-CAN 分析仪抓总线上的报文。市面上常见的分析仪在 PC 端会显示时间戳、ID、DLC、数据字节和帧类型。第一步是确认总线上有没有错误帧,如果有,先把错误类型和时间周期记录下来。

分析报文时,先根据项目里定义的报文矩阵,把周期性和事件性报文分出来。周期性报文在总线正常时应该稳定出现,发现某个 ID 不出现了,说明对应的节点可能掉线或处于 Bus Off 状态。事件性报文出现在某个操作触发后,比如按下急停按钮前后,重点看触发时刻前后的报文序列。

如果想对报文内容做更细致的解析,就需要 DBC 文件了。DBC 定义了每个 ID 里各个信号在哪个字节的哪些位,用怎样的缩放系数和偏移量换算成物理量。用支持 DBC 导入的分析软件,把每一个信号转换成转速、温度、压力等真实物理值,才能判断数据是否合理。拿原始字节硬看,转速和油压用十六进制直接读很难发现问题。

6.3 典型故障排查链路:从全部无通信到偶发错误帧

调试 CAN 总会遇到几类典型故障,我把排查链路整理成了一套固定套路。

第一类:所有节点都无法通信。先万用表测 CAN_H 与 CAN_L 之间的电阻,正常应该在 60 欧姆左右。如果接近 0,说明总线短路,重点查有没有线缆破损或者接错端子。如果电阻无穷大,说明总线断路或没有接终端电阻。总线空闲电平异常也要考虑,CAN_H 和 CAN_L 对地电压如果偏离 2.5 V 太远,多半是某个收发器坏了或者供电有问题。

第二类:某个节点收发不正常,其他节点没问题。先把问题节点的 CAN 控制器配置从头对一遍,特别是波特率位定时参数。确认波特率无误后,单独把该节点接在一条短总线上测试,排除线缆和连接器问题。如果短总线正常,怀疑是支线过长或节点耦合电容过大导致信号畸变。

第三类:总线偶发错误帧,通信成功率不稳定。这时候要拿示波器看波形毛刺和振铃,同时用分析仪记录错误类型。CRC 错误多,通常是电磁干扰;位错误多,通常是收发器边沿过缓或终端电阻不对。如果错误帧呈现周期性,很可能是某个节点的报文 ID 冲突,或者两条总线错误地并联在一起了。

这些排查步骤的核心原则是:先物理层后数据链路层,先用万用表和示波器确认总线电气状态,再上分析仪看协议层,不要上来就怀疑软件配置。软件配置出问题的概率其实比硬件低很多,但很多人排查时第一个动作就是反复改代码,结果问题依旧,最后发现是线缆被压断了。

7. 最后分享几个我自己调试 CAN 时的小习惯

总线上电之前,先量 CAN_H 和 CAN_L 之间的静态电阻,确认是 60 欧姆左右再接线。新到的分析仪先确认它自带终端电阻的开关状态,避免和总线终端电阻叠加。给每个节点连接器贴上明确的 ID 标签,方便排查时逐个断开测试。配置波特率时留意采样点参数,大部分默认配置是 80% 附近,对抗总线干扰的稳定性比 50% 好很多。

CAN 协议本身规范结构严谨,但协议只是基础,真正让系统稳定运行的是对物理层、报文规划、故障机制的深刻理解。希望这篇能把 CAN 的骨架串起来,工程和开发过程中遇到具体问题再去查文档,心里就有底了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询