ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

CAN总线与车辆协议全景解析:从物理层到应用实战

CAN总线与车辆协议全景解析:从物理层到应用实战 做车辆调试这么多年我从没见过哪条总线像CAN这样长青。二十多年前第一辆量产车装上CAN的时候没人想到它今天会覆盖从车窗升降到发动机管理的几乎所有车载节点。而“CAN总线与车辆协议全景解析”这个标题背后核心就两件事一是搞清楚那两根线上的电平变化到底怎么来的二是弄明白上层跑的协议林林总总该怎么选、怎么用。这篇内容适合三类人刚入行的汽车电子工程师、做机器人和自动化设备想用CAN通信的嵌入式开发者以及那些手里有CAN卡但玩不转上位机的维修调试人员。我会从物理层的电压差讲起层层拆到J1939、UDS、CANopen这些协议最后用一份阻尼电机通过CAN实现关节控制的实际案例收尾。1. 为什么CAN总线能统治汽车网络从设计思路说起理解一套技术最忌讳一上来就背帧格式、记寄存器。当初Bosch设计CAN总线的时候面对的是八十年代汽车线束越来越重、电子模块越来越多、点对点连线成本没法接受这些非常现实的问题。所以CAN从一开始就不是奔着“高性能通信”去的它是奔着“在糟糕的电气环境下、用尽可能少的线把一堆节点可靠地连起来”去的。1.1 车载网络要解决的三个核心痛点第一个痛点是布线复杂度。传统点对点方式下N个ECU如果要互相通信需要N×(N-1)/2条连接五六个节点还能忍上了二十个节点就是灾难。CAN用一对双绞线挂所有节点线束重量和成本直接降一个数量级。第二个痛点是干扰。发动机点火线圈、雨刷电机、电磁阀这些负载在车上密集存在普通单端信号根本扛不住。CAN采用差分传输两根线上的噪声基本共模接收端只看差值干扰就被抵消掉了。第三个痛点是多主竞争。车上不存在一个绝对的中心节点来调度所有通信每个控制器都在实时产生数据转速信号、温度信号、开关状态这些优先级完全不同CAN用报文ID仲裁机制让高优先级的信号天然抢占总线不需要额外的调度器。这套设计让CAN在可靠性和实时性之间取得了极佳的平衡成本又低。后来J1939基于它覆盖了整个商用车和工程机械UDS基于它实现了排放法规要求的诊断接口CANopen基于它把伺服驱动器和PLC串成了产线网络。你会发现在车辆协议全景里CAN永远处于最底层它是所有上层协议的地基。1.2 车载总线家族里CAN的位置在车里面CAN并不是唯一的总线但它是绝对的主干。低速廉价的LIN用来控制车窗、后视镜这些对实时性要求不高的执行器一条LIN总线上挂一个主机几个从机成本极低。FlexRay和面向音视频的MOST基本已经边缘化前者只在极少数高端底盘中存在后者早被以太网取代。以太网正在快速进入车载领域像OTA升级、ADAS摄像头数据、诊断刷写这些动辄几十兆甚至千兆的场景CAN根本带宽不够用。但以太网的物理层成本和电源管理复杂度远超CAN所以今后很长时间里车辆网络会是CAN打底、以太网做骨干的混合架构。只要车上还有廉价的传感器和执行器CAN就不会消失。2. CAN物理层核心电压差是怎么改变和产生的网上随手一搜“CAN总线”最常见的疑问就是“电压差是怎么改变的”。说实话这个问题问得很好因为物理层不搞清楚后面看波形、查故障、算时序都会发虚。我尽量把原理掰开揉碎讲。2.1 总线电平的三个状态与两个电压差CAN总线在物理上就是两根线叫做CAN_H和CAN_L。通信时线上的状态只有两种显性和隐性。所谓隐性就是差分电压约为0V的状态所谓显性就是差分电压约为2V的状态。注意这里说的是“差分电压”也就是CAN_H电压减去CAN_L电压的差值。隐性时总线处于一个被动的松弛状态。CAN收发器的输出级有一个偏置电路把CAN_H和CAN_L都拉到2.5V附近此时CAN_H减CAN_L大约是0V总线处于“默认没人说话”的状态。显性时收发器主动驱动总线CAN_H被拉到约3.5VCAN_L被拉到约1.5V差分电压3.5-1.52V。就这么个简单逻辑差分0V是“1”差分2V是“0”CAN就靠着这两个稳定的电压差传数据。2.2 收发器内部是怎么实现电压切换的电压差的改变本质上是CAN收发器内部输出晶体管的开关动作。以一颗典型的CAN收发器比如TJA1050为例它内部有CAN_H和CAN_L两个输出驱动。发送逻辑“1”隐性时两个驱动管都不导通总线靠内部偏置电阻自然回落到2.5V-2.5V差分约0V。发送逻辑“0”显性时CAN_H驱动管导通把CAN_H端通过一个约60Ω的内部电阻拉到3.5VCAN_L驱动管同步导通把CAN_L端通过另一个约60Ω的内部电阻拉到1.5V于是线上就有了2V的差分电压。这里有很重要的一点显性驱动是主动的隐性偏置是被动的。实际抓波形时你会发现显性跳变非常陡而隐性恢复相对平缓。如果总线上所有节点都处于隐性那总线只是稳定在2.5V附近只要有一个节点发出显性位整个总线就被拉到显性。这种“线与”特性正好是实现多主仲裁的基础——多个节点同时发送时看到总线被拉成显性而自己原本要发隐性的节点就会立刻退出竞争高优先级报文优先通过。2.3 120欧终端电阻为什么不能省终端电阻在物理层中扮演的角色比我早期想的复杂得多。CAN标准要求在总线两端各接一个120Ω电阻目的是匹配传输线阻抗。两根双绞线的特性阻抗大约就是120Ω如果总线末端不接匹配电阻高速跳变的信号到达终端会发生反射反射回来的能量会叠加到原始信号上造成波形振铃、码元抖动轻则误码重则整个网络瘫痪。实践中最典型的故障案例一条CAN总线上接了示波器没接终端电阻低速时通信正常换成500kbps以上波特率时偶发报错一测波形发现上升沿上全是毛刺接上120Ω电阻后波形立刻干净了。还要注意有些设备内置了终端电阻接到总线上的时候如果两头都接了内置电阻设备等效并联电阻就成了60Ω也会导致信号异常。所以动手做CAN网络前先数清楚总线上到底哪两个节点承担终端匹配职责。2.4 测量与示波器测试的实操要点测CAN信号最直观的方法是拿示波器探CAN_H和CAN_L。双通道分别接两根线通道A接CAN_H通道B接CAN_L然后把数学通道设置为A-B就能直接看到2V差分的曼彻斯特编码波形。示波器带宽不需要很高100MHz就足够看CAN-FD之前的绝大多数场景。采样率倒是要留意至少得是波特率的20倍以上否则波形上的细节比如总线仲裁位和ACK位根本看不清。一个常见误区是测量时只用探头夹子接GND。车辆环境里GND往往带噪声而且有些测试点的GND和示波器地之间存在压差我建议用隔离探头或者把示波器供电隔离否则轻则波形有毛刺重则烧探头参考地。早期在实验室做ECU测试时我就因为探头地线夹在车身钣金上结果复位信号被共模干扰打出无数个假边沿排查了整整两天。3. 车辆协议全景从裸CAN到应用层的分层体系知道物理层电平之后还得知道这些电平怎么组织成有意义的“话语”。CAN总线协议分很多层裸CAN只规定了帧结构、仲裁、错误检测这些基础能力而真正的“车辆协议”指的是上层怎么定义ID、怎么组织数据、怎么做诊断。这部分才是做整车和车载设备对接时天天打交道的核心。3.1 数据帧结构ID、数据段和校验字段先看最底层的数据帧。CAN 2.0A标准帧由帧起始、仲裁段、控制段、数据段、CRC段、ACK段和帧结束构成。仲裁段里最重要的就是11位ID这个ID不只是报文的“名字”它直接决定总线抢占优先级ID数值越小优先级越高。控制段里有DLC也就是数据场长度指示范围是0到8字节。数据段最多8字节这是老CAN带宽受限的根本原因。在2021年之后CAN FD引入了64字节数据场但经典CAN的8字节在今天仍然占据绝大多数车载应用。这里提一个很多人容易忽略的细节CRC段覆盖的范围是帧起始到数据段而ACK段有个应答间隙发送节点在这个位里释放总线接收节点如果正确收到报文会在ACK位把总线拉成显性来应答。如果总线上完全没有节点响应发送节点会检测到ACK显性失败并触发错误帧。这也是判断总线是否接通的快速手段之一——看总线上错误帧的比例。3.2 OEM私有协议与标准化协议的分野实际车辆上跑的协议大致可以分成三类。第一类是OEM私有协议像早期很多自主品牌车厂自己定义ID分配和信号排布不同车型间完全不兼容一台新车上CAN报文只能重新逆向分析。第二类是行业标准协议例如J1939、CANopen这些都是开放标准有公开的文档和ID分配规则商用车上康明斯、博世、采埃孚等厂商标准统一。第三类是基于标准协议的诊断扩展比如UDSISO 14229和OBD-II。选型时我的一条核心建议是能上标准协议就别自己发明协议。原因很简单标准协议的工具链成熟、文档多、人才好找。你自己定了一套ID规则爽的是协议很简单痛苦的是后续每一个设备对接都要写文档、做适配而且完全没有现成测试工具。商用车领域一个ECU不支持J1939基本等于没法装机。3.3 商用车与工程机械的J1939J1939源于SAE标准工作在250kbps的CAN总线上是重卡、农用机械、工程机械、发电机组里事实上的应用层标准。它的核心设计思路是把报文按PGN参数组编号组织每个PGN对应一个特定的参数集合比如发动机转速、冷却液温度、车速这些。每个ECU用源地址SA标识身份0x00是发动机0x03是变速器0x15是仪表盘分配清晰。J1939最值得学的是它的地址声明和网络管理机制。刚上电时ECU先通过地址声明报文广播自己的源地址如果有两个ECU用了同一个地址就会触发地址冲突处理。平时做联调时用CAN卡直接发地址声明命令就能把不听话的节点“请”到别的地址上去这个技巧在实车上排查总线冲突时非常实用。3.4 车载诊断协议UDS与OBD-II如果是修车或者做检测设备那UDS和OBD-II是绕不开的。OBD-II是排放相关的诊断接口每个车厂都必须实现物理层通常走CANID固定0x7E0和0x7E8分别对应诊断请求和响应。它的功能相对有限主要读故障码DTC和清故障码。UDS则更通用它定义了一套完整的诊断服务包括0x10会话控制、0x22按ID读数据、0x2E写数据、0x31例程控制、0x34请求下载、0x36传输数据、0x37请求退出传输。通过UDS不仅能看到故障码还能做刷写软件、标定参数、配置车辆配置码等操作。UDS跑在CAN上时一般用ISO-TPISO 15765-2做分段传输。因为单帧最多传8字节数据而一条诊断响应动辄几十字节就需要把长数据切分到多个CAN帧里依次发送。做上位机时如果直接拿CAN原始帧解析数据会发现很多“看似乱序”的报文其实都是ISO-TP层在拆包重组。这也是很多人在没有协议栈的情况下用CAN卡读UDS数据读到一半就卡住的原因。3.5 工业自动化里的CANopenCANopen在乘用车里用得少但在工业控制、机器人、医疗设备、特种车辆内部网络中非常普遍。它的思路和J1939不一样核心是对象字典OD每个节点维护一张表把设备的参数、输入输出、状态都映射到16位的索引和8位的子索引上。PDO过程数据对象用来周期传输实时数据可以理解成“高速直通通道”SDO服务数据对象用来读写字数较长的配置数据速度慢但可靠。做运动控制时CANopen配合CiA 402协议可以控制伺服驱动器完成位置、速度、力矩三种模式状态机包括“启动-准备好-使能-运行-停止”几个标准状态。很多国产伺服和步进驱动器都支持CANopen这也是CAN在工业机器人中仍然活跃的重要原因。要注意的是CANopen对一个网段上的节点数量有限制一般不超过64个节点节点数量增多后总线负载和实时性也会下降设计网络拓扑时就要提前规划。下面用一张表把几种常见应用层协议做对比方便在实际项目中快速选型。协议典型领域波特率核心特点最大数据场OEM私有协议乘用车内部250k-500k定义灵活兼容性差8字节J1939商用车/工程机械250kPGN参数组地址管理完善8字节CANopen工业控制/机器人125k-1M对象字典PDO/SDO分离8字节UDS诊断/刷写500k以上服务化诊断支持刷写8字节经典CANCAN FD逐渐普及的新车500k-5M64字节数据场更高带宽64字节4. 实操实录CAN总线测试与协议解析的完整过程说再多理论都不如坐下来接一套设备、抓一段波形来得实在。我整理一下自己平时做CAN测试和环境搭建时最常用的方案这套流程从上学DIY到后来在公司实验室里都适用完全没有门槛。4.1 硬件准备CAN卡、示波器和调试线束做CAN测试最基础的硬件是USB-CAN卡。市面上的选择很多从几十块钱的兼容周立功方案的卡到上千块的原厂分析仪核心功能其实都差不多USB转CAN、接收发送报文、把总线上的错误帧打标记。我的建议是新手先从带PCAN驱动兼容的便宜方案入手等确认项目对工具链的依赖后再决定要不要升级。示波器用来确认物理层波形如果预算有限至少准备一台支持数学通道A-B减法的两通道示波器。接线是很多人容易翻车的环节。CAN总线的两根线不要随便用杜邦线接强烈建议用双绞线延展线缆长度超过1米就尽量套屏蔽层并单点接地。我在实验室调试一套三节点CAN网络时有人图省事用了几根长短不一的飞线结果500kbps下频繁出现CRC错误和形式错误换了一段0.5米的双绞线后问题消失。线缆不对称、线间距不一致给差分信号带来的串扰在低频段表现不明显一旦波特率上来就立刻暴露。4.2 抓包流程看报文与识别错误帧把CAN卡接入总线后打开上位机软件设置好波特率通常能直接看到总线上的报文流。正常通信时报文ID、数据长度、数据值都是规律的。排查问题时我一般分三步走第一步看负载率。总线负载率如果长期超过80%说明网络压力已经很大高优先级报文会挤压低优先级报文的发送窗口导致延迟抖动。第二步看错误帧。CAN错误帧的波形特征是ID正常但紧跟在一个小间隙之后出现6个连续显性位。如果错误帧占比超过1%总线物理层很可能有问题比如终端电阻不匹配、节点距离过长、接地电位不一致。第三步看报文内容是否合理。比如发动机转速对应的数据段某个字节的值一般会在一个范围内周期变化如果数据跳变无规律或者频繁全0全F多半是信号定义没对齐。如果手头没有CAN卡还有一个土办法示波器直接抓两根线的波形用数学通道A-B看差分。波形上的帧起始是一个显性位后面跟着一串在0V和2V之间跳变的波形。数一下一帧里有多少个跳变就能大致判断是不是数据在传输即使不知道协议也能排查总线是否“活着”。4.3 波特率自动检测与匹配CAN通信的第一个大坑就是两边波特率不一致。不同的波特率下位时间不同同一个报文占用的时长完全不同。CAN标准本身没有自动波特率协商机制所以实际联调时新接入的节点必须预先配置好和总线上其他节点一致的波特率否则就会出现错误帧满屏飞。有些CAN分析仪具备波特率扫描功能原理是总线空闲时用不同位时间采样一段已知报文通过帧起始和CRC波形去匹配最优波特率。大多数情况下总线上跑的波特率都是有迹可循的乘用车内部ECU一般500kbpsOBD-II诊断接口是500kbps或250kbps商用车J1939固定250kbpsCANopen常见125k、250k、500k、1M。如果实在不确定先拿示波器看一眼隐性到显性跳变的最小脉宽再推算波特率这个办法百试百灵。4.4 常见问题快速排查表现象可能原因检查顺序完全收不到任何报文两端波特率不一致/线序接反先确认收发器供电和地线再核对CAN_H/CAN_L错误帧比例高终端电阻缺失或重复用万用表量总线两端的阻值应为60Ω左右报文时通时断接插件松动/线缆过长摇晃线束复现故障看CAN卡的错误计数器只有个别节点收不到节点地址冲突/过滤器配置检查该节点的验收滤波器和地址声明波形上升沿严重过冲终端电阻不匹配/线缆分支过长在过冲点就近补一个120Ω电阻实际调试时我还会习惯性先量一下CAN_H对地电压和CAN_L对地电压。正常工作时两者基准都在2.5V附近隐性发送显性时分别跳到3.5V和1.5V。如果量到CAN_L对地电压只有0V多半是CAN_L断路如果CAN_H稳定在5V多半是CAN_H和电源短路。这一步比盲目抓波形快得多。5. 进阶实战达妙电机如何通过CAN实现精准关节控制把视线从车转到机器人和自动化设备。CAN总线在这些领域的应用热度一直没降特别是机器人关节电机控制CAN几乎是性价比极高的通信方案。拿达妙电机这类常见的关节电机来说它的控制接口就是CAN一圈人问“达妙电机怎么通过CAN总线实现精准关节控制”背后其实是CAN底层机制和上层控制策略的叠加。5.1 为什么机器人关节控制偏爱CAN机器人关节控制对通信的要求一是实时性高控制周期通常1kHz甚至更高二是要同时挂多个电机每个手臂关节一个电机一台机器人动辄十几个节点三是电机本身是强干扰源PWM驱动电流很大通信协议必须扛得住电磁骚扰。串口天然不是为多节点设计的以太网在实时性和成本上对小型控制器也不友好。CAN每帧最多8字节、优先级仲裁毫秒级完成、差分信号抗干扰强刚好卡在需求点上。达妙电机就是典型的CAN应用场景。它把驱动器和电机集成在一起外部只需要供电和CAN两对线就能完成位置、速度、力矩的闭环控制。上位机比如MCU或者运控卡通过CAN发送指令帧电机返回当前状态帧一套低成本、高性能的关节控制链路就搭起来了。5.2 达妙电机CAN控制的完整闭环初次拿到达妙电机首先配置电机ID和CAN波特率。一般通过上位机工具或者CAN报文设置把电机ID设为1号、2号等波特率设为1Mbps。注意总线上每台电机的ID必须唯一否则两个节点同时响应同一条指令会出现地址冲突。给电机上电后它会周期性在总线上发送状态报文如果CAN卡里能看到这些报文就说明链路已经通了。控制指令数据帧一般包含目标位置、速度、力矩指令以及启停的控制字。以常见的控制格式为例一条控制帧的数据场里会打包控制字、目标位置int32、目标速度int16、目标力矩int16等字段。MCU侧要做的事就是把用户的运动学解算结果填进这些字段然后按固定的CAN ID发给对应电机。电机驱动器收到后内部通过FOC算法驱动无刷电机再把实际位置、速度、电流等状态打包发回总线。实际控制要精准核心在两点。第一是控制周期稳定建议定时器中断里固定周期发送不要用主循环延时来凑否则控制周期的抖动会直接影响位置环的动态响应。第二是数据字段的对齐发送端和接收端对“控制字中bit0是否为使能”这类定义必须完全一致有个团队就是因为高低字节顺序定义不一样电机怎么调都是往一个方向猛冲排查了一个多星期才发现。5.3 带宽与实时性计算一台控制器能带几台电机很多人关心一个问题1Mbps的CAN总线上控制周期1kHz时能带多少个电机这个可以简单算一笔账。每帧CAN报文如果按标准帧、8字节数据来计算总位数大约是帧起始1位 仲裁段12位 控制段6位 数据段64位 CRC段16位 ACK段2位 帧结束7位再考虑位填充Stuff Bits的最坏情况一般经验值按每帧130位左右估算。1Mbps下一秒钟能传约1,000,000/130≈7692帧。如果控制周期1ms也就是1秒发1000次控制帧每个电机一帧控制指令加一帧状态反馈那就是2帧/控制周期1000次需要2000帧。这样算下来1kHz控制周期大约能带3到4台电机。如果电机数量更多有两条路可走一是把控制周期降到500Hz实时性仍然能满足大部分位置环需求二是切换到CAN FD数据场从8字节翻到64字节一条报文能同时塞下多个电机的指令大幅降低总线帧数。设计机器人分布式驱动系统时这个带宽预算要提前算清楚否则后面堆硬件也救不了总线拥堵。5.4 CAN总线上的时序协调与优先级分配多电机协同控制时报文ID分配是有讲究的。高优先级给时间敏感的指令比如急停指令用最低的ID确保任何时候都能立刻抢占总线每个电机的控制指令ID按电机编号递增状态反馈ID也类似。这样总线上天然形成一种调度秩序紧急指令优先、位置指令其次、状态反馈最后完全靠硬件仲裁实现不需要软件调度器介入。实际调试时CAN总线上还会偶发错误帧、重发帧这些都会占用总线时间。加电瞬间多个电机同时发送状态报文也可能造成短时总线拥堵。比较好的做法是电机使能前分时上电或者让电机状态报文在某个时间窗口分批发送错峰出帧。这个细节在3台以上电机协同时会变得特别明显。5.5 电机CAN控制里的高频坑我踩过的最深的一个坑是电源问题。多电机同时大力矩启动时母线电压瞬间跌落CAN收发器的输出级供电跟着波动导致隐性电平被拉到离谱的值总线上全是错误帧。后来在电源输入端加大电容、把CAN收发器独立供电问题立刻缓解。另外电机动力线和CAN线一定要分开走线实在没法分开就必须用屏蔽双绞线屏蔽层单端接地否则PWM高频开关噪声耦合到CAN线上波形出来全是毛刺什么协议分析都白搭。还有一个容易被忽略的是CAN控制器初始化顺序。有些MCU掉电后引脚处于高阻态如果此时总线上其他节点正在通信这个“掉电节点”虽然不影响总线电平但上电瞬间引脚状态不确定可能短暂拉低总线造成一串错误帧。解决办法是配置CAN外设时先把引脚设置为复用功能并保证收发器进入待机模式一切就绪后再加入总线。6. 从协议分析到系统设计我的选型与排障建议分享几个多年攒下来的经验。第一点做CAN网络设计时千万别在拓扑上搞花活。CAN最稳定的是总线型拓扑也就是说从节点A到节点B到节点C到节点D一条线串下来然后在这条线的物理两端接120Ω终端电阻。星型拓扑、T型分支这些在实验室里看着方便实测节点多起来或者波特率拉高后反射和信号完整性会让人崩溃。第二点CAN报文不是越全越好。很多时候我看到有人把传感器采集的所有数据都往总线上塞结果负载率飙到70%以上实时性反而下降了。在设计报文时要区分周期报文和事件报文转速、温度这种变化平缓的参数用低频周期发送按键、故障这类事件用“变化时才发”的方式触发可以把总线占用率降下来不少。第三点上位机软件能买就不自己写。CAN调试的工具链看起来简单实际里面对时间戳精度、错误帧记录、离线回放的要求很高。自己写一个“简单CAN调试助手”很容易但要达到商用分析仪的水平得投入大量精力处理驱动、缓冲、波形渲染。有那个时间不如把精力花在协议设计和控制算法上。回到协议选择本身。纯量产的乘用车网络别想绕过OEM私有协议和诊断协议的组合商用车和工程机械直接以J1939为蓝本扩展自己的专用报文工业设备和机器人项目如果追求生态和标准化CANopen依然是可靠选择如果对带宽有硬需求直接上CAN FD它的报文结构兼容经典CAN过渡起来很平滑。不过要注意CAN FD和经典CAN的物理层不完全互通选型时所有节点都要支持才行。最后再讲一个细节排查CAN总线问题时要养成先看物理层再看协议层的习惯。物理层的问题表现为错误帧多、波形畸形、总线电平异常这类问题用示波器一眼就能看出来协议层的问题表现为波形正常、错误帧很少但应用数据就是不对这类问题要花时间对比ID、比对DBC文件和信号映射表。很多人调试CAN一上来就抓高层协议抓来抓去发现是阻值不对纯属浪费时间。我个人的经验是任何一次整车CAN通信异常先用万用表量终端电阻再用示波器看隐性电平这两步做完基本能排除80%的故障。剩下的20%才轮到报文解析上场。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进