
搞汽车电子、嵌入式开发或者工业自动化的估计没人绕得开CAN协议。哪怕你没亲手用示波器抓过波形也一定在联调时听同事喊过“CAN不通”“丢帧了”“bus off”这类话。CAN总线从1986年出现到现在快四十年依然是车载网络和工业现场的主力。想真正上手CAN第一关就是把“协议种类”和“CAN数据帧”这两个基础啃透。这篇文章我就以一个常年跟总线打交道的工程师视角把CAN协议的种类划分、CAN数据帧的每个bit拆开揉碎讲清楚顺便分享一些实测中踩坑的经验。1. 先弄明白CAN总线解决的是什么问题1.1 从一车线束讲到总线仲裁在没有CAN的年代汽车内部的电子通信是典型的点对点模式。一个简单的车窗控制开关、电机、控制器、门锁模块之间都要单独走线。一个中端车型的线束总长度可以达到几公里重量几十甚至上百公斤线束越多装配越复杂故障点也越多。博世公司1986年推出CANController Area Network控制器局域网协议初衷就是用一对双绞线把车上所有电子控制单元ECU挂到同一条总线上需要交换的状态和信息都通过报文广播出去谁需要谁接收。这样线束数量大幅减少系统也更容易扩展。强调这段历史不是为了考古而是为了理解CAN协议为什么设计成现在这样。CAN的很多规则比如多主结构、广播式通信、逐位仲裁都是在“一根总线上挂几十个节点且不能互相干扰”这个前提下推导出来的。理解了前提后面再去看帧结构、仲裁场、错误处理就不会觉得那些规则是死记硬背。1.2 为什么CAN总线能在汽车和工业现场站稳三十年CAN协议能够三十年不衰核心是三点物理层可靠、链路层机制完整、实时性可预测。物理层采用CAN_H和CAN_L两条线上的差分电压传输抗共模干扰能力强。链路层有CRC校验、位填充、错误帧、错误计数器和总线关闭机制把通信可靠性做得很扎实。更关键的是它的仲裁机制所有节点可以同时发送数据通过ID和电平显隐性逐位比较优先级高的报文自动赢得总线不会出现以太网那种冲突后回退重传的“丢包”情况。这三点优势使得CAN在汽车动力、底盘、车身、诊断等场景里非常合适也延伸到工业控制、医疗设备、机器人等领域。哪怕现在出现了CAN FD、CAN XL、车载以太网经典CAN依然大量存在因为它的成本极低、确定性极强而且几代工程师已经把它的坑都摸透了。2. CAN协议种类到底怎么划分很多初学者一上来就问“CAN有多少种协议”这个问题其实要分层次回答。CAN的“种类”在不同语境下有完全不同的含义物理层有速度与容错之分数据链路层有版本演进再往上还有各种应用层协议。如果不把这几个层次分开后面看文档会越来越糊涂。2.1 物理层先分家高速CAN与容错CAN物理层上CAN标准主要分两类ISO 11898-2定义的高速CAN和ISO 11898-3定义的低速容错CAN早期叫ISO 11519-2。高速CAN最高速率1Mbps显性电平下CAN_H约3.5V、CAN_L约1.5V差分约2V隐性电平下两条线都约2.5V差分0V。它要求总线两端各接一个120欧终端电阻匹配做不好波形就乱。容错CAN的速率一般不超过125kbps但允许总线上某一根线对地短路或者断路时继续降级通信所以叫“容错”。对比起来很直白对比项高速CAN容错CAN对应标准ISO 11898-2ISO 11898-3常用最高波特率1Mbps125kbps以下终端匹配两端各接120Ω每个节点按需接入对线束要求低抗线束故障能力弱双线必须完好支持单线断路或对地短路降级通信典型应用动力、底盘、ADAS车身舒适、仪表、门窗等实际工作中容错CAN已经越来越少大部分新车型哪怕车身网络也倾向于升级到更高的速率或者直接用LIN处理低速节点。但老车型诊断、商用车售后偶尔还会碰到容错CAN知道它的存在能少走弯路。2.2 链路层版本演进CAN 2.0A、CAN 2.0B、CAN FD数据链路层上大家常说的“CAN 2.0”其实分成两个版本。CAN 2.0A定义标准帧标识符ID只有11位CAN 2.0B定义扩展帧标识符扩展到29位。早期有些设备只支持CAN 2.0A收到扩展帧会直接忽略这在混装设备时要注意。经典CAN的最高速率都是1Mbps数据场最多8字节这个限制在当时很合理但放到现在做ECU刷写、大数据量日志传输就有点捉襟见肘。于是CAN FD出现了。CAN FD全称CAN Flexible Data-rate灵活数据速率2012年前后由博世提出2015年纳入ISO 11898-1标准。它有两个关键变化一是数据场最长扩展到64字节二是数据段可以采用比仲裁段更高的速率传输常见配置是仲裁段500kbps、数据段2Mbps或5Mbps。FD帧通过FDF位与经典CAN区分总线上可以经典CAN和CAN FD混跑但必须是支持FD的节点才能收发FD帧。这几年新车型的刷写、诊断、自动驾驶传感器数据回传基本都切到CAN FD了。再往后还有CAN XL数据场最长2048字节数据段速率能到10Mbps以上主要用于下一代车载主干网。但目前量产应用还不算多学习优先把经典CAN和CAN FD打牢就够了。版本标识符数据场最大长度最高速率兼容性CAN 2.0A11位8字节1Mbps经典CANCAN 2.0B11位/29位8字节1Mbps兼容2.0A设备需支持扩展帧CAN FD11位/29位64字节数据段最高约8Mbps向下兼容经典CANCAN XL11位/29位2048字节10Mbps以上需要专用收发器支持2.3 应用层协议与CAN种类的关系CANopen、J1939、DeviceNet很多入门的人把CANopen、J1939当成“CAN协议的种类”这个理解不算全对。CAN协议本身只管到OSI模型的数据链路层和物理层它只解决“报文怎么可靠送到”不解决“某个ID代表什么含义、数据字节怎么编码、节点之间怎么握手认证”。CANopen、J1939、DeviceNet这些应用层协议解决的是报文内容的语义问题。打个比方CAN数据帧是路应用层协议是交通规则没有路车走不了没有规则路上会乱套。CANopen对象字典 PDO/SDO 心跳报文广泛用于伺服驱动、运动控制、电梯等工业设备。J1939基于29位扩展帧通过PDU格式定义PGN、源地址、目标地址广泛用于卡车、工程机械、船舶。DeviceNet由Rockwell主导常用于传感器、执行器、阀岛等工业现场设备。ISO 15765UDS on CAN车载诊断协议OBD刷写和读故障码的基础。搞清楚这个层次看协议文档时就能快速定位遇到ID和电平相关的配置查CAN链路层遇到报文语义和状态机查应用层协议。两边混着看容易越看越乱。3. CAN数据帧结构逐位拆解CAN数据帧的结构是这篇内容的重头戏我这几年面试嵌入式工程师问CAN一定绕不开这几个位。经典CAN有好几种帧数据帧、远程帧、错误帧、过载帧其中日常通信最核心的是数据帧。数据帧又分标准帧和扩展帧两种格式下面逐个拆。3.1 标准帧的一整帧位序标准数据帧在总线上的位序如下SOF1位显性→ 11位标识符 → RTR1位→ IDE1位→ r01位→ DLC4位→ 数据场0-8字节→ CRC15位→ CRC界定符1位隐性→ ACK槽1位→ ACK界定符1位隐性→ EOF7位隐性→ IFS3位隐性SOFStart of Frame帧起始1位显性逻辑0。总线从空闲进入一帧的开始所有节点的同步都从这里开始。11位标识符标准帧的ID范围0x000到0x7FF越小优先级越高。RTRRemote Transmission Request远程发送请求位数据帧里是显性0远程帧里是隐性1。看这一位就能判断是数据帧还是远程帧。IDEIdentifier Extension标识符扩展位标准帧里为显性0。r0保留位显性0标准帧固定。DLCData Length Code4位数据长度码表示数据场字节数。数据场0到8个字节按DLC决定长度。CRC场15位CRC校验序列覆盖从SOF到数据场的内容用来检测传输错误。ACK场发送方发出1位隐性ACK槽其他节点如果正确收到这一帧会在ACK槽期间拉显性表示“我收到了”。EOF7位隐性位帧结束。IFS3位隐性帧间隔两个帧之间的最小间隔。整个标准帧加在一起最短一帧约47位最长约111位。1Mbps下一帧8字节数据大约111微秒这个数值在做实时性估算时很常用。3.2 扩展帧与关键位RTR、IDE、SRR的作用扩展帧和标准帧最大的区别在仲裁场。扩展帧的位序是SOF1位显性→ 11位基础ID → SRR1位隐性→ IDE1位隐性→ 18位扩展ID → RTR1位→ r11位→ DLC4位→ 数据场 → CRC → ACK → EOF → IFSSRRSubstitute Remote Request替代远程请求位固定隐性1。它出现在基础ID之后对应的是标准帧里RTR的位置目的是保证时序对齐不影响仲裁结果。IDE位在扩展帧里是隐性1在标准帧里是显性0所以这一位才是标准帧和扩展帧在总线上仲裁时真正的分水岭。扩展ID是18位和11位基础ID拼成29位标识符范围0x00000000到0x1FFFFFFF。很多人容易把RTR、IDE、SRR搞混我做个表区分位标准帧扩展帧主要作用RTR数据帧显性0远程帧隐性1数据帧显性0远程帧隐性1区分数据帧和远程帧IDE显性0隐性1区分标准帧和扩展帧SRR不存在固定隐性1替代远程帧中RTR的位置保证时序一致扩展帧和标准帧如果同时抢总线两者ID相同的情况下标准帧会在IDE位显性0胜过扩展帧的IDE隐性1所以标准帧优先级更高。这也是一个常见的面试考点。3.3 数据长度码DLC与数据场DLC是4个bit经典CAN只支持0到8字节所以DLC的值0000到1000直接对应数据长度0到8。9到15这几种编码在经典CAN里是非法且不允许使用的收到后会按8字节处理但这个行为容易在混用节点时引发不一致。CAN FD把DLC的编码扩展了1001到1111可以表示超过8字节的长度。具体对应关系我列出来调试时对照很方便DLC值二进制经典CAN数据长度CAN FD数据长度00000字节0字节00011字节1字节00102字节2字节00113字节3字节01004字节4字节01015字节5字节01106字节6字节01117字节7字节10008字节8字节1001不支持12字节1010不支持16字节1011不支持20字节1100不支持24字节1101不支持32字节1110不支持48字节1111不支持64字节数据场本身对大端小端没有规定按什么字节序解释是应用层协议的事。比如J1939里很多参数是大端CANopen里一般是小端搞错字节序是上层解析最常见的bug之一。3.4 CRC校验、位填充与ACK应答经典CAN的CRC是15位覆盖从SOF到数据场的所有内容包括期间插入的填充位。接收方重新计算CRC和收到的CRC比较不一致就认为这一帧出错进而触发错误帧。位填充是CAN最容易被忽略的机制之一。从SOF开始一直到CRC序列结束如果发送方遇到连续5个相同电平的bit就会在第5个bit之后自动插入一个反相的填充位。这样做的目的很朴素接收方靠波形上的跳变沿来同步采样时钟长时间没有跳变会使收发双方逐渐失去同步。接收方解码时会把对应的填充位删掉。如果总线上出现连续6个相同电平的bit接收方就认为位填充违例直接报错并发出错误帧。这也解释了为什么很多“莫名随机错误帧”最后查到根因是某个节点的晶振精度太差或者波特率配置不对。ACK场是最容易理解的握手。发送方发出的ACK槽是隐性1所有正确接收到这帧的节点都会在ACK槽期间输出显性0。发送方只要在ACK槽读到显性电平就知道有人确认收到了如果一直是隐性说明总线上没有其他节点成功接收发送方会认为出错并重发。4. 抓一帧报文并解析的实操记录这一部分我结合一次台架调试的实测过程讲。手里的设备是一台USB-CAN分析仪加一个便携示波器目标是把一条500kbps标准帧总线上的报文抓下来并确认ID和数据内容。4.1 需要的工具和终端电阻提醒常用的CAN抓包工具分为几类USB-CAN分析仪常见的有ZLG、PCAN、Kvaser、逻辑分析仪Saleae之类带CAN解码、示波器加人工解bit。调试建议组合是“一块带CAN解码的示波器 一个USBCAN工具”。示波器看物理层波形和电平是否正常USBCAN工具看报文和错误统计。动手之前先确认终端电阻。标准做法是断电状态下在总线上任意一点用万用表欧姆档量CAN_H和CAN_L之间的电阻。正常总线上两端各120欧并联测出来应该在60欧左右。如果测出来是120欧说明有一端终端电阻缺失如果测出来接近0那基本是短路了。这里提醒一下不要带电测不要只靠软件里标称的“内置120欧”就以为万事大吉。4.2 手把手从波形还原一帧CAN报文假设示波器已经触发到一帧波形采样时间轴拉到一个bit约2微秒对应500kbps。按下面步骤还原找到SOF总线从隐性电平约为2.5V差分0V跳变到显性差分约2V的那一下就是SOF读为0。读11位ID从SOF之后数11个bit。波形上显性0、隐性1转成二进制再换算成十六进制就是标准ID。判断RTR和IDE继续读1位RTR如果这帧是数据帧电平是显性0然后读1位IDE标准帧是显性0。读DLC接下来4个bit就是数据长度码比如0010表示2字节。读数据按照DLC值读取后面的数据字节每8个bit拼一个字节。确认ACK读到最后会看到发送方发出的隐性ACK槽位置被其他节点拉出一小段显性脉冲这就是应答。实际操作中示波器人工读bit误差很大主要用来判断“这帧是不是符合预期长度”判断“ACK有没有”辅助确认总线电平是否健康。真正批量解析报文还是靠USBCAN工具的软件显示这里讲人工方法是为了让你在工具抽风时也能勉强看得懂波形。有一次我调试新老两个控制器混跑新控制器用的是CAN FD老控制器是经典CAN示波器上看波形一切正常但老的USBCAN工具始终不识别新节点的报文。后来发现老工具软件不支持CAN FD帧一看到FDF位是隐性就直接当错误帧忽略。换成支持CAN FD的抓包工具问题当场消失。遇到相似现象先确认工具是否支持FD再去怀疑硬件。4.3 常见总线故障排查速查表这些年排查总线问题排得多了我把自己常用的判断逻辑整理成了一个表现象可能原因排查方向总线上完全没有波形缺终端电阻、节点供电异常、CAN_H和CAN_L接反测终端电阻、量节点供电、核对线序有波形但错误帧不断波特率不一致、采样点设置不当、时钟精度太差示波器量实际bit宽度软件里调整波特率和采样点单个节点收不到报文节点ID过滤配置错误、节点处于Bus Off检查验收码和屏蔽码查错误计数某个节点一发送就报错该节点晶振偏差大、连接器松动、地参考电位不一致单独抓该节点与其他节点的通信波形总线出现规律性变异终端电阻位置不对、总线分支过长检查星型拓扑缩短分支确保终端在总线两端CAN_H与CAN_L电压异常某一根线短路到电源或地断电测线缆电阻分段排除排查原则有一条我反复跟同事强调先物理层再链路层最后应用层。别一上来就翻报文ID很多“玄学丢帧”最终都是终端电阻松动和地电平不统一导致的。5. 调试CAN总线多年后想提醒你的几件事5.1 采样点与波特率90%错误帧的根源波特率不匹配在联调初期特别常见。两个节点速率差一点点在短线上可能看不出问题但总线一长或者环境一恶劣错误帧就爆发。实际排查时不要只对比“波特率名字”对不对最好用示波器实际测量一个bit的时间宽度再反推实际速率。举例说如果看到bit宽度是2.1微秒而不是理论上的2微秒说明有个节点的时钟偏差已经偏到5%了必须换晶振或者改波特率配置。另外采样点很多人不注意。CAN控制器会在一个bit时间的某个百分比处采样电平这个点通常建议在75%到87.5%之间。有些默认配置会落在50%附近这在短距离没问题但在总线较长、上升沿不够陡的时候容易采样到不稳定的电平产生偶发错误。修改TSEG1和TSEG2可以调整采样点调试时先定波特率再调采样点不要两个一起盲目改。5.2 物理层问题优先于链路层判断我的经验是CAN调试中80%以上的问题是物理层问题接线错误、终端电阻缺失、连接器接触不良、电源纹波过大、地电位不一致。很多新手喜欢抱着工具软件看错误帧ID绕了半天找不到原因最后拿万用表量一下终端电阻发现是120欧而不是60欧拧上终端电阻立刻就好了。还有一些细节容易被忽视。长距离通信时CAN线最好用双绞屏蔽线屏蔽层单端接地不要用普通跳线排随便飞线飞线超过半米就很容易出反射问题。总线上加节点时尽量采用直线型拓扑链式往下串避免星型分支过长。分支太长会导致信号反射点靠近接收端波形上的过冲和下冲很容易让接收端误判。我个人调CAN这几年最大的体会是先把示波器差分波形看稳了再谈报文逻辑。基础的东西往往最值钱CAN数据帧结构看起来不难但真正理解每一bit背后的设计意图排查问题时才能不慌不忙一抓一个准。