第2章:CAN基础回顾
各位同学,在正式进入CANopen协议之前,咱们得先把CAN总线的基础打牢。说实话,我见过太多工程师一上来就啃CANopen协议栈,结果遇到总线通信问题就抓瞎——根源往往出在CAN底层。今天这一章,我就带大家把CAN总线的物理层、数据帧、仲裁机制和错误处理这四块硬骨头啃下来。
2.1 CAN总线物理层
CAN总线物理层,说白了就是信号怎么在线上跑。它用的是差分信号传输,两根线:CAN_H和CAN_L。我刚开始接触时也觉得奇怪,为什么非要用两根线?后来在项目现场吃过亏才明白——差分信号抗干扰能力是真的强。
关键参数:
- 显性电平(Dominant):CAN_H ≈ 3.5V,CAN_L ≈ 1.5V,逻辑0
- 隐性电平(Recessive):CAN_H ≈ 2.5V,CAN_L ≈ 2.5V,逻辑1
- 总线终端电阻:120Ω(两端各一个)
嗯,这里要注意:显性电平会覆盖隐性电平。这个特性直接决定了后面的仲裁机制。我曾经在一个项目中,终端电阻没焊好,结果总线信号反射得一塌糊涂,通讯时好时坏。排查了整整两天才找到问题。
CAN总线的拓扑结构很简单,就是一条总线挂多个节点。节点数理论上可以到110个,但实际项目中我建议别超过30个。为什么?因为节点多了,总线负载电容变大,信号边沿会变缓,通讯距离和速率都会受影响。
| 总线长度 | 最大波特率 |
|---|---|
| 40m | 1 Mbps |
| 100m | 500 kbps |
| 500m | 125 kbps |
| 1km | 50 kbps |
你想想看,如果要在100米距离上跑1Mbps,信号早就畸变得没法看了。所以选波特率时,一定要先算好总线长度。
2.2 CAN数据帧结构
CAN数据帧有标准帧和扩展帧两种。标准帧11位ID,扩展帧29位ID。我个人习惯在CANopen中只用标准帧,因为11位ID已经够用了,扩展帧反而增加了协议处理的复杂度。
来看一个标准数据帧的结构:
SOF | 11位ID | RTR | IDE | r0 | DLC | 数据段(0-8字节) | CRC | ACK | EOF
每个字段的含义:
- SOF(Start of Frame):1位显性位,同步所有节点
- ID(Identifier):11位,决定优先级
- RTR(Remote Transmission Request):区分数据帧和远程帧
- DLC(Data Length Code):4位,表示数据字节数(0-8)
- 数据段:最多8字节
- CRC:15位校验 + 1位定界符
- ACK:2位,接收节点应答
- EOF:7位隐性位,帧结束
个人经验:DLC字段虽然可以表示0-8,但实际项目中我建议数据段尽量填满8字节。因为CAN帧的协议开销是固定的,多传几个字节并不会增加太多总线负载。我曾经优化过一个电机控制项目,把多个状态量打包到一个8字节帧里,总线利用率直接降了一半。
2.3 CAN总线仲裁机制
仲裁机制是CAN总线最巧妙的设计之一。多个节点同时发送时,谁优先级高谁先发。怎么判断优先级?看ID,ID越小优先级越高。
具体过程是这样的:所有发送节点在SOF之后同步开始逐位发送ID。每个节点在发送位的同时,也会监听总线电平。如果自己发送的是隐性位(1),但总线上是显性位(0),说明有更高优先级的节点在发送,自己就乖乖退出发送。
为什么会这样?因为显性位会覆盖隐性位。你想想看,如果两个节点同时发送,一个ID=0x100,一个ID=0x200。在ID的最高位,0x100是0(显性),0x200是1(隐性)。总线被拉成显性,0x200的节点检测到冲突,立刻停止发送。
避坑指南:我曾经在一个项目中,把两个节点的ID设成了0x100和0x101。结果发现0x100的节点总是能抢到总线,0x101的节点经常发不出去。后来才意识到,这两个ID太接近了,仲裁时几乎每次都是0x100赢。所以设计ID分配时,建议给不同优先级的节点留出足够的间隔。
仲裁机制的好处是:总线不会因为冲突而浪费带宽。失败的节点自动转为接收模式,等总线空闲后再重发。整个过程对上层协议完全透明。
2.4 CAN错误处理机制
CAN总线的错误处理机制,可以说是它能在工业环境中站稳脚跟的关键。它有一套完整的错误检测和故障隔离机制。
错误类型有五种:
- 位错误:发送节点发送的位和监听到的位不一致
- 填充错误:连续6个相同电平(违反位填充规则)
- CRC错误:接收节点计算的CRC与发送的不一致
- 形式错误:固定格式的位段出现非法电平
- 应答错误:发送节点在ACK槽没收到显性位
每个节点都有两个计数器:发送错误计数器(TEC)和接收错误计数器(REC)。错误多了,计数器会累加。当TEC或REC超过某个阈值,节点状态会发生变化:
| 节点状态 | 条件 | 行为 |
|---|---|---|
| 错误主动 | TEC < 128 且 REC < 128 | 正常发送,检测到错误时发送主动错误帧 |
| 错误被动 | TEC > 127 或 REC > 127 | 只能发送被动错误帧,且发送前要等待8个隐性位 |
| 总线关闭 | TEC > 255 | 完全断开与总线的连接 |
关键点:总线关闭状态是自动恢复的。节点会在检测到128次11个连续隐性位后,自动回到错误主动状态。这个机制保证了总线不会因为一个故障节点而瘫痪。
我记得有一次在现场调试,一个电机驱动器频繁报错。用CAN分析仪抓包发现,那个节点一直在发送主动错误帧。查了半天,原来是它的CAN收发器供电电压偏低,导致发送的显性位电平不够。其他节点收不到正确的信号,就不断回复错误帧。最后换了电源模块才解决。
嗯,这里要提醒大家:错误处理机制虽然强大,但不要完全依赖它。在项目开发阶段,我建议把每个节点的TEC和REC值通过CANopen的SDO读出来,监控错误计数器的变化趋势。如果某个节点的错误计数器持续增长,说明硬件或布线有问题,要尽早排查。
好了,CAN基础就回顾到这里。下一章我们正式进入CANopen协议的核心——对象字典和通信对象。到时候你会发现,有了CAN底层的扎实基础,理解CANopen会轻松很多。