1.CAN 通讯介绍
CAN(Controller Area Network控制器局域网,简称CAN或CAN bus)是一种丰富的车用总线标准。
设计用于不需要主机的情况下,允许网络上的单片机和仪器相互通信。
它基于消息传递协议,设计之初在车辆上复用通用线缆,以降低铜线使用量,后来也被其他行业所使用。
CAN拥有良好的弹性调整能力,可以在现有网络中增加节点而不用在软硬件中做出调整。
2.物理层
2.1.CAN网络节点
一个CAN控制器
核心功能:它负责 CAN 报文的发送与接收控制,完成总线显性 / 隐性电平解析、帧结构处理、数据校验、位时序控制及总线仲裁等功能,并通过 CAN_Tx/CAN_Rx 引脚与外部收发器交互,将逻辑电平信号转换为总线差分信号。
隐性电平:总线无驱动,差分电压接近 0,对应逻辑 1
显性电平:总线被主动驱动,存在明显差分电压,对应逻辑 0
位置:通常集成在 MCU 内部,是 CAN 通信的协议处理核心。
一个CAN收发器
核心功能:实现逻辑电平 ↔ 总线差分电平转换,将 CAN 控制器的 TTL 逻辑信号转为 CAN_H/CAN_L 差分信号,同时提供总线驱动、抗干扰与隔离保护。
位置:位于控制器与物理总线之间,属于外部接口芯片。我开发用的是PD1050收发器芯片。
STM32 特性:片内集成 CAN 控制器,无需外接协议芯片,仅需外接 CAN 收发器。
CAN控制器和CAN收发器之间通过CAN_Tx和CAN_Rx信号线相连接。
CAN收发器和CAN总线之间使用CAN_High、CAN_Low信号线相连
2.2.CAN网络节点发数据流程
MCU 内部的 CAN 控制器准备好二进制数据。
控制器通过 CAN_Tx 线,把逻辑电平信号发给收发器。
收发器将逻辑电平转换为差分电平:
逻辑 0 → 显性电平(CAN_H 拉高、CAN_L 拉低,差分电压大)
逻辑 1 → 隐性电平(CAN_H 和 CAN_L 电压接近,差分电压≈0)
差分信号通过 CAN_H/CAN_L 输出到 CAN 总线上,被其他节点接收。
2.3.CAN网络节点接收数据流程
通过收发器接收总线上的数据到控制器上时,则是与发送数据流程相反。
收发器把总线上收到的CAN_High和CAN_Low信号转换成普通的逻辑电平信号。
通过CAN_RX输出到控制器中。
2.4.CAN总线网络
2.4.1.CAN总线网络介绍
当CAN线上接入多个设备时,就构成了CAN总线网络。
根据接法不同,总线网络分闭环总线网络、开环总线网络 2种。
2.4.2.闭环总线网络
闭环总线网络: CAN总线上CAN_High和CAN_Low两端通过120欧电阻连接形成闭合回路,这样的CAN总线网络就称闭环总线网络。
协议标准:遵循 ISO11898 高速 CAN 标准,最高通信速率 1 Mbps,对应最大传输距离约 40 m。
不同CAN协议标准对比如下表。
闭环总线网络连接如下图所示。
扩展:
CAN FD(ISO 11898-1 扩展):CAN FD 协议在数据段可支持 5 Mbps 高速率(最高甚至 10 Mbps),适合短距离板间或设备间通信,传输距离随速率提升而显著缩短(5 Mbps 时通常仅数米)。
CAN XL:CAN FD 协议在数据段可支持 20Mbps 高速率(最高甚至 50 Mbps)
2.4.3.开环总线网络
开环总线网络: CAN总线上CAN_High和CAN_Low左端分别串联一个2.2K欧的电阻,两根总线独立不形成闭环,这样的CAN总线网络就称开环总线网络。
协议标准:遵循 ISO11519-2 标准低速,远距离的“开环网络”。最大传输距离1Km,通信速率最高125Kbps。
2.5.差分信号
2.5.1.差分信号介绍
CAN_High和CAN_Low走一对差分信号。
传统的单端信号传输:一根信号线一根地线。
差分传输是一种信号传输技术,差分传输在这两个根线上都传输信号,这两个信号的振幅相同,相位相反。
信号接收端比较这两个电压的差值来判断发送端发送的逻辑状态。
在电路板上,差分走线必须是等长、等宽、紧密靠近,且在同一层面的两根线。
2.5.2.差分信号优缺点
优点
抗干扰能力强:干扰噪声一般会等值、同时的被加载到两根信号线上。而其差值为0,即噪声对信号的逻辑意义不产生影响。
能有效抑制电磁干扰(EMI):由于两根信号线靠得很近且信号幅值相等,这两根线与地线之间的耦合电磁场也相等,同时他们的信号极性相反,其电磁将相互抵消。因此对外界和受外界的电磁干扰也小。
缺点
差分信号一定要走两根等长、等宽、紧密靠近,且在同一层面的线。对电路板比较小、走线比较紧张的情况下,给布线带来挑战。
串口 通信协议的演进,也体现出差分传输的优势:
RS232(非差分,两根线)→ RS485(半双工差分,两根线分时收发)→ RS422(全双工差分,收发各一组差分线,共四根)。
差分信号线具有这些优点,在USB协议、485协议、以太网协议及CAN 协议的物理层中,都使用了差分信号传输。
2.5.3.差分信号原理
以 ISO 11898-2 高速 CAN 为例,CAN 总线通过 CAN_High 与 CAN_Low 的电压差值 来传输逻辑 0/1:
逻辑1(隐性电平):
电平状态:CAN_High = CAN_Low=2.5V
电压差:VH-VL=0V
物理表现:总线无主动驱动,两根线电压相等,差分电压接近 0。
逻辑0(显性电平):
电平状态:CAN_High = 3.5V,CAN_Low=1.5V
电压差:VH-VL=2V
物理表现:总线被主动驱动,CAN_High 拉高、CAN_Low 拉低,形成明显电压差。
CAN 总线之所以是「线与」逻辑,是因为它的电平由所有节点共同决定:只要有一个节点发送显性电平(逻辑 0),总线就表现为显性,等价于所有节点输出做逻辑与运算。这种机制是 CAN 总线实现高效、可靠仲裁的基础。
CAN 线与逻辑的物理本质:显性电平为主动驱动,隐性电平为被动悬浮。
发送逻辑 0 时,节点会通过电流主动将 CAN_H 拉高、CAN_L 拉低,其驱动力远大于被动悬浮状态,因此只要有一个节点输出显性电平,总线就会被强制拉为显性。
3. 协议层
3.1.CAN 总线的广播通信机制
CAN总线是广播类型的总线。
这意味着所有节点都可以侦听到所有传输的报文,无法将报文单独发送给指定节点。
但是CAN硬件能够本地过滤功能,让每个节点对报文选择性地做出响应。每个节点可以通过设置验收滤波器,只对符合 ID 规则的报文进行接收和处理,对无关报文直接忽略。这种 “全网广播 + 本地过滤” 的模式,既保证了数据的共享性,又避免了节点 CPU 资源的浪费。.
3.2.CAN 报文的内容寻址与短帧特性
内容寻址而非地址寻址
传统串口、以太网等通信方式,通常在报文中携带明确的目标地址 / 源地址,用于指定收发双方。
而 CAN 报文中不包含任何节点地址,采用内容寻址方式:
短报文设计
CAN 总线采用短帧结构传输,报文长度有限,最大实用数据负载为 94 位。
短帧带来的优势非常明显:
3.3.CAN的帧(报文)种类
CAN总线上传输的报文有5种类型(或帧):数据帧、远程帧、错误帧、过载帧和帧间隔。
数据帧
数据帧是最常用的报文类型,用于发送单元向接收单元发送数据。
携带报文 ID 与 0~8 字节数据;
分为标准数据帧(11 位 ID)和扩展数据帧(29 位 ID);
所有正常的业务通信都依靠数据帧完成。
远程帧
远程帧用于接收单元向具有同样id的发送单元请求发送数据,自身不携带数据段。
格式与数据帧类似,但无数据域;
某节点发送对应 ID 的远程帧,拥有该 ID 数据的节点会自动回复数据帧;
实现 “按需请求数据” 的通信方式。
错误帧
错误帧当检测出错误时,向其他单元通知错误的帧。
用于通知全网当前总线出现异常;
触发错误处理机制,如报文重发、节点错误计数累加;
是 CAN 高可靠性的重要保障机制。
过载帧
过载帧用于节点向总线申请延迟,告知其他节点自身暂时无法接收新报文。
常见于节点处理速度不足、接收缓冲区满等场景;
强制总线空闲一段时间,避免数据丢失;
过载帧并不常用,因为当今的CAN控制器会非常智能化地避免使用过载帧。
帧间隔
用于将数据帧及遥控帧与前面的帧分离开来的帧。
其中,错误帧、过载帧、帧间隔都是由硬件自动完成,没有办法用软件来控制。对于一般使用者来说,只需要掌握数据帧和遥控帧。
数据帧和遥控帧有标准格式和扩展格式,标准格式有11位标识符,扩展格式有29位标识符。
3.4.数据帧(Data Frame)
数据帧是CAN总线中最核心的报文类型,用于实现总线上各节点间的数据传输,其结构具有固定规范,可分为标准帧和扩展帧两类。其中标准帧是最常用的类型,以下重点介绍其具体结构:
3.4.1.标准帧结构
标准帧结构由帧起始SOF、仲裁段、控制段、数据段、CRC段、ACK段、帧结束EOF 七个大部分组成,如下图所示即为标准帧结构。
针对上图标准帧结构的各段进行解释说明:
帧起始:Start Of Frame-SOF
位长:1bit,固定为显性信号(逻辑0)。
作用:明确表示数据帧(或远程帧)的传输开始。
发送规则:仅能在总线处于空闲状态时,发送帧起始信号。
仲裁段
仲裁段(Arbitration Field)主要用于总线仲裁,包含两个核心部分:标识符位(Identifier field-ID)和远程发送请求位(Remote Transfer Request-RTR)。
标识符位:
长度:11位(11bit),范围为ID10 ~ ID0,传输时按照ID10至ID0的顺序进行。
作用:它属于功能性地址,CAN总线的接收节点会通过该标识符,对数据帧进行过滤筛选,判断是否接收该报文。
远程发送请求位RTR:
长度:1位(1 bit)
作用:是区分当前帧的类型。其中显性信号(逻辑0)代表数据帧(Data Frame),隐性信号(逻辑1)代表远程帧(Remote Frame)。
控制段
控制段(Control Field)一共6位,包括IDE位(Identifier Extension)、R0位 、DLC段(Data Length Code)。
IDE位(ID扩展位):
位长:1位
作用:用于区分标准格式与扩展格式。显性电平(逻辑0)代表标准格式,隐性电平(逻辑1)代表扩展格式。
R0位
位长:1位。
作用:保留位,方便以后扩展使用。
DLC段
位长:4位(对应 DLC3、DLC2、DLC1、DLC0 四个独立位)。
作用:用于表示数据段包含的字节数,有效值范围为0~8,是接收方解析数据长度的唯一依据。
编码规则:4 位 DLC 通过不同的显性 / 隐性信号组合,与 0~8 字节数据一一对应,具体编码逻辑如下表所示:
数据段
数据段(Data Field)是数据帧的核心内容,它是节点要发送的原始信息。
长度范围:由 DLC 段决定,包含0 ~ 8 个字节(即 0~64 位),是 CAN 协议中数据承载的唯一区域。
传输顺序:严格遵循 ** 高位先行(MSB first)** 规则,即每个字节的最高位(Bit7)优先发送,最低位(Bit0)最后发送。
边界约束:数据段实际发送的字节数,必须与 DLC 声明的长度一致。
若有效数据不足 DLC 规定长度,剩余字节 / 位需用 隐性电平(逻辑 1) 填充,保证帧结构完整连续。
示例说明
假设 DLC = 2,表示本次数据段长度固定为 2 字节(16 位)。
而当前有效业务数据只有 1 字节 0x12,不足 2 字节。
发送时处理方式:
先发送有效数据:0x12
剩余 1 字节用隐性电平填充,即 0xFF
最终数据段为完整 2 字节:0x12 0xFF
对 CAN 控制器而言,这就是合法的 2 字节数据段;
接收方收到后,由上层应用协议决定是使用全部 2 字节,还是只取前 1 字节为有效数据。
CRC段
CRC段(CRC Field)包含CRC校验码和界定符2个部分。
CRC校验码
长度:CRC校验码长度为15bits。
作用:用于校验传输是否正确。对帧起始、仲裁段、控制段、数据段的所有数据进行循环冗余校验,接收方通过校验结果判断传输过程是否出现位错误。
界定符
长度:1bit。
电平:固定为 隐性电平(逻辑 1)
作用:作为分隔标志,表示CRC校验码区域结束(固定为逻辑1),为后续 ACK 段做准备,保证帧结构边界清晰。
ACK段
ACK段(ACK Field)是应答,包含ACK确认位和界定符。
ACK确认位
长度:1bit隐性位(逻辑 1)。
作用:发送端的ACK确认位默认为隐性位,接收端接收到正确的CRC校验位后,把这ACK确认位的值置为显性位,说明接收端接收到了数据。
界定符
长度:1bit。
电平:固定为 隐性电平(逻辑 1)。
作用:作为分隔标志,与后面的帧结束隔开。
帧结束
帧结束段 EOF(End Of Frame)由发送节点发送的7个隐性位表示结束。
3.4.2.扩展帧结构
扩展帧(Extended Frame)在标准帧基础上同时修改了仲裁段与控制段,其余段结构与标准帧完全一致。
核心差异1:仲裁段
结构:11 位基本 ID + SRR + IDE + 18 位扩展 ID + RTR(共 32 位)
SRR 位:固定为隐性电平(1),仅为占位位,用来替代标准帧中 RTR 所在位置,仅用于总线兼容与仲裁,不参与帧类型判断。
SRR 设为隐性 1,目的是让标准帧在总线竞争时优先级更高,保证向下兼容。
IDE 位:固定为隐性电平(1),标识当前为扩展帧。
18 位扩展 ID:在 11 位基本 ID 后追加,使总 ID 长度达到 29 位,极大扩展了 CAN 总线的寻址空间。
RTR位:唯一用于区分数据帧与远程帧的标志位,与标准帧定义完全一致,不受 SRR 位影响。
核心差异2:控制段
结构:R1 + R0 + DLC
R1:扩展帧新增的保留位,固定为显性电平(0),为协议扩展预留。
R0:保留位,固定为显性电平(0)。
R0、R1 设为显性 0,只是普通保留位,遵循协议默认填充规则,无仲裁竞争目的。
DLC:4 位数据长度码,用于表示数据段字节数,范围 0~8,功能与标准帧一致。
其余段结构
控制段、数据段、CRC 段、ACK 段、帧结束,与标准帧完全相同,保证协议兼容性与可移植性。
3.5.远程帧(Remote Frame)
远程帧相比数据帧没有数据段,其他段一样。
3.6.CAN总线仲裁机制
CAN 总线采用非破坏性位仲裁机制,CAN总线处于空闲状态的时候,最先发送消息的单元(节点)获得发送权。
报文 ID 数值越小,优先级越高。当多个节点同时开始发送报文时,从仲裁段(报文 ID)的第一位起逐位进行仲裁,连续输出显性电平越多的节点越能继续发送;一旦某个节点发送隐性电平而总线上出现显性电平(CAN 总线为线与逻辑,显性电平对应逻辑 0,节点主动驱动总线产生差分电压;隐性电平对应逻辑 1,总线仅为高阻悬浮无差分电压,因此显性电平会覆盖隐性电平),则该节点竞争失败,失去总线占有权并转为接收模式。竞争失败的节点会自动监测总线状态,待总线再次空闲时立即重新尝试发送。
当某个节点发送数据帧的同时,另一节点以远程帧请求数据时,数据帧优先级高于远程帧。
由此可见,报文 ID 与 RTR 位共同参与 CAN 总线仲裁,这也是 RTR 位(用于区分数据帧与远程帧)被设计在仲裁段中的原因。
3.7.CAN的位时序
3.7.1 位时序的定义
CAN 总线的位时序,是对每一位数据的时间长度进行划分和定义的规则,核心是明确位周期、时间分段及采样点位置,为数据可靠采样提供基础。
3.7.2 位时序的核心作用
规定总线上传输的每一位的时间框架(位周期),明确采样点的标准位置,确保收发双方对“何时读取数据”形成共识,是 CAN 总线可靠通信的前提。
3.7.3 位时序的核心构成
CAN 总线的每一个位周期(即单个比特的时间长度),通常分为4个核心时间分段,各分段协同作用,确保采样准确,具体细节如下:
3.7.3.1 基础概念
时间量子(TQ):是 CAN 位时序的最小时间单位,由节点内部时钟分频得到,位周期的总时长 = 各分段时长之和(通常为8~25个 TQ)。
位时间(Bit Time):4个时间分段的总时间,即传输一位数据所需的总时间。
时间量化器:将位时间分为若干等长的时间单元(即 TQ),Sync、Pro-seg、Phase 1、Phase 2 四个分段的时间均为 TQ 的整数倍。
TQ 长度设置:可根据传输速率需求调整,在 STM32 的 CAN 外设中,通过设置波特率分频器的值来确定 TQ 的大小。
每一位周期的时间分段示意图如下:
3.7.3.2 四个核心时间分段详解(结合位同步机制)
四个时间分段并非独立存在,而是与位同步(硬同步、重同步)深度绑定,每一段的设计都直接服务于时钟偏差补偿和采样准确性。
核心逻辑:
通过分段协同,配合硬同步、重同步机制,抵消时钟偏差累积,确保采样点始终稳定在位周期中心区域。
同步段为硬同步提供触发与对齐基础;
相位缓冲段 1/2 为重同步提供动态调整空间;
传播段为两种同步机制扫清延迟干扰。
参考结构图,具体解析如下:
同步段(Sync Segment)
核心作用:检测总线帧SOF位(或数据段)电平跳变,为硬同步(或重同步)提供触发条件,是整个位同步机制的基础,确保节点与总线时序快速对齐。
时间长度:长度固定为1个时间量子(TQ),不可动态调整,是整个位周期中唯一长度固定的分段。
同步检测与触发机制:
跳变沿来源判断:
若跳变沿来自 SOF(帧起始)位:触发硬同步,强制将节点时序重置到同步段起点,完成全局时序对齐,不进入 “失步判定” 流程。
若跳变沿来自 数据段 / 仲裁段等非 SOF 位:进入重同步检测流程。
重同步检测流程:
若跳变沿被包含在同步段范围内:判定节点与总线时序同步,无需调整,维持当前位时序。
若跳变沿落在传播段或相位缓冲段 1 内:判定为时序失步,触发重同步机制,通过调整相位缓冲段 1/2 的长度,将采样点拉回位周期中心。
若跳变沿落在相位缓冲段 2 内:属于异常时序偏差,通常被判定为总线错误,触发相应的错误处理。
传播段(Propagation Segment)
核心作用:补偿总线传输延迟与节点内部电路延迟,为硬同步、重同步提供准确、无延迟的电平信号,避免延迟干扰同步判断。
总线传输延迟:信号在CAN总线上传输过程中产生的时间延迟;
节点内部电路物理延迟:包括发送单元的发送延迟和接收单元的接收延迟。
时间长度:长度至少为1个时间量子(TQ),实际应用中通常配置为1~8个TQ,可根据总线长度、节点数量灵活调整。
相位缓冲段1(Phase Buffer Segment 1)
核心作用:专为重同步设计,可动态调整长度,初步补偿收发端的时钟偏差;当同步段检测到时序失步时,通过调整自身长度实现同步校准。
调整规则:在重新同步阶段,若检测到时序失步,其时间长度可自动加长,带动同步段后移,从而将采样点向位周期中心调整。
时间长度:初始大小可配置为1~8个TQ;重同步时(进入重新同步阶段后),可根据失步程度自动加长(根据失步方向调整),灵活补偿时钟偏差。
相位缓冲段2(Phase Buffer Segment 2)
核心作用:与相位缓冲段 1 配合,共同服务于重同步,进一步精细调整采样点位置,确保采样点始终处于位周期的中心区域——该区域电平最稳定,可最大程度避免采样错误。
调整规则:在重新同步阶段,可根据时序失步(不同步)情况自动缩短自身长度,带动同步段前移,与相位缓冲段1的“加长”调整形成配合,精准校准采样点。
时间长度:初始大小可配置为2~8个TQ;进入重新同步阶段后,可自动缩短(根据失步方向调整),实现采样点的精准校准。
3.7.4.位时序面临的核心问题——时钟偏差
3.7.4.1 时钟偏差的产生原因
CAN 总线无统一时钟线,各节点使用独立内部时钟,收发双方时钟频率天生存在微小差异(即时钟偏差)。
3.7.4.2 时钟偏差的累积效应(对比UART)
UART 场景:帧长仅10位左右,微小时钟偏差来不及累积,采样点仍在有效位范围内,通信可靠。
CAN 场景:一帧数据位数远超10位,偏差持续累积,导致采样点偏离位中心,最终引发采样错误。
3.7.4.3 采样错误的直观体现(附示意图)
如下图所示,是仅靠固定波特率计时、不进行同步调整时,CAN 长帧的采样情况:
图中核心细节:接收端时钟偏快(位宽更短),采样点从第1位开始漂移,前10位(UART帧长)采样正常,第14位时采样点超出位范围,引发采样错误。
3.7.5 位同步(解决时钟偏差)
3.7.5.1 核心目的
动态调整采样时刻,抵消时钟偏差累积带来的采样偏移,确保采样点始终对准位周期中心,保障 CAN 长帧通信可靠,避免因时序偏差导致的采样错误。
3.7.5.2 两种同步方式
CAN 总线通过两种同步方式,结合位时序的分段设计(同步段、相位缓冲段),实现时钟偏差的补偿,具体如下:
硬同步
触发条件:当一个节点检测到数据帧的 SOF 起始位时,触发硬同步操作。
核心作用:快速校准时序基准,将采样点对准电平跳变沿,解决突发的时序偏差,对应同步段的跳变检测功能。
操作过程:执行硬同步时,控制器的时间基准会立即调整,与检测到的电平跳变沿对齐,确保节点内部时间基准与数据帧时间基准一致。
重同步:在检测到总线上的时序与节点使用的时序有相位差时(即总线上的非 SOF 位跳变沿不在节点时序的同步段SS范围内),通过**延长相位缓冲段1(PBS1段)或缩短相位缓冲段2(PBS2段)**来获得同步(即让总线上的非 SOF 位跳变沿重新回到节点时序的同步段SS范围内)。
所有同步机制都是由CAN控制器硬件自动完成。而开发者只需要设置TQ 时间长度和位时序分段TQ数量,就可以确定CAN通讯的波特率。
3.7.6 设置CAN波特率
CAN 波特率
CAN 波特率由通讯节点共同约定:
单个时间量子 TQ 的时间长度
每一位数据包含多少个 TQ
满足以上两点,即可唯一确定 CAN 波特率。
设置方法
TQ 时间长度:根据目标波特率调整时钟分频,在 STM32 CAN 外设中,通过配置 BRR(波特率预分频器)寄存器确定单个时间量子(Time Quantum, TQ)的时长。
位时序分段TQ数量:手动为传播段、相位缓冲段 1、相位缓冲段 2分别分配对应的 TQ 数量(各分段 TQ 数量范围参考 3.7.3.2算),同步段固定是 1 TQ,不需要配置,硬件自动计算。
四个分段的 TQ 数量之和,构成一个完整位周期的总 TQ 数。
计算CAN波特率
假设右图中的1Tq=1us,而每个数据位由20个Tq组成,则传输一位数据需要时间为20us,> 从而每秒可以传输的数据位个数为: 1 *1000000/20= 50000
这个每秒可以传输的数据位的个数就是CAN通讯的波特率,比如上面的波特率就是50kbps。
3.6.CAN的位填充
3.6.1 位填充的核心作用
CAN 总线使用NRZ 非归零编码,如果总线长时间保持同一电平(连续很多个 1 或 0),没有电平跳变沿,接收端就无法触发重同步,收发时钟偏差会持续累积,最终导致采样错误。
位填充与位同步配合,共同保证总线传输时序的正确性:
位同步:利用跳变沿校准位时序,把采样点拉回稳定位置
位填充:主动制造跳变沿,保证重同步能一直有触发条件
3.6.2 发送端填充规则
发送端在发送帧起始到 CRC 序列这段数据时,遵循:
连续出现 5 个相同电平(5 个 1 或 5 个 0),
自动插入 1 个相反电平(5 个 1 后插 0;5 个 0 后插 1)。
插入目的:强制产生跳变沿,让接收端可以触发重同步,避免同步丢失。
3.6.3 接收端对位填充的处理
接收端会完整接收包括填充位在内的所有总线电平,并按以下逻辑处理:
正常采样与同步:
接收端依然按照四个时间分段 + 重同步机制,对每一位进行采样,插入的填充位产生的跳变沿,会正常触发重同步,修正时钟偏移,保证采样点稳定。
检测并删除填充位:
接收端同样统计连续相同电平的位数:
当检测到连续 5 个相同电平 后,紧跟的第 6 位相反电平,识别为位填充位;
接收端自动丢弃该填充位,不将其送入数据缓冲区;
后续位继续按原始数据规则解析。
填充错误检测
若连续 5 个相同电平后,第 6 位仍然相同,判定为位填充错误(Stuff Error);
CAN 控制器会自动识别该错误并进入相应错误处理流程。
一句话概括:填充位只用于同步,不参与实际数据;接收端采样时用它同步,同步完就扔掉,只还原原始有效数据。
4 STM32的CAN外设
4.1 CAN外设(CAN控制器) 介绍
STM32系列芯片集成的CAN控制器主要分为 bxCAN 和 FDCAN 两大类。其中bxCAN广泛应用于F1、F4、L4等系列,而FDCAN则存在于H7、F7、G4等高性能系列中。
4.2 bxCAN(Basic Extended CAN)
4.2.1 协议标准
STM32 芯片集成 bxCAN(Basic Extended CAN) 控制器,支持 CAN 2.0A 与 CAN 2.0B Active 协议标准。:
4.2.2 基本特性
通信速率:最高支持 1 Mbit/s
自动重发:支持
DMA支持:不支持通过DMA进行数据收发
报文兼容:自动完成CAN报文的发送与接收,兼容标准ID与扩展ID报文
4.2.3 发送邮箱
外设包含 3 个发送邮箱,用于缓存待发送报文,最多可同时缓存 3 条报文,发送顺序由发送调度机制决定,支持软件配置发送优先级,并可记录报文发送时间。
4.2.4 接收端
接收端提供 2 个独立接收 FIFO,每个 FIFO 深度为 3 级;配合硬件过滤器,可灵活筛选指定 ID 的报文,只接收需要的数据,减轻 CPU 负担。
接收滤波器(过滤器)
作用:对接到的报文进行过滤。最后放入FIFO 0或FIFO 1。
当总线上报文数据量很大时,总线上的设备会频繁获取报文,占用CPU。过滤器的存在,选择性接收有效报文,减轻系统负担。
有2种过滤模式:
(1)标识符列表模式,它把要接收报文的ID列成一个表,要求报文ID与列表中的某一个标识符完全相同才可以接收,可以理解为白名单管理。
(2)掩码模式(屏蔽位模式),它把可接收报文ID的某几位作为列表,这几位被称为掩码,可以把它理解成关键字搜索,只要掩码(关键字)相同,就符合要求,报文就会被保存到接收FIFO。
如果使能了筛选器,且报文的ID与所有筛选器的配置都不匹配,CAN外设会丢弃该报文,不存入接收FIFO。
每个CAN提供了14个位宽可变的、可配置的过滤器组(13~0)。每个过滤器组x由2个32位寄存器,CAN_FxR1和 CAN_FxR2组成。
FIFO接收接收流程
接收流程如下图所示:
由上图可知,bxCAN 接收遵循先过滤、后入队、再出队的硬件逻辑,完整流程为:
输入方向:CAN 总线报文 → 硬件过滤器(先过滤)
路由逻辑:过滤器匹配成功后,分支流向 FIFO0 或 FIFO1
入队方向:硬件自动写入 Level 0(队尾,最新报文)
出队方向:CPU 从 Level 2(队头,最早报文)读取,遵循 先进先出 (FIFO)
4.3 CAN控制器的3种工作模式
CAN控制器有三种工作模式:初始化模式、正常模式、睡眠模式,模式通过状态位与切换条件实现状态流转,完整状态机如下图所示:
三种工作模式定义:
睡眠模式
状态标识:SLAK = 1(睡眠确认)、INAK = 0(初始化未激活)
核心特性:上电复位后,CAN 控制器默认进入睡眠模式,核心作用是降低系统功耗。此时控制器不参与总线通信,仅维持最低功耗状态 。
唤醒方式:支持软件主动唤醒 或 总线活动唤醒(检测到总线报文时自动唤醒)
初始化模式(INITIALIZATION)
状态标识:SLAK = 0(非睡眠)、INAK = 1(初始化激活)
核心特性:需配置波特率、过滤器、工作模式等关键寄存器时,必须先进入初始化模式。仅在此模式下可修改核心配置参数,正常模式下无法写入。
使用场景:CAN 控制器初始化配置阶段,配置完成后需退出该模式才能通信。
正常模式(NORMAL)
状态标识:SLAK = 0(非睡眠)、INAK = 0(初始化未激活)
核心特性:控制器的实际通信运行状态,可完整参与 CAN 总线同步、仲裁、应答、报文收发等全部协议流程。
状态标识说明
SLAK:睡眠确认标志,1 = 已进入睡眠,0 = 未睡眠。
INAK:初始化确认标志,1 = 已进入初始化模式,0 = 未初始化。
请求与控制位说明
INRQ(初始化请求位):置 1 时请求进入初始化模式,清 0 时请求退出
SLEEP(睡眠请求位):置 1 时请求进入睡眠模式
ACK(确认应答位):硬件对模式切换请求的确认响应
SYNC(总线同步位):控制器与总线完成同步的状态标识
上划线(如 INRQ‾\overline{INRQ}
INRQ
):表示对应位为低电平 / 清 0 状态。
工作流程
上电复位:CAN 控制器默认进入睡眠模式(SLAK=1, INAK=0),处于低功耗待机状态。
切换至初始化模式:软件置 SLEEP=0(清睡眠请求)、INRQ=1(置初始化请求),等待硬件确认位 ACK=1(对应 INAK=1),代表切换成功,进入初始化模式。
寄存器配置:在初始化模式下,完成波特率、过滤器、工作模式等核心参数的配置。
切换至正常模式:配置完成后,软件置 SLEEP=0、INRQ=0,等待总线同步完成(SYNC=1),控制器自动进入正常模式,开始 CAN 总线通信。
4.4 CAN控制器的3种测试模式
CAN控制器有3种测试模式:静默模式、环回模式、环回静默模式。当控制器进入初始化模式的时候才可以配置测试模式。
静默模式可以用于检测总线的数据流量。环回模式可以用于自检(影响总线)。环回静默也是用于自检,不会影响到总线。
5 CAN通讯案例1:环回静默模式测试
5.1 需求描述
使用 环回静默模式(Loopback Silent Mode) 测试 STM32H750 的 FDCAN 能否正常工作。在该模式下:
环回模式:发送的报文不经过外部总线,直接在内部环回至接收端
静默模式:不向外发送显性位(总线上表现为隐性),避免影响外部总线
测试方法:FDCAN 自发送报文,内部环回接收,将接收到的报文数据通过串口打印输出,验证 CAN 通信功能是否正常。
5.2 硬件环境
注意:环回静默模式下,报文不经过外部总线,因此无需外接 CAN 收发器即可进行测试。
5.3 软件设计(基于HAL库)
5.3.1 开发环境
5.3.2 CubeMX 配置步骤
5.3.2.1 时钟配置
系统时钟配置(对应图中标注1)
时钟源选择 PLLCLK,经 D1CPRE Prescaler = /1 分频,配置系统时钟 SYSCLK = 75 MHz,作为CPU主运行时钟。
USART1 时钟配置(对应图中标注2/3)
系统时钟经 D2PPRE2 = /2 分频,得到 PCLK2 = 37.5 MHz(标注2);
在 USART1,6 Clock Mux 中选择 PCLK2 作为USART1时钟源,最终USART1时钟为 37.5 MHz(标注3)。
FDCAN 时钟配置(对应图中标注4/5)
外部25MHz HSE经PLL1倍频:/2 → ×12 → /3,输出 PLL1P = 50 MHz(标注4);
在 HRTIM Clock Mux 中选择 PLL1Q(与PLL1P同源50MHz)作为FDCAN时钟源,最终FDCAN内核时钟为 50 MHz(标注5)。
5.3.2.2 GPIO引脚配置
FDCAN1 引脚配置
在芯片引脚分布图中,PB8 与 PB9 被分配给 FDCAN1 外设。点击引脚后,右侧 GPIO Settings 面板显示如下参数:
引脚信号:
PB8:固定为 FDCAN1_RX (CAN 接收)
PB9:固定为 FDCAN1_TX (CAN 发送)
模式 (Mode):复用推挽输出 (Alternate Function Push Pull)
此模式为 CAN 总线通信标准配置,确保信号驱动能力。
上下拉 (Pull-up/Pull-down):无上下拉 (No Pull)
CAN 总线依赖终端电阻匹配,引脚内部无需上下拉电阻。
最大输出速度 (Maximum Output Speed):高速 (High Speed)
满足 CAN 总线信号边沿速率要求。
复用功能 (Alternate Function):AF9_FDCAN1
截图中,引脚功能下拉菜单明确选择 AF9,将 PB8/PB9 映射至 FDCAN1 控制器。
USART1 引脚配置
PA9 与 PA10 为 USART1 的默认通信引脚,其配置参数如下:
引脚信号:
PA9:固定为 USART1_TX (串口发送)
PA10:固定为 USART1_RX (串口接收)
模式 (Mode):复用推挽输出 (Alternate Function Push Pull)
全双工异步串口的标准 GPIO 模式。
上下拉 (Pull-up/Pull-down):无上下拉 (no pull-up and no pull-down)
最大输出速度 (Maximum Output Speed):高速 (High Speed)
适配 115200 波特率的高速通信需求。
5.3.2.3 FDCAN 参数配置
5.3.2.3.1 同步机制说明
Classic Mode(经典 CAN 模式)
在 Classic Mode 下,整个 CAN 帧(包括数据帧和远程帧)使用 同一套 Nominal 时序 进行同步。
数据帧同步流程
远程帧同步流程
远程帧与数据帧的区别在于 没有数据段,但同步流程相同:
结论:在 Classic Mode 下,数据帧和远程帧使用完全相同的同步机制,全程基于 Nominal 时序进行硬同步(SOF)和重同步(后续所有段)。
FD Mode(CAN FD 模式)
在 FD Mode 下,CAN FD 帧支持 BRS(比特率切换),仲裁段和数据段使用 两套独立的时序参数,同步机制也相应分为两段。
数据帧同步流程(BRS = 1):
远程帧同步流程(FD Mode):
重要:CAN FD 协议规定,远程帧不能使用 BRS(比特率切换),即远程帧的 BRS 位必须为 0。
因此,FD Mode 下的远程帧同步流程与 Classic Mode 完全相同:
结论:在 FD Mode 下,数据帧(BRS=1)使用 Nominal + Data 两段式同步,远程帧全程使用 Nominal 同步。
两种模式核心差异
5.3.2.3.2 FDCAN 参数配置过程(以Classic Mode模式为例)
经典 CAN 模式下只使用 Nominal 相关参数,Data Prescaler、Data Sync Jump Width、Data Time Seg1、Data Time Seg2 均为 CAN FD 专用,无需配置。
波特率计算:
CAN 时钟:50 MHz
时间份额 tq = 1 / (50 MHz / 50) = 1000 ns
总位长度 = 1 + 3 + 6 = 10 tq
实际比特率 = 1 × 1000000000 / (10 × 1000 ns) = 100 kbps
5.3.2.3 串口参数配置
上图就是CubeMX 的串口1的默认配置,由图可知,默认配置如下表:
5.3.3 基于HAL库代码
5.3.3.1 fdcan.h
在fdcan.h中添加如下代码。
/* USER CODE BEGIN Prototypes */
typedef struct
{
uint16_t stdId;
uint8_t data[8];
uint8_t length;
}RxDataStruct;
void FDCAN_ConfigFilter(void);
void FDCAN_SendMsg(uint16_t stdId, uint8_t *pTxData,uint8_t length);
void FDCAN_ReceiveMsg(RxDataStruct rxDataStruct[],uint8_t *msgCount);
/* USER CODE END Prototypes */
5.3.3.2 fdcan.c
在fdcan.c中添加如下代码。
/* USER CODE BEGIN 1 */
void FDCAN_ConfigFilter(void)
{
FDCAN_FilterTypeDef sFilterConfig;
/* 1.FDCAN ID Type :Standard ID(11 bit ID)*/
sFilterConfig.IdType = FDCAN_STANDARD_ID;
/* 2.Specifies the filter which will be initialized.*/
sFilterConfig.FilterIndex = 0; // 0 and 127
/* 3.Specifies the filter type:FDCAN_FILTER_MASK */
sFilterConfig.FilterType = FDCAN_FILTER_MASK;
/* 4.Route received frames to Rx FIFO0*/
sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
/* 5. Accept all IDs */
sFilterConfig.FilterID1 = 0x000; // Filter ID
sFilterConfig.FilterID2 = 0x000; // Mask
/* 6.Apply filter configuration */
if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief
* @param stdId 0~0x7FF
* @param pTxData 发送的数据
* @param length 发送数据字节长度
*/
void FDCAN_SendMsg(uint16_t stdId, uint8_t *pTxData,uint8_t length)
{
FDCAN_TxHeaderTypeDef pTxHeader;
/* 1.Check if Tx FIFO is ready */
while (HAL_FDCAN_GetTxFifoFreeLevel(&hfdcan1) == 0);
/* 2. Specifies the identifier.*/
pTxHeader.Identifier = stdId;
/* 3.Specifies the identifier type for the message that will be transmitted.*/
pTxHeader.IdType = FDCAN_STANDARD_ID;
/* 4.Specifies the frame type of the message that will be transmitted.*/
pTxHeader.TxFrameType = FDCAN_DATA_FRAME;
/* 5.Specifies the length of the frame that will be transmitted.*/
pTxHeader.DataLength = FDCAN_DLC_BYTES_64; // most is 64bits
switch(length)
{
case 0: pTxHeader.DataLength = FDCAN_DLC_BYTES_0; break;
case 1: pTxHeader.DataLength = FDCAN_DLC_BYTES_1; break;
case 2: pTxHeader.DataLength = FDCAN_DLC_BYTES_2; break;
case 3: pTxHeader.DataLength = FDCAN_DLC_BYTES_3; break;
case 4: pTxHeader.DataLength = FDCAN_DLC_BYTES_4; break;
case 5: pTxHeader.DataLength = FDCAN_DLC_BYTES_5; break;
case 6: pTxHeader.DataLength = FDCAN_DLC_BYTES_6; break;
case 7: pTxHeader.DataLength = FDCAN_DLC_BYTES_7; break;
case 8: pTxHeader.DataLength = FDCAN_DLC_BYTES_8; break;
default: pTxHeader.DataLength = FDCAN_DLC_BYTES_8; break;
}
/* 6.Disable Bit Rate Switch before setting Classic CAN format */
pTxHeader.BitRateSwitch = FDCAN_BRS_OFF;
/* 7.Use Classic CAN format */
pTxHeader.FDFormat = FDCAN_CLASSIC_CAN;
/* 8.No Tx Event FIFO */
pTxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
/* 9.No message marker */
pTxHeader.MessageMarker = 0;
/* 10.Apply tx configuration */
HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1,&pTxHeader,pTxData);
}
void FDCAN_ReceiveMsg(RxDataStruct rxDataStruct[],uint8_t *msgCount)
{
uint8_t i;
FDCAN_RxHeaderTypeDef rxHeader;
/* 1.Get the number of messages available in Rx FIFO0 */
*msgCount = HAL_FDCAN_GetRxFifoFillLevel(&hfdcan1, FDCAN_RX_FIFO0);
/* 2. Retrieve all messages from Rx FIFO0 */
for (i = 0; i < *msgCount; i++)
{
HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO0, &rxHeader, rxDataStruct.data);
rxDataStruct.stdId = rxHeader.Identifier;
rxDataStruct.length = rxHeader.DataLength;
}
}
/* USER CODE END 1 */
5.3.3.3 main.c
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MPU Configuration--------------------------------------------------------*/
MPU_Config();
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_FDCAN1_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* 1. Configure filters */
FDCAN_ConfigFilter();
/* 2. Start CAN communication */
HAL_FDCAN_Start(&hfdcan1);
printf("CAN Silent Loopback Test Start\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
uint16_t stdId = 0x011;
uint8_t *tData;
RxDataStruct rxDataStruct[8];
uint8_t rxMsgCount;
uint8_t i;
while (1)
{
// printf("1111\r\n");
/* 1. send data */
tData = "abcdefg";
FDCAN_SendMsg(stdId,tData,strlen((char *)tData));
printf("Send Finish\r\n");
tData = "123";
FDCAN_SendMsg(stdId,tData,strlen((char *)tData));
printf("Send Finish\r\n");
/* 2. receive data */
FDCAN_ReceiveMsg(rxDataStruct, &rxMsgCount);
printf("Receive Finish rxMsgCount = %d...\r\n", rxMsgCount);
for (i = 0; i < rxMsgCount; i++)
{
RxDataStruct msg = rxDataStruct;
printf("stdId = %d, length = %d, msgData = %s\r\n", msg.stdId, msg.length, msg.data);
}
HAL_Delay(100);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
5.3.4 打印测试结果
Send Finish
Send Finish
Receive Finish rxMsgCount = 2...
stdId = 17, length = 5, msgData = abcdefg
stdId = 17, length = 3, msgData = 123
————————————————
版权声明:本文为CSDN博主「心语星愿11」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_26942353/article/details/159468119
|
|