干了这么多年嵌入式,跟总线打交道最多的就是CAN。不管是做车载ECU、工业设备,还是机器人通信,CAN协议都是绕不开的基础。很多刚入行的朋友一上来就啃协议手册,或者直接拿现成库调收发,结果总线上出来一个错误帧都不知道怎么回事。我整理了这篇CAN协议基础的内容,重点讲清楚CAN协议的种类(CAN 2.0A、CAN 2.0B、CAN FD)和CAN数据帧的完整结构,以及我用分析仪抓帧、排错的一些实际操作经验。不管是刚开始学CAN,还是正在调试手头设备,这篇文章应该能帮你把底盘打牢。
1. CAN协议家族图谱:从CAN 2.0A到CAN FD
1.1 标准帧与扩展帧的本质区别
CAN协议最初由Bosch在1986年提出,后来被ISO标准化。大家最常听到的CAN 2.0规范分为A和B两个部分,它们的核心区别在于标识符(Identifier,简称ID)的长度。
CAN 2.0A定义的是标准帧,标识符只有11位。11位意味着什么?二进制范围是0x000到0x7FF,一共2048个ID。在早期的汽车电控系统里,发动机、ABS、变速箱、仪表盘这些ECU加起来也就几十个节点,每个节点发几个报文,2048个ID完全够用。所以早期设计就把ID长度定在了11位,成本低、帧短、实时性也好。
后来功能越来越复杂,总线上的报文种类越来越多,11位就不够用了。于是CAN 2.0B引入了扩展帧,标识符扩展到29位,也就是在11位基础ID后面再追加18位扩展ID。29位能提供的地址空间是2^29,超过5亿个,这在工程上基本等于无限了。要注意的是,CAN 2.0B规范还区分了两种设备:一种是只支持标准帧的被动兼容设备,另一种是既能发也能收扩展帧的主动设备。实际项目里,只要总线上有一个节点需要扩展帧,那么所有节点的控制器最好都选支持扩展帧的型号,否则会出现接收过滤配置不上的情况。
我记得第一次做多ECU联调时,某个节点用的老款芯片只支持CAN 2.0A,结果扩展帧报文一上总线,这个节点就直接报错。后来换了支持CAN 2.0B的控制器,问题才消失。这个坑在新老设备混用的产线上尤其常见,大家在选型时一定先确认清楚。
1.2 CAN FD:为什么我们会需要它
CAN FD(Flexible Data-rate)是Bosch在2012年推出的增强版,2015年被ISO 11898-1:2015收录。它解决的核心痛点是两个:带宽不够和数据长度不够。
传统CAN的数据段最长只有8字节,波特率主流是500kbps或者250kbps。放在十年前的车载网络里足够,但放到现在的OTA升级、ADAS传感器数据、多屏娱乐系统上,传输固件动辄几十MB,用CAN帧一包8字节地传,能传到天荒地老。CAN FD做了两件事:
一是把数据场的最大长度从8字节提升到64字节。大家别小看这个提升,64字节意味着一条报文可以承载一个完整的诊断故障码快照,或者一段音频采样数据,传输效率高了很多。
二是引入可变速率传输。CAN FD允许在仲裁段用标准速率(比如500kbps),在数据段切换到一个更高的速率(比如2Mbps或5Mbps)。为什么这么做?因为CAN总线在隐形位到显性位的切换过程中,需要足够的上升/下降时间让所有节点采样一致。仲裁段要保持低速率,是为了保证多个节点同时发帧时能可靠地进行位仲裁;而数据段里只剩一个节点在发,这时可以把速率提上去而不影响仲裁可靠性。
这个设计非常巧妙,既保留了CAN原有的仲裁机制,又把数据吞吐量提了一个台阶。实测下来,同样一条总线上多个节点都用CAN FD,数据段的吞吐量能比传统CAN提升3到8倍。当然,前提是总线布线质量和收发器必须跟得上,否则高速率下沿变差,误码率会直线上升。
1.3 不同协议版本之间的兼容性
很多人关心CAN 2.0和CAN FD能不能混用。答案是:可以,但有条件。CAN FD设计时就考虑了向后兼容,FD帧的仲裁段仍然使用标准速率,且帧格式里用一个FDF位(Flexible Data-rate Format位)来区分这是传统CAN帧还是FD帧。传统CAN控制器收到FD帧时,如果它不认识FDF位,就会把这一帧判定为错误帧,然后总线上的错误计数器会增长。
所以实际部署中有两种做法:如果整个网络里的节点都升级为CAN FD控制器,那就做纯FD通信;如果只是部分节点升级,那么传统节点必须挂在单独的网段上,或者通过网关做协议转换,不要让FD帧直接经过旧节点的收发器。这点在改造成本项目时一定要规划好。
2. 深入拆解CAN数据帧的每一个字段
2.1 帧起始与仲裁段
先拿最常用的标准数据帧举例,把帧从第一个bit到最后一个bit全部拆开看,你会对整个协议有种豁然开朗的感觉。
帧起始(SOF)只有1个bit,固定为显性(逻辑0)。总线在空闲状态时是隐性的(逻辑1),当某个节点发送SOF时,总线电平从隐性跳到显性,这个下降沿就是所有节点同步的起点。说白了,它就像一篇文章的开头标点,告诉所有节点“我要开始发内容了”。
紧跟着就是仲裁段。标准帧的仲裁段由12位组成:11位ID加1位RTR(Remote Transmission Request)。扩展帧的仲裁段更复杂:11位基础ID、1位SRR(Substitute Remote Request)、1位IDE(Identifier Extension)、18位扩展ID、最后是1位RTR。
这里有个重点:在CAN总线中,ID不仅用来标识报文的来源和优先级,还直接参与仲裁。多个节点同时发帧时,总线会根据ID逐位仲裁,显性位(0)优先于隐性位(1)。所以ID数值越小,优先级越高,这一点在划分报文优先级时必须慎重。想象一下,如果某个关键的安全气囊报文不小心被分配了一个大ID值,而在同一时刻有一个普通传感器报文也在发,那么传感器反而会先发出去,这在汽车这种对安全要求极高的场景里是不可接受的。
2.2 控制段、数据段与CRC段
仲裁段之后是控制段。标准帧的控制段由6位组成:IDE位、保留位r0和4位DLC(数据长度码)。扩展帧则是保留位r1、r0和DLC。
IDE位的作用是表示当前是不是扩展帧,标准帧里IDE是显性,扩展帧里是隐性。保留位留作以后扩展,传统CAN里它们必须发显性,但接收端一般不去检查,所以很多设备实际发送时也是显性。
DLC字段大家要重点记:它用4位二进制表示数据段的字节数,范围是0到8。在CAN FD里情况不一样了,DLC不是线性对应字节数,而是有跳变的编码表,下面专门说。
数据段就是真正要传的内容,传统CAN最多8字节。数据段是唯一的,和ID不同,它不参与仲裁,也就是说数据段的每个bit都按正常位时序发送,不会因为其他节点的干扰而中断。
数据段之后是CRC段。传统CAN的CRC是15位,基于多项式x^15 + x^14 + x^10 + x^8 + x^7 + x^4 + x^3 + 1(即CRC-15)计算,覆盖SOF、仲裁段、控制段、数据段的内容。别以为CRC只是随便加个校验,在CAN的强电磁干扰环境下,CRC是防止错误帧被误采纳的最后一道防线。接收节点会重新计算一遍CRC,如果和发送方附带的不一致,就会忽略这一帧并发出错误帧。
CAN FD的CRC更复杂一点,它除了要覆盖前面的字段,还要额外处理数据段的高速位流。由于数据段存在位速率切换,传统的一位一采样的CRC计算方式不适用,所以CAN FD在CRC段里额外加入了“填充位计数”信息和“奇偶校验位”,用于检测高速段的位填充是否正确。这也是为什么CAN FD帧的CRC字段长度是动态的,可以是17位或21位,取决于数据段长度是否超过16字节。
2.3 ACK槽与帧结束
CRC段后是ACK段,总共2位:一个ACK槽位加一个ACK分隔位。
ACK槽位非常巧妙。发送方在发出ACK槽位时,会主动发送一个隐性位,然后释放总线。总线上其他所有正常接收该帧的节点,如果在本帧过程中没有发现错误,就会在这个槽位发送一个显性位来“确认”。也就是说,发送节点通过采样到一个显性位,就知道至少有一个节点成功接收了这帧。如果没有任何节点应答,发送方会认为自己发出的帧没有被正确接收,虽然它不会因此重发(重发是上层协议的事),但这会让发送节点的错误计数器增加。
这个机制我第一次理解时觉得很巧妙——它把“点对点确认”变成了一种“总线级广播确认”。代价是没有节点能确认具体是哪个节点收到了,但在广播式的CAN总线上,这种设计已经足够高效。
最后一小段是EOF(End of Frame),标准帧是7个隐性位,扩展帧也是7个隐性位。EOF之后总线上就会恢复空闲状态,允许下一帧开始。帧结束后面还可能有3个bit的ITM(Intermission)间隔,也叫帧间隔,让节点有时间处理接收缓存。
3. 数据帧之外的帧类型与总线仲裁机制
3.1 四种帧类型
很多人以为CAN总线上只跑数据帧,其实协议定义了四种帧:
第一是数据帧,用来承载实际数据,也就是上面拆解的那种。第二是远程帧,节点可以主动请求总线上另一个节点发送某个特定ID的报文——远程帧没有数据段,只有ID和DLC。
第三是错误帧,当节点检测到总线错误时,会主动发一个由6个显性位加8个隐性位组成的错误帧,用来打断当前正在传输的帧,告诉所有节点“刚才这帧有问题”。第四是过载帧,一个节点如果接收缓冲区满了,可以发送过载帧来延迟下一帧的到达。
我喜欢把这四种帧理解成日常沟通的场景:数据帧是正常说话,远程帧是点名“某某某你来说两句”,错误帧是有人突然打断说“你刚才说错了”,过载帧是“你慢点说我处理不过来”。这样记起来非常快。
3.2 仲裁机制:为什么不会发生冲突
总线仲裁是CAN协议最具魅力的地方。在没有CAN的时代,多设备共享一根总线通常都需要“令牌”或者“主从问答”来避免冲突。CAN的做法非常简单又优雅:靠ID逐位仲裁。
初始化时所有节点监听总线,如果两个节点同时开始发送,那么它们每个bit都会往总线上写数据,同时回读总线电平。因为显性位能覆盖隐性位,所以当某一bit出现一个节点发隐性(1)、另一个节点发显性(0)时,总线呈现显性,发隐性的那个节点会发现自己“写1读0”,立刻退出仲裁,转入接收模式。整个过程一直在比较,直到剩下最后一个节点继续发完整个帧。
这个机制真正厉害的地方在于:仲裁过程不会中断正在进行的帧传输,赢家从头到尾不需要重发,整个总线带宽被充分利用。对比一些冲突检测需要重发的协议(比如经典的以太网CSMA/CD),CAN在这个方面的效率要高得多。
当然,仲裁的前提是所有节点对位的采样时机一致。如果两个节点波特率偏差过大,或者采样点设置不合适,就可能出现“前一个节点还在发低位,后一个节点已经采到自己想要的电平”的情况,导致仲裁失效。后面我专门讲采样点设置时再细说。
3.3 位填充机制与同步
CAN还有一个容易被忽视但极其关键的机制:位填充(Bit Stuffing)。协议规定,在SOF到CRC段之间,如果连续出现5个相同的电平,就会在第6个bit的位置自动插入一个相反电平的填充位。
为什么要做位填充?因为接收节点需要从总线的边沿变化中恢复出时钟信息。如果总线上长时间没有电平跳变,接收端的PLL就会失去参考,采样就会漂移。位填充保证了每个bit流里最多5个bit就会出现一次边沿,这样接收节点就能持续从边沿中做重新同步。
但同时也有一个坑:位填充不是协议用户可控的,它是控制器硬件自动完成的。在很多老手册里你会看到“最大帧长度”这种说法,实际上算的是含填充位的最坏情况。CAN FD在高速数据段对位填充策略做了调整,它是用固定的“每4个bit插入一个填充位”的方式来保证高速采样的稳定性,所以CRC计算时也必须把这部分统计进去。
理解位填充对排查问题很有帮助。比如你看到某帧数据非常长,比理论算出来的最大长度还长,那大概率是填充位导致的,不是控制器异常。另外,连续5个相同电平后的第6个bit如果仍是相同电平,就会触发填充错误,这通常说明总线上有节点时序异常,是硬件问题的一个重要特征。
4. 实操:用分析仪抓帧与解析CAN数据
4.1 工具准备与连接方式
理论讲得再多,不如自己抓一帧数据看看。实操需要的工具很简单:一个CAN分析仪(市面上常见的兼容周立功驱动或者PCAN的都可以)、两个120欧终端电阻(很多分析仪和开发板上已经内置了,但外接时不要重复接)、以及一个可以发CAN报文的上位机软件(比如CANTest、PCAN-View、或者汽车电子常用的CANalyzer)。
接线方面,CAN分析仪的CANH接总线的CANH,CANL接总线的CANL,GND要跟总线上的设备共地。这一步很多人忽略,实际上CAN收发器虽然可以用差分信号抗共模干扰,但如果各节点地电位差过大,共模范围超限后一样会通信异常。
接好线后,把波特率设置为总线实际波特率。如果你不知道具体波特率,可以用分析仪的“自动识别波特率”功能去扫描。最常用的候选波特率是125k、250k、500k、1M,扫描时间一般几秒就能找到。
4.2 一帧数据从波形到解析的完整过程
假设我用分析仪抓到了一条标准数据帧,原始数据显示为:
ID = 0x123, DLC = 8, Data = 01 02 03 04 05 06 07 08这个只是上层软件简化后的显示,硬件底层实际传输的是完整帧结构。如果我们把这条帧放到示波器上观察,能看到的波形顺序是:
- 1个显性SOF
- 11位ID(0x123的二进制是0 0010 0011,但要注意总线上是高位先发,所以顺序是从第10位到第0位)
- 1位RTR(数据帧里是显性0)
- 1位IDE(标准帧里是显性0)
- 1位保留位(显性0)
- 4位DLC(数值为8,即1000,也是高位先发)
- 8字节数据,共64位,逐字节高位先发
- 15位CRC,加上1位CRC分隔符(隐性1)
- 1位ACK槽(发送方发隐性1,接收方回一个显性0来覆盖)
- 1位ACK分隔符(隐性1)
- 7个EOF(全为隐性1)
做嵌入式上位机开发时,经常需要自己解析这个格式。我给你一个最简的计算方式:在不考虑位填充的理想情况下,标准数据帧的物理位长度可以用这个公式估算:
总位数 = 1(SOF) + 11(ID) + 1(RTR) + 1(IDE) + 1(r0) + 4(DLC) + 8*DLC字节数 + 15(CRC) + 1(CRC分隔) + 1(ACK) + 1(ACK分隔) + 7(EOF)
代入DLC=8,就是1+11+1+1+1+4+64+15+1+1+1+7=108位。实际线缆上的位流会因位填充变长,标准帧理论上最长约130位左右。
4.3 CAN FD帧的抓取与速率切换
如果你用支持CAN FD的分析仪去抓CAN FD报文,上面那个公式就要变了。CAN FD帧在仲裁段和数据段之间有一个速率切换点,位于BRS位。波形上你会看到明显的一处“变密”现象,也就是后面的bit宽度变窄。
抓CAN FD帧时,关键是正确配置仲裁段波特率和数据段波特率。比如仲裁段是500k,数据段是2M,那么分析仪软件里要在“FD设置”里分别填写这两个值。采样点位置也要针对两段分别调整。如果配置不对,最常见的现象是:能看到帧头ID,但数据段全是乱码,或者直接报“CRC错误”。
我实测过一种情况:用默认采样点70%去收一个数据速率2M的CAN FD网络,误码率在1%左右;把采样点调到75%后,连续跑一小时零错误。所以做CAN FD项目时,采样点设置不要照抄默认值,一定要根据你的线缆长度和收发器规格去算。
5. 常见问题与排查技巧实录
5.1 常见问题速查表
我在实验室和现场都排过不少CAN总线问题,下面这个表是我自己的经验总结,大部分场景都适用:
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 所有节点都收不到数据 | 总线缺少终端电阻或终端电阻接错 | 测CANH与CANL之间电阻,正常应为60欧左右 |
| 单个节点收不到 | 该节点波特率配置错误,或节点处于Bus Off状态 | 断开该节点单独测试,修改波特率后重新上电 |
| 偶发错误帧 | 采样点偏移、线缆过长、布线靠近干扰源 | 调整采样点,缩短分支线,检查屏蔽和接地 |
| 一上电就Bus Off | 控制器连续发错误帧 | 检查收发器供电、CANH/CANL是否接反 |
| 高速CAN FD数据乱码 | 数据段波特率配置错误 | 确认分析仪和被测设备的仲裁/数据两段速率均一致 |
| 明明能收到但CRC报错 | 收发器摆率不够或总线阻抗不匹配 | 检查线缆特性阻抗,尽量用120欧双绞屏蔽线 |
5.2 排查经验:先量电阻,再抓波形
很多朋友拿到设备就问“为什么CAN不通”,我一般建议按这个顺序排查:
先量总线电阻。正常工作的CAN总线,在任意节点上测CANH到CANL之间的直流电阻,应该是60欧左右——这是两个120欧终端电阻并联的结果。如果测出来约120欧,说明只有一端接了终端电阻;如果接近0欧,说明有短路;如果是无穷大,说明两端都没接或线断了。这个测试5秒钟就能做,能排除一大半硬件问题。
再抓波形。用示波器量CANH和CANL对地的波形。正常通信时,显性位CANH约3.5V、CANL约1.5V,差分约2V;隐性位两者都在2.5V左右,差分约0V。如果你看到差分电压偏小,比如小于1.5V,那大概率是终端电阻或者收发器驱动能力出了问题。偏大的话,说明共模电压有问题或两个节点参考地不一致。
最后才是用分析仪抓帧看错误类型。CAN控制器的错误寄存器会记录最近一次的错误类型,比如位错误、填充错误、校验错误、ACK错误等。每种错误对应不同的排错方向:位错误说明发送时总线电平跟预期不一致,往往是有其他节点干扰或者收发器有问题;填充错误说明位流格式不对,通常是波特率不匹配;ACK错误说明发出了但没人接收,检查总线上是否只有发送方。
5.3 独家避坑技巧
最后分享几个常规文档里不太会写的细节:
第一,总线分支线越短越好。CAN不是点对点的串行总线,而是像一棵树一样挂很多节点,每个节点出来的那根线叫“分支线”。分支线越长,反射越大,对高速传输的影响越明显。经验值是波特率1M时,分支线要控制在30厘米以内;500k时可以放宽到1米左右。现场实在避免不了长分支线时,可以适当降低波特率或者用双绞线加磁环。
第二,采样点不要用默认80%一刀切。标准CAN推荐采样点一般在70%到80%之间,但位数不同、速率不同、线缆长度不同,最佳值都不一样。我的习惯是先算一下:采样点=(同步段+传播时间段+相位缓冲段1)/(同步段+传播时间段+相位缓冲段1+相位缓冲段2)。比如你用1M波特率、8分频,同步段1个Tq,传播段3个Tq,相位段1设为5个Tq,相位段2设为3个Tq,那么采样点就是(1+3+5)/(1+3+5+3)=69.2%。线缆短、波特率低时采样点可以稍微靠前;线缆长、干扰大时稍微靠后,这个需要实际抓波形调。
第三,终端电阻不要重复接。有些开发板内部已经焊了120欧电阻,如果用外部分析仪又接了120欧,并联后只有60欧,终端阻抗失配会增加反射。接之前拿万用表量一下最靠谱。
第四,远程帧这玩意能不用就不用。它虽然协议里有,但在实际项目中容易引发“广播风暴”——多个节点同时请求同一个ID,导致总线上连续不断出现远程帧。现代开发更建议上层应用直接用数据帧周期发送,安全性高得多。
我个人在实际项目里的体会是,CAN协议最核心的设计哲学就是“简单、健壮、不依赖主节点”。它的仲裁机制和错误处理机制,让整个总线在没有中心管理器的情况下依然能稳定运转。无论你是刚接触CAN还是被某个棘手总线问题卡了一整天,把帧结构、仲裁、位同步这三块彻底吃透,绝大部分问题你都能自己找出答案。后面如果有机会,我准备再写一篇CANopen和J1939这些上层协议的实战笔记,那是另一片广阔的战场了。