2. CAN协议基础:CAN 2.0A/B标准,帧格式与仲裁机制
大家好,我是你们的老朋友。今天咱们来聊聊CAN协议的基础。说实话,CAN协议在BMS里太常见了,几乎每个电池包都在用。我当年刚入行时,被CAN的各种帧格式搞得晕头转向,后来踩了不少坑才真正搞明白。今天我把这些经验分享给你,希望能帮你少走弯路。
2.1 CAN 2.0A与2.0B标准
先说说这两个标准。CAN 2.0A是早期版本,它的标识符只有11位。而CAN 2.0B扩展到了29位。你可能会问,为什么要扩展?
原因很简单——车上的节点越来越多。11位标识符最多支持2048个不同ID,在早期够用。但现在一辆车可能有几十个ECU,加上BMS内部的各种模块,11位就不够用了。我个人习惯在BMS项目中直接用CAN 2.0B,哪怕暂时用不到29位,也留个余地。
这里有个关键点:CAN 2.0B兼容CAN 2.0A。也就是说,2.0B的控制器可以接收2.0A的报文。但反过来不行。我在项目中遇到过,有人把2.0A的节点挂在2.0B的网络上,结果通信时好时坏。排查了半天才发现是兼容性问题。
2.2 帧格式详解
CAN协议有四种帧类型:数据帧、远程帧、错误帧、过载帧。咱们一个一个说。
2.2.1 数据帧
数据帧是最常用的。它负责把数据从一个节点传到另一个节点。结构上分为七段:
- 帧起始(SOF):1个显性位,告诉总线“我要开始发了”
- 仲裁段:包含标识符和RTR位。标准帧11位ID,扩展帧29位ID
- 控制段:包含IDE位、保留位和DLC(数据长度码)
- 数据段:0-8字节的数据。BMS里通常用来传电压、温度、电流
- CRC段:15位CRC校验+1位CRC界定符
- ACK段:2位。发送方发隐性位,接收方拉成显性表示确认
- 帧结束(EOF):7个隐性位
嗯,这里要注意数据段最多8个字节。为什么是8个?因为CAN总线设计时考虑到实时性,8字节刚好够传一条完整的控制指令。我见过有人非要把10个字节塞进一帧里,结果只能拆成两帧发,反而增加了复杂度。
2.2.2 远程帧
远程帧是用来请求数据的。它没有数据段,但DLC字段会指明请求的数据长度。举个例子:BMS的主控想知道某个从模块的电压,就可以发一个远程帧,从模块收到后回复数据帧。
说实话,远程帧在实际BMS项目中用得不多。我更喜欢用周期性发送的方式,简单可靠。远程帧有个坑:如果多个节点同时请求同一个ID,可能会引发总线冲突。我曾经因为这个原因,把远程帧改成了定时发送,问题就解决了。
2.2.3 错误帧
错误帧是CAN协议自带的错误处理机制。当节点检测到总线错误时,会主动发送错误帧。结构很简单:6个显性位的错误标志 + 8个隐性位的错误界定符。
错误帧分两种:主动错误帧和被动错误帧。主动错误帧的6个显性位会覆盖总线上的正常数据,相当于“强行打断”。被动错误帧的6个位是隐性的,不会影响其他节点。
2.2.4 过载帧
过载帧用于节点忙不过来时请求延迟。比如某个从模块正在处理数据,来不及接收新报文,就发过载帧让发送方等一等。
过载帧的结构和错误帧很像,也是6个显性位+8个隐性位。但触发条件不同:过载帧是节点主动发的,错误帧是检测到错误后自动发的。
我在BMS项目中很少遇到过载帧。因为BMS的通信节奏相对固定,只要设计合理,一般不会出现处理不过来的情况。但如果你用CAN做OTA升级,过载帧就可能频繁出现——大数据量传输时,接收缓冲区很容易满。
2.3 位填充机制
位填充是CAN协议里一个巧妙的设计。规则很简单:连续发送5个相同位后,自动插入一个相反位。
为什么要这么做?因为CAN总线靠电平跳变来同步时钟。如果连续发太多相同的位,节点之间的时钟就会漂移。位填充保证了总线上每5个位至少有一次跳变。
举个例子:你要发数据0x00(二进制00000000),CAN控制器会自动在每5个0后面插入一个1。实际发出去的是000001000001... 接收方收到后,再把插入的1去掉,还原原始数据。
嗯,这里要注意:位填充只适用于数据帧和远程帧的SOF到CRC段。ACK段、EOF段和帧间隔不参与填充。我刚开始做CAN驱动时,自己写了个位填充算法,结果在边界条件上出了bug,导致某些特定数据发不出去。后来直接用硬件自带的CAN控制器,再也没出过问题。
2.4 仲裁机制
仲裁是CAN协议最精彩的部分。它解决了“多个节点同时发送时谁先发”的问题。
原理很简单:显性位(0)优先于隐性位(1)。当两个节点同时发送时,它们逐位比较ID。谁的ID先出现显性位,谁就赢得仲裁,继续发送。输的节点自动转为接收模式,等总线空闲后再重发。
举个例子:节点A的ID是0x100(二进制000100000000),节点B的ID是0x200(二进制001000000000)。同时发送时,第3位(从高位算起)A是0,B是1。A的显性位覆盖了B的隐性位,A赢得仲裁。
你想想看,这个机制有多巧妙——仲裁过程不浪费任何带宽。输的节点自动退出发送,赢的节点继续发完整帧。不像其他总线协议,需要专门的仲裁帧或令牌传递。
在BMS中,我通常把关键报文(比如电池状态、故障告警)分配较低的ID值,确保它们能优先发送。非关键报文(比如日志信息)分配较高的ID值。这样即使总线繁忙,关键信息也不会被阻塞。
2.5 小结
好了,今天的内容就到这里。咱们回顾一下:
- CAN 2.0A用11位ID,2.0B用29位ID。建议直接上2.0B
- 四种帧格式:数据帧传数据,远程帧请求数据,错误帧报告错误,过载帧请求延迟
- 位填充保证时钟同步,每5个相同位插一个相反位
- 仲裁靠显性位优先,ID越小优先级越高
这些是CAN协议的基础,也是BMS通信的基石。下一章咱们聊聊CAN在BMS中的具体应用——怎么设计报文格式、怎么分配ID、怎么处理错误。到时候我会分享更多实战中的经验。
有什么问题,欢迎在评论区留言。咱们下期见!