3. CMS32 CAN控制器架构

各位同学,今天我们来啃CAN通信里最核心的一块——报文帧。说实话,这部分内容我第一次学的时候也觉得有点枯燥,但后来在项目里被坑过几次,才真正体会到这些概念有多重要。咱们慢慢聊,我尽量把每个知识点都讲透。

3.1 四种报文帧格式

CAN总线上跑的数据,一共就四种格式。你想想看,一个通信协议,只定义了四种报文,是不是挺简洁的?

帧类型 用途 谁发
数据帧 发送节点把数据传给其他节点 任意节点
远程帧 请求其他节点发送特定ID的数据 任意节点
错误帧 检测到总线错误时通知所有节点 检测到错误的节点
过载帧 接收节点忙不过来,请求延迟发送 接收节点

实际项目中,数据帧用得最多,远程帧偶尔用,错误帧和过载帧嘛——我希望你永远别在调试时看到它们。但真看到了也别慌,后面章节我会专门讲怎么处理。

3.1.1 数据帧结构详解

数据帧是CAN通信的主角。标准格式有11位ID,扩展格式有29位ID。我个人习惯用标准格式,除非项目里ID实在不够用。

一个标准数据帧长这样:

SOF | 11位ID | RTR | IDE | r0 | DLC | 0~8字节数据 | CRC | ACK | EOF

每个字段的含义:

  • SOF(Start of Frame):1位显性位,告诉总线“我要发数据了”
  • ID:11位标识符,决定优先级
  • RTR(Remote Transmission Request):数据帧里是显性位(0),远程帧里是隐性位(1)
  • DLC(Data Length Code):4位,表示数据字节数(0~8)
  • 数据段:0~8字节,想传啥传啥
  • CRC:15位校验码+1位分隔符
  • ACK:2位,接收节点确认
  • EOF:7位隐性位,帧结束
我的经验:DLC字段经常被新手忽略。有一次我调试一个电机控制器,对方发来的数据帧DLC写的是8,但实际只用了前4个字节。结果我的接收程序按8个字节解析,后面4个字节全是随机值,折腾了一下午才发现问题。记住:DLC必须和实际数据长度一致。

3.1.2 远程帧

远程帧说白了就是“请求帧”。节点A发一个远程帧,ID填成节点B的ID,节点B收到后就会发一个同ID的数据帧回来。

远程帧和数据帧的区别就两点:

  • RTR位:远程帧是隐性(1),数据帧是显性(0)
  • 没有数据段:远程帧的DLC虽然存在,但后面不跟数据字节

我记得有个项目,用远程帧做传感器数据轮询。主节点每隔100ms发一次远程帧,从节点收到后回复当前采样值。这种方式在节点少的时候挺好用,节点多了就得小心仲裁问题了。

3.1.3 错误帧与过载帧

这两种帧属于“异常情况”。

错误帧由6个连续的显性位(错误标志)加上8个隐性位(错误界定符)组成。任何节点检测到总线错误,都会立刻发错误帧。这时候整条总线上的节点都会收到通知。

过载帧结构类似,但触发条件不同——接收节点来不及处理数据时才会发。我在实际项目中很少见到过载帧,因为现在的MCU处理速度都够快。但如果你用低速MCU做CAN通信,就得留个心眼了。

避坑指南:我曾经在一个项目中,因为CAN收发器的终端电阻没焊好,导致总线信号反射严重,时不时冒出错误帧。用示波器一看,波形上全是毛刺。后来换了质量好的120Ω电阻,问题就解决了。硬件基础不牢,协议层再牛也白搭。

3.2 仲裁机制与ID优先级

CAN总线最牛的地方之一,就是它的仲裁机制。多个节点同时发数据怎么办?不冲突,不丢包,谁优先级高谁先发。

原理其实很简单:总线电平是“线与”逻辑。显性位(逻辑0)会覆盖隐性位(逻辑1)。仲裁的时候,每个节点一边发自己的ID,一边监听总线。如果发现自己发的位是隐性,但总线上是显性,说明有更高优先级的节点在发数据,自己就乖乖退出发送。

举个例子:

节点A ID:0x100(二进制:001 0000 0000)
节点B ID:0x200(二进制:010 0000 0000)

仲裁过程:
第1位:A发0,B发0 → 总线0,继续
第2位:A发0,B发1 → 总线0(A的显性位覆盖了B的隐性位)
        B检测到总线与自己发送不一致 → B退出仲裁
结果:节点A获胜,继续发送数据

所以ID值越小,优先级越高。0x000是最高优先级,0x7FF是最低优先级(标准帧)。

关键点:仲裁过程不破坏数据,不浪费带宽。输掉仲裁的节点会在总线空闲后自动重发。这种机制在实时性要求高的场合特别有用——比如汽车上的刹车信号,必须用低ID保证优先发送。

我建议你在设计系统时,把关键控制信号分配小ID,普通状态信息分配大ID。比如:

  • 0x001~0x0FF:紧急控制指令(刹车、转向等)
  • 0x100~0x2FF:周期性状态数据(车速、转速等)
  • 0x300~0x7FF:诊断信息、配置参数等

3.3 位填充与CRC校验

这两个机制保证了CAN通信的可靠性。没有它们,总线上的数据分分钟出错。

3.3.1 位填充规则

位填充是为了保证总线上的电平有足够多的跳变,方便接收节点同步时钟。规则很简单:

连续发送5个相同电平的位后,自动插入一个相反电平的位。

比如你连续发了5个显性位(00000),发送器会在后面自动加一个隐性位(1)。接收器收到后,会自动把这个填充位去掉,还原原始数据。

位填充的范围包括:SOF、ID、控制字段、数据字段、CRC字段。但CRC界定符、ACK字段和EOF不参与填充。

我的经验:位填充规则在调试时经常被忽略。有一次我用逻辑分析仪抓CAN总线数据,发现波形里多了一个位,还以为硬件有问题。后来一查,原来是位填充导致的。所以看原始波形时,心里要清楚哪些是填充位,哪些是真实数据。

3.3.2 CRC校验

CAN的CRC校验用的是15位CRC,生成多项式是:

x^15 + x^14 + x^10 + x^8 + x^7 + x^4 + x^3 + 1

发送节点计算CRC值,放在CRC字段里。接收节点收到数据后,用同样的多项式重新计算,如果结果不一致,就说明数据在传输过程中被破坏了。

CRC校验覆盖的范围是:SOF、ID、控制字段、数据字段。也就是说,从帧开始到CRC字段之前的所有位,都要参与CRC计算。

实际项目中,CRC校验出错的概率其实不高。但如果你的系统工作环境电磁干扰严重(比如靠近大功率电机),CRC错误就会频繁出现。这时候可以考虑在应用层再加一层校验,比如每个数据包末尾加一个累加和。

避坑指南:我曾经在一个项目中,发现CAN总线偶尔出现CRC错误,但频率不高,大概每小时一次。排查了很久,最后发现是CAN收发器的共模电感选型不对,导致共模抑制能力不足。换了一个共模电感后,CRC错误就再也没出现过。所以硬件设计对通信质量的影响,有时候比软件更大。

3.4 知识体系总览

下面这张图把本章的核心概念串起来了,你可以对照着复习:

CAN报文帧格式与通信机制 四种报文帧 数据帧 远程帧 错误帧 过载帧 仲裁机制 线与逻辑:显性覆盖隐性 ID越小,优先级越高 失败节点自动重发 可靠性保障 位填充(5位插1位) 15位CRC校验 标准数据帧结构:SOF | 11位ID | RTR | IDE | r0 | DLC | 0~8字节数据 | CRC | ACK | EOF 核心原则:ID决定优先级,仲裁不丢数据,位填充保证同步,CRC确保完整 四种帧各有用途,但数据帧是日常使用最多的

好了,这一章的内容就这些。四种帧格式、仲裁机制、位填充和CRC校验,是CAN通信的基石。你把这些搞懂了,后面学CANopen、J1939这些高层协议就会轻松很多。