直接上干货。这篇聊 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 协议的设计目标与典型应用场景CANController 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 VCAN_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。我是这样拆解的SOFStart 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 槽拉为显性表示我收到了。这保证了发送节点至少知道总线上有一个节点成功接收。EOF7 个隐性位表示帧结束。数据场为什么最多只有 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 ActiveTEC 和 REC 都小于 128。节点正常收发检测到错误时发送主动错误标志6 个显性位可以积极影响总线。被动错误Error PassiveTEC 或 REC 大于等于 128。节点还能收发但检测到错误时只能发送被动错误标志6 个隐性位而且发送前要等待总线空闲。总线关闭Bus OffTEC 大于 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 FDCAN 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 的骨架串起来工程和开发过程中遇到具体问题再去查文档心里就有底了。 SEO 优化官网定制响应式建站教育培训建站