ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

CAN总线从报文结构到负载率计算:工业现场核心通信技术详解

CAN总线从报文结构到负载率计算:工业现场核心通信技术详解 1. 为什么CAN总线在工业控制网络里始终占着重要位置做工业控制和车载电子的工程师大概没有不认识CAN总线的。从车间里PLC到伺服驱动器之间的联动到整车网络上几十个ECU之间的数据交换CAN总线几乎是无处不在的默认答案。很多人都把它当成一个理所当然的存在但真要问一句为什么是CAN不少人也只能说个大概。很多刚接触CAN的朋友甚至会把它和RS-485、Modbus混为一谈觉得无非就是一种串行通信协议而已但实际上这两个东西从设计哲学上就不一样。先说RS-485。RS-485虽然也是差分传输、抗干扰能力强但它在协议层面只是定义了物理层的电气特性上面跑什么完全靠你自己定。更关键的是典型的RS-485主从式轮询架构里总线上需要有一个主站不断地点名问从站1在吗从站1请回复其他从站只能被动等。这种一问一答的模式有两个固有缺陷第一实时性差节点越多主站轮询一圈的时间越长紧急事件根本插不进去第二单点故障风险高主站一旦挂了整个网络就瘫了。工业现场很多设备都是实时联动的轴与轴之间的位置同步、急停信号的快速响应靠RS-485轮询很难做到让人放心。工业以太网这些年发展也很快EtherCAT、Profinet、Powerlink这些协议确实很强大实时性和带宽都远超CAN但它们的部署成本、调试门槛和对硬件的要求也摆在那里。很多中小规模的系统并不需要千兆级别的带宽而是在成本和可靠性之间找一个平衡点。CAN总线恰恰卡在这个位置物理层用两根线就能解决成本极低协议层自带多主架构、逐位仲裁、错误检测和恢复机制这些功能如果用工业以太网来实现往往需要更复杂的硬件和软件配合。加拿大一位嵌入式工程师曾经在我的技术交流群里打过一个比方我觉得特别贴切如果把工业以太网比作一条八车道的高速公路CAN总线就是一条乡间县道。高速公路车速快、容量大但修路成本高、入口匝道多县道虽然窄但它不怕错车——因为CAN在设计上就允许所有节点同时尝试上路天然解决了冲突问题。所以你会发现CAN总线最核心的价值在于三点一是多主架构任何节点在总线空闲时都可以主动发起通信不需要等主站点名二是非破坏性仲裁机制多个节点同时发送时通过标识符逐位仲裁高优先级的报文不会被打断整个过程不会浪费一个比特的总线时间三是完备的错误处理机制五种错误检测类型加错误计数器把错误节点自动隔离出去保证总线上的其他节点不受影响。这三条加起来让CAN在实时性、可靠性和成本之间找到了一个非常实用的平衡点。这也是为什么从1990年代至今CAN总线在汽车电子、工业控制、医疗设备、工程机械、轨道交通、机器人等领域始终没有被淘汰。即便后来出现了CAN FDCAN with Flexible Data-rate把数据段从8字节扩到了64字节也不改变它的底层工作逻辑。说白了协议可以升级但骨架还是那个骨架。2. 报文结构细读一帧CAN数据到底长什么样理解了CAN总线的地位接下来就要看它到底是怎么工作的。CAN总线上传输的基本单位叫帧最常见的两种是数据帧和远程帧另外还有错误帧和过载帧这两种特殊情况。在这里我把数据帧拆开逐个字段讲清楚因为后面讨论负载率计算、错误排查都离不开这些基础概念。2.1 标准帧与扩展帧的字段对比先看标准帧也就是CAN 2.0A规定的格式。一帧标准数据帧从上到下依次是帧起始SOF1位、仲裁域12位、控制域6位、数据域0到8字节、CRC域16位、ACK域2位、帧结束EOF7位帧和帧之间还有帧间空间IFS最少3位。扩展帧是CAN 2.0B规定的格式把仲裁域从12位扩展到32位标识符从11位变成了29位11位基础ID加18位扩展ID两者之间的关系我用表格列出来字段标准帧CAN 2.0A扩展帧CAN 2.0B帧起始1位显性1位显性标识符11位11位基础ID 18位扩展IDRTR/SRR位1位远程请求1位SRR 1位IDE 1位RTRIDE位1位显性标识标准帧1位隐性标识扩展帧控制域1位r0 4位DLC1位r1 1位r0 4位DLC数据域08字节08字节CRC域15位CRC 1位定界符15位CRC 1位定界符ACK域1位ACK槽 1位定界符1位ACK槽 1位定界符帧结束7位隐性7位隐性这里有个常见的误区很多人以为扩展帧更高端所以都用扩展帧。实际上29位标识符在复杂的车载或工控网络里确实更容易规划但标准帧在仲裁效率和帧长度上有明显优势。如果你的节点数量不超过几十个总线上报文的种类也有限标准帧就完全够了。过度使用扩展帧反而会让总线负载率升高同一条总线单位时间内能传的报文数量更少。2.2 仲裁机制为什么低ID优先CAN的仲裁机制是我觉得整个协议设计里最精妙的部分。总线上有多个节点同时发送时每个节点从SOF之后开始逐位发送自己的标识符同时监听总线电平。CAN的物理层规定显性位逻辑0会覆盖隐性位逻辑1也就是说当一个节点发送隐性位却听到总线上是显性位时它就知道自己有更高优先级的竞争对手在发送数据于是自动退出仲裁变成接收方。这个机制和一群人去抢一个话筒的差别在于谁喊的声音最小谁就赢——准确的说是标识符越小优先级越高。整个过程是逐位的最高位先比如果两个ID前面几位相同就往下一位继续比。因为仲裁不打断数据的完整性仲裁失败方的报文也不会被破坏只是被延迟到下一轮发送所以叫非破坏性逐位仲裁。实际工程里这意味着你可以通过合理分配标识符来确保关键报文优先通过。比如急停报文、伺服位置同步报文一定要分配比较小的ID。注意优先仲裁的是ID不是DLC数据长度码也不是数据内容本身。所以ID规划直接决定了整个总线系统的紧急程度等级划分这一步偷懒了后面联调时一定会吃苦头。2.3 位填充与位同步看不见的插入规则CAN还有一个自动执行的位填充规则在发送SOF到CRC序列这一段时间内如果连续出现了5个相同电平的位发送方会自动插入一个反相位的位。接收方在解码时会自动把这个插入位剔除。为什么非要多此一举因为CAN的接收节点靠边沿来同步时钟。如果总线上出现长时间没有跳变沿的连续电平不同节点的采样时刻就会逐渐产生偏移最终导致误码。位填充保证了总线上每隔段时间必然会有一个跳变沿让所有接收节点能持续保持同步。这件事对负载率计算和时序估算影响很大很多人算负载率时直接把固定帧长拿来算忽略了位填充会导致帧实际传输时间周期性变化。这一点到后文计算负载率的时候再展开。2.4 应答机制一帧报文发完至少需要一个节点点头最后说一下ACK应答槽机制很简单但很关键。发送方在CRC定界符之后会释放总线输出隐性这时任何一个接收节点如果成功收到报文就在ACK槽内拉一个显性位表示我收到了而且校验没问题。发送方如果没在ACK槽采样到显性位就会报应答错误并且重发这一帧。我之前在现场遇到过一种诡异现象总线上能够收到数据但每天都偶发几次很小的毛刺错误。后来排查发现是有个从站的CAN收发器输出电容老化导致它在ACK槽拉低的时刻偏晚发送方采样窗口已经关了于是判定应答失败并重发。这种事不深入理解ACK机制光在应用层抓包看数据很难定位到硬件问题上。这也呼应了后面要讲的错误帧排查报文能否被正确应答直接决定了总线的健康状态。3. 中断接收还是DMA接收从一次现场丢帧问题说起关于中断接收和DMA接收的选择我看网上问的人很多。其实这个问题的本质不是哪个好而是你的系统处于什么样的负载状态、接收后要做什么处理。我接触过一个典型的现场案例设备在调试阶段偶发丢帧查到最后不是通信物理层的问题而是MCU在中断里处理不过来。3.1 两种接收方式的本质差异先明确概念。所谓中断接收是CAN控制器每收到一帧完整报文就拉高中断引脚触发MCU进入中断服务程序由CPU从控制器的接收FIFO或邮箱中把数据搬到内存。所谓DMA接收是CAN控制器收完一帧后直接通知DMA控制器由DMA硬件把FIFO中的数据源源不断地搬到内存的指定缓冲区收完一批后在DMA中断或传输完成标志置位时CPU再做批量处理。这个差别用日常来类比中断接收相当于每来一个客人前台就要亲自起身迎接一次DMA接收相当于客人先进大厅坐着前台每隔一段时间统一登记一次。系统闲的时候无所谓但客人很多的时候中断方式会导致前台频繁起身完全没法干别的活。看一组实际数据对比我们以STM32的bxCAN外设为例在500kbps的波特率下一帧标准帧大约耗时130μs如果总线负载率到30%大约每秒会产生2300帧报文也就是每435μs就中断一次。如果中断服务里还有别的实时性要求更高的活要干频繁的抢占就会导致问题。对比项中断接收DMA接收CPU介入时机每帧都介入按批次介入帧间隔短的场景容易丢帧或触发频繁占用CPU少、吞吐稳定逐帧处理的实时性好收到即处理需要等DMA搬运完成代码复杂度简单直观需要配置DMA通道和缓冲区管理适用场景报文率低、需快速响应的控制信号报文率高、数据量大、CPU还要跑控制算法3.2 什么时候选中断、什么时候选DMA我自己的选型习惯是这样的如果总线上的报文速率在500帧/秒以下而且每条报文进来之后需要立刻参与控制逻辑比如急停信号、模式切换指令那我会优先用中断接收配合一个环形缓冲区中断里只做拷贝逻辑处理放在主循环。这种做法实时性好代码也简单排查问题也容易。如果报文速率超过1000帧/秒或者数据量很大比如持续记录一段时间的总线数据用于分析那我会毫不犹豫选择DMA。让DMA把数据搬到连续内存主循环或高优先级任务定期轮询缓冲区批量解析协议。实测过一个项目同样是50ms内接收约1000帧数据中断方式下CPU占用率接近40%换成DMA后降到6%左右效果非常明显。还有一个容易被忽略的问题当总线上出现大量错误帧重发时中断接收的节点会因为错误中断、接收中断同时进来导致中断嵌套加深严重的时候系统跑飞。DMA方式下错误帧和正常帧先进缓冲区CPU的异常处理可以更集中、更有条理。3.3 丢帧问题的定位过程回到我开头说的那个现场案例。当时现象是从站A周期性给主站发送数据频率不高500ms一帧但偶发连续丢两到三帧。拿CAN分析仪挂到总线上看从站A和主站之间的报文全部正常收发分析仪上没有错误帧。说明物理层和协议层都没问题问题出在主站的接收处理上。查主站MCU代码才发现接收用的中断优先级配得比较低而另一个外设的DMA中断在高优先级上频繁触发导致主站的CAN接收中断被长期延迟。中断被卡住期间CAN控制器的接收FIFO溢出新的报文来不及被取走就直接被覆盖了。对策有两步一是把CAN接收中断优先级提高二是把接收方式改成DMA批量搬运从根上降低了CPU被频繁打断的压力。3.4 核心注意事项关于DMA接收有几点必须提醒。第一DMA缓冲区大小要按最恶劣情况来定也就是总线上短时间突发大量报文时缓冲区不能被冲爆。第二DMA传输完成中断里要快速切换缓冲区双缓冲模式不要让DMA链路断掉。第三CAN控制器的FIFO溢出标志要妥善处理一旦溢出最好在程序里留一个统计计数而不是直接忽略。还有一点经验中断接收时如果FIFO深度不够可以配合CAN控制器的过滤功能只接收本节点关心的重要报文减少无效中断。在STM32的bxCAN里有一个48位标准帧/扩展帧各对应的一组过滤器组可以按ID范围过滤很多工程师都忽视了这个功能。4. 错误帧和总线异常五种错误机制与排查手段在做CAN总线测试时最让人头疼的就是CAN分析仪上源源不断的错误帧。错误帧不是一种独立的报文而是当任何节点检测到错误时立刻在总线上发出的一组特殊信号。很多初学者一看到错误帧就以为是总线不稳实际上不一定是硬件问题协议层面的状态机切换、波特率不匹配、终端电阻缺失都可能触发错误帧。4.1 五种错误类型与触发条件CAN协议一共定义了五种错误每种都被设计成了独立的检测机制这种冗余设计保证了误检率极低。错误类型触发条件检测方位错误节点发送的位与监控到的总线位不一致发送节点填充错误连续6个相同极性位出现在需填充区域所有节点CRC错误接收方本地计算的CRC与发送方CRC不一致接收节点形式错误固定格式的位如CRC定界符、ACK定界符、EOF出现显性电平接收节点应答错误发送节点在ACK槽没有收到显性应答发送节点其中位错误的触发条件最容易理解也最容易出现在布线问题上比如两个节点波特率不一致或者总线反射太严重发送方发出的位被反射叠加后形成了另一个电平值发送方就会认为自己发错了主动拉出一个错误标志。我以前排查过一个问题两条分支线缆太长又没有加终端信号在分支末端反射回来正好叠加到发送节点的采样点导致偶发位错误。后来重新布置分支线问题就消失了。4.2 错误计数器和节点状态机每个CAN节点内部都有两个计数器发送错误计数器TEC和接收错误计数器REC。规则大致如下检测到一次错误如果是发送方相关错误TEC加8如果是接收错误REC加1成功收发一帧后计数器分别减1。听起来简单但这两个计数器决定了节点处于三种状态之一主动错误状态TEC和REC都小于128节点可以正常收发检测到错误后发送6位显性错误标志被动错误状态任一计数器在128到255之间节点在错误标志上表现为6位隐性而且发送前要等待8位隐性间隔总线关闭状态TEC大于255节点完全断开与总线的连接不再参与任何通信必须复位或等待协议规定的恢复时间。我见过最典型的例子是某个节点在强电磁干扰下频繁触发位错误TEC不断累加最终进入总线关闭状态。上层应用根本不知道还在那干等数据而总线上其他节点正常通信。这也是为什么工程上要监控节点的TEC/REC状态及时发现即将掉线的节点。4.3 不容易发现的隐性坑实际排查中有一些反复出现的坑值得单独拿出来说。一是CAN_H和CAN_L两根线接反而导致的假正常总线空闲时可能电平没问题但一旦有节点发送显性位两个线的逻辑关系就错乱了会出现大量位错误和格式错误。二是只用了一端终端电阻总线波形反射变大偶发错误帧但示波器上看波形又难发现异常。三是波特率标称一致但实际有偏差两个节点一个用内部时钟、一个用外部晶振如果晶振精度差150ppm长报文跑下来就可能出现CRC错误。四是地电位差太大多个节点之间的电源地没有连在一起导致CAN收发器的共模电压超出允许范围错误帧会间歇性出现。之前有个项目我排查了一天最后发现是各节点电源模块使用了隔离电源但地没连网CAN_H和CAN_L的共模电压在两个节点间漂移到了十几伏虽然没烧毁收发器但已经超过了收发器允许的共模范围。解决方案也简单把分布在各机柜的CAN节点地统一接到系统地上问题立刻消失。4.4 现场排查的标准动作清单如果你在总线上看到错误帧我建议按下面这个顺序排查不要一上来就换线换收发器用CAN分析仪分别监听每个节点单独上电时的错误计数定位是哪个方向产生的错误用万用表量CAN_H对CAN_L的电阻断开所有节点电源后应在60Ω左右两端并联120Ω如果不对重点检查终端电阻示波器直接在终端电阻两端看隐性电平应在2.5V左右和显性差分幅值至少1.5V以上检查两个终端电阻是否安装在总线物理末端而不是在节点板卡上随意焊接逐个把节点从总线上摘除看错误消失不消失用排除法锁定肇事节点锁定节点后检查其CAN收发器的电源、地、晶振以及MCU侧配置的波特率采样点位置。这套流程走下来大部分错误帧问题都能定位到具体环节。如果都排查完了仍然偶发错误那就要考虑线路是否有电机变频器之类的强干扰源线缆屏蔽层是否正确单点接地分支线是否过长这些物理层面的因素在高速率下影响尤其明显。5. 负载率不是拍脑袋帧长、波特率与容量规划的算法很多工程师在设计CAN网络时给定波特率和报文周期后很少实际计算过总线到底每秒要传多少个位占了多少带宽。结果等系统跑起来报文延迟越来越大才发现总线已经超载了。负载率这个概念其实不复杂但有不少细节容易被忽略。5.1 负载率的定义总线负载率简单说就是单位时间内总线上实际传输的位数量除以总线理论上最多能传输的位数量通常用百分数表示。对于一对一的系统来说负载率高了无非就是延迟增加但在多节点、多报文且有关键周期报文的系统里负载率过高会导致低优先级报文的延迟忽大忽小从而破坏整个控制系统的实时性。行业里一般建议设计阶段峰值负载率按30%以下来做某些对实时性要求不高的系统可以放宽到50%但长期稳定运行超过70%的总线是随时可能出问题的。5.2 一帧报文到底占多少个位计算负载率的第一步是算清一帧报文在总线上实际占用多少个位。这里面最主要的坑就是位填充。前面提到CAN在SOF到CRC区间内连续5个相同位会自动插入一个反相位。由于数据内容是随机的填充位的数量也是不确定的所以算帧长时要区分固定部分和期望填充部分。标准数据帧的固定位长度用下面的公式算固定位 SOF(1) ID(11) RTR(1) IDE(1) r0(1) DLC(4) 数据字节×8 CRC(15) CRC定界符(1) ACK槽(1) ACK定界符(1) EOF(7) IFS(3)合并之后就是固定位 47 8×NN为数据字节数。以8字节数据为例固定位 47 64 111位。至于填充位工程上常用数据随机的情况下平均约每4.6个位产生1个填充位这个近似结论也可以用更稳妥的最大值来估算。填充发生在CRC序列之前的34 8×N个位上理论最大填充位约为这些位的四分之一。8字节数据时最大填充位约24位实际随机数据平均约19位左右。所以一帧8字节标准数据帧实际典型占位约在130到135位之间。如果取130位的平均值在500kbps波特率下一帧耗时约260μs。要注意的是扩展帧仲裁域变长了所以同样数据长度的扩展帧占位会更长数据段短的时候差距特别明显。5.3 实例一条报文周期对负载率的实际影响假设有一辆车的车身控制网络波特率500kbps总线上有发动机状态标准帧8字节10ms周期ID0x100、制动信号标准帧8字节10ms周期ID0x200、车门状态标准帧8字节50ms周期ID0x300、仪表盘数据标准帧8字节100ms周期ID0x400。先算每秒产生的总线位数发动机状态每秒100帧每帧约130位共13000位制动信号每秒100帧每帧约130位共13000位车门状态每秒20帧每帧约130位共2600位仪表盘数据每秒10帧每帧约130位共1300位。总位数约29900位除以波特率500kbps负载率约6%非常健康。但如果你把发动机状态改成5ms周期也就是每秒200帧总位数升到42900负载率约8.6%仍然没问题。问题是你看淡了总线负载而增加节点、增加报文周期后负载率会非线性上升。再看一个反例如果总线上有10个节点每个节点每10ms都要发一条8字节报文每秒就是1000帧总位数约130000位除以500kbps负载率高达26%这在多节点控制网络中已经算偏高了如果还有偶发的错误帧重发很容易突破30%。更危险的是有些节点在上电瞬间会同时上报大量状态信息瞬间峰值负载可能翻倍到50%以上。所以设计阶段建议做一张表把每条报文的ID、方向、周期、数据字节数逐一列出来计算每条报文的单帧位宽和每秒位数最后汇总出平均负载率和峰值负载率。峰值负载考虑上电瞬间、告警风暴等极端情况可以按平均负载率的1.5到2倍来估计。5.4 降低负载率的常用手段如果算完发现负载率过高有几个调整方向。第一检查有没有该用事件触发却用了周期上报的报文比如某些状态量只在变化时才需要上报改成事件触发能省下大量空闲周期的带宽。第二冗余的监控报文可以降低上报频率。第三能合并的数据尽量合并成一帧比如温度、压力、转速能放一帧8字节就尽量不要拆三帧。第四如果还是不够可以考虑提高波特率但要注意总线长度和物理层硬件是否支持。6. 一个CAN网络项目的完整设计和现场调试记录讲了这么多理论最后放一个实际项目的复盘把这些知识串起来。这个项目属于非标自动化产线控制要求是1个主站PLC加5个运动控制站、2个传感器站共8个CAN节点工作场景比较恶劣机柜之间有电机变频器干扰。6.1 需求梳理与ID规划先明确总线上要跑哪些报文位置同步报文周期10ms、各轴状态报文周期20ms、传感器数据报文周期50ms、报警和急停报文事件触发、维护诊断报文手动请求。在ID分配上我按优先级排序急停和报警用最小ID其次位置同步再然后是状态和传感器数据诊断报文优先级最低。用标准帧就够了因为节点总数就8个报文种类不超过10种没必要上29位的扩展帧给自己增加负载。报文功能报文ID方向触发方式数据字节数急停/报警0x001各站→主站事件4位置同步0x100主站→各站10ms周期8轴运行状态0x110~0x114各站→主站20ms周期8传感器数据0x200, 0x201传感器站→主站50ms周期8诊断请求/响应0x700起双向手动触发8波特率选了500kbps基于两点考虑一是总线长度控制在20米以内500kbps下的位时间2μs物理层完全撑得住二是这个速率下上述报文的周期负载率算下来约5%即使在峰值情况下负载率也不会超过15%留足了余量。6.2 硬件布局与终端电阻处理硬件上我的经验是尽量减少分支线尽量采用菊花链拓扑。CAN总线允许一定长度的分支具体和波特率相关但分支线越长信号反射越严重。这个项目的终端电阻放在了总线的物理两端也就是最远的两个节点机柜内各一个120Ω电阻实际测量总线等效电阻约60Ω。上电前我习惯先用万用表量一遍每个节点的CAN_H和CAN_L之间有没有短路再量总线的等效直流电阻。如果节点都断电了测到的应该是60Ω左右。如果只看到120Ω说明有一端终端电阻没接好如果接近0Ω说明某个节点的收发器可能已经损坏或者CAN_H和CAN_L之间短路了。6.3 联调中实际遇到的两个问题第一个问题是最初的波特率配置。虽然大家都写500kbps但主站的采样点配置在75%而某个运动控制站的固件里采样点默认在85%两者相差了10%的采样点位置。总线较短时勉强能通但一遇到个别帧的数据位边沿抖动就偶发CRC错误。后来统一到80%采样点错误帧消失。采样点这个东西很多工程师选用默认值就不管了但多节点网络中采样点的不一致会造成边际性的同步裕量差异日积月累就是偶发错误。第二个问题与屏蔽层接地有关。开始部署时屏蔽层只在主站一侧接了系统地从站侧悬空。电机变频器启动瞬间偶发错误帧增加。把屏蔽层在从站侧也通过1nF电容接地后干扰大幅改善。这种做法在工业现场很常见长距离线缆屏蔽层一端直接接地、另一端通过电容接地既能泄放高频干扰又能避免地环路电流在屏蔽层上造成低频干扰。6.4 验收测试和日常监测联调结束后我用CAN分析仪做了连续72小时的稳定性测试重点关注三个指标错误帧数量、总线最大占用率、低优先级报文的端到端延迟。72小时内错误帧为零最大占用率没有超过设计峰值各报文延迟在预期范围内。然后把错误帧统计、节点收发的错误计数器都映射到了HMI人机界面上以后现场运维人员可以直接在屏幕上看到总线健康状况不需要再每次扛着分析仪去现场测。这个项目做下来最大的感受是CAN总线项目成败往往不在协议本身而在物理层的细节和设计阶段的规划。ID规划得好不好、终端电阻装得对不对、采样点统不统一这些问题在一两个节点的小系统里可能不暴露但节点一多、环境一变问题就会像滚雪球一样冒出来。最后说点个人习惯。我在现场调试时永远会带三样东西一支能测CAN差分波形的示波器、一个带错误帧统计的USB-CAN分析仪、一支尖头万用表。遇到总线问题先用万用表排查终端电阻和短路再用分析仪统计错误帧方向最后用示波器看波形细节。大部分玄学问题最终都会落到这三个工具的交叉验证里。如果你正在被CAN总线的偶发错误或丢帧问题困扰不妨也从这个顺序试起大概率比直接改代码有效率得多。
返回列表