Cortex-M3-STM32F1 开发:(五十四)CAN(车企会用),难但很重要

上一篇 下一篇

目 录


CAN(车企会用)

CAN in Automation(CiA) 官网。

CAN 总线结构其实和 USB 很类似,内部都是 TX 和 RX ,通过对应的芯片电路将传输信号改成差分信号,也有点类似 RS485 。但是 CAN 总线的通信方式又和 IIC 类似。

CAN 总线通常用于 MCU 级的通信。

1)简介

CAN(Controller Area Network),是 ISO 国际标准化的 串行通信协议,使用差分信号传输,半双工 。制定目的是:为了满足汽车产业的"减少线束的数量"、"通过多个 LAN ,进行大量数据的高速通信"的需求。CAN 的发展史如下:

  • 低速 CAN(ISO11519)通信速率 10~125Kbps ,总线长度可达 1000 米

  • 高速 CAN (ISO11898)通信速率 125Kbps~1Mbps ,总线长度 ≤40 米【也被叫做 经典 CAN

  • CAN FD 通信速率可达 5Mbps ,并且兼容经典 CAN ,遵循 ISO11898-1 做数据收发

1.1)CAN 总线拓扑图

CAN 总线由两根线(CANL 和 CANH)组成,允许挂载多个设备节点,每个节点都有自己的 CAN 控制器(通常是节点自己的 MCU)和 CAN 收发器,都有自己的时钟源(低速 CAN:能挂20个,高速 CAN:能挂30个)。但 CAN 总线是半双工,同一时间只有一个节点发送数据。

通讯流程大概是(结合下面物理层的说明再看): MCU 通过控制器连接 CAN 收发器,从 TX 线告诉 CAN 收发器要发高电平还是低电平,然后 CAN 收发器将其转换成差分信号发送到 CAN 总线上,接收的话是 CAN 收发器 读取 CAN 总线上的差分信号,然后从 RX 线告诉 CAN 控制器收到的是高电平还是低电平。

P.S.: CAN 设计最厉害的地方之一就是:没有公共时钟、每个节点时钟都有误差,却依然可以稳定同步通信。

低速和高速 CAN 的结构中最后的黑色电阻是:终端电阻,用于阻抗匹配,以减少回波反射,增强稳定性。结构差异和芯片电路差异决定了两者的功能差异。

1.2)特点

  • 多主控制:每个设备都可以主动发送数据
  • 系统的柔软性:没有类似地址的信息,添加设备不改变原来总线的状态,但是有ID
  • 通信速度:速度快,距离远
  • 具有错误检测&错误通知&错误恢复功能
  • 故障封闭:可以判断故障类型,并且进行隔离
  • 连接节点多:用户可根据速度与数量找到平衡

CAN 总线协议已广泛应用在汽车电子、工业自动化、船舶、医疗设备、工业设备等方面。

2)物理层

CAN 使用差分信号进行数据传输,根据 CAN_H 和 CAN_L 上的电位差来判断总线电平(类似 USB )。

总线电平分为 显性电平(dominant,D,逻辑0隐性电平(recessive,R,逻辑1 ,二者必居其一,显性电平(0)具有优先权,相当于线与逻辑(0&1/0=0,1&1=1) (当多个节点同时向总线发送数据时,就会发生"位竞争",无论有多少个节点在发送隐性电平,只要有一个节点发送显性电平,总线上的物理电平就是显性电平0) 。

发送方通过使总线电平发生变化,将消息发送给接收方(类似 IIC )。

高速 CAN 和低速 CAN 的电平逻辑如下:

电平 高速 CAN 低速 CAN 总线表现
显性电平(0) U C A N _ H -- U C A N _ L = 2 V U_{CAN\H}~--~U{CAN\_L}= 2V UCAN_H -- UCAN_L=2V U C A N _ H -- U C A N _ L = 3 V U_{CAN\H}~--~U{CAN\_L}= 3V UCAN_H -- UCAN_L=3V 拉高 CAN_H,拉低 CAN_L,程度不一样
隐性电平(1) U C A N _ H -- U C A N _ L = 0 V U_{CAN\H}~--~U{CAN\_L}= 0V UCAN_H -- UCAN_L=0V U C A N _ H -- U C A N _ L = − 1.5 V U_{CAN\H}~--~U{CAN\_L}= -1.5V UCAN_H -- UCAN_L=−1.5V 拉低 CAN_H,拉高 CAN_L,程度不一样

常用的 CAN 收发器芯片有:TJA1050、TJA1042、SIT1050T,SIT1050T 支持高速 CAN ,传输速率可达 1Mbps ,正点原子就用的这个。

正点原子的 STM32F103ZET6 战舰板 上的 CAN 收发器芯片如下:

引脚信息如下:

  • D:CAN 发送引脚
  • R:CAN 接收引脚
  • Vref:参考电压输出,默认悬空
  • CANL:低电位 CAN_L 电压输入输出端
  • CANH:高电位 CAN_H 电压输入输出端
  • RS:高速 / 静音模式选择(低电平为高速)

这个匹配电阻看实际场景,串联的 CAN 设备,开头和结尾的设备加匹配电阻,中间的不要加。

3)协议层

CAN 总线依赖 分布式时钟 + 边沿驱动同步 机制,每个节点都需要有自己的时钟源。

CAN 总线以"帧"形式进行通信。CAN 协议定义了 5 种类型的帧:数据帧、遥控帧、错误帧、过载帧、间隔帧,其中数据帧最为常用。

这 5 种帧的描述如下表:

帧类型 帧作用
数据帧 (Data Frame) 用于发送单元向接收单元传输数据的帧
遥控帧 (Remote Frame) 用于接收单元向具有相同 ID 的发送单元请求数据的帧
错误帧(Error Frame) 用于当检测出错误时,向其他单元通知错误的帧
过载帧(Overload Frame) 用于接收单元通知其他帧:自己尚未做好接收准备的帧
间隔帧(Inter Frame Space) 用于将数据帧、遥控帧与前面的帧分离开来的帧

(ID下面解释)

3.1)数据帧

也叫做报文,后面都称呼为报文

数据帧由 7 段组成。数据帧又分为标准帧(CAN2.0A协议)和扩展帧(CAN2.0B协议),扩展帧是在标准帧的基础上增加了更多的标识符位,以支持更大的地址空间。主要体现在仲裁段和控制段,帧结构如下(图中的数字表示位数):

① 各个段的作用及内部构成如下(标准帧):

  • 帧起始: 表示数据帧开始的段,显性信号
    • SOF --- 1位 (Start of Frame):是一个 显性位(必须为逻辑0),这个显性电平(逻辑 0)是帧起始的"有效触发信号" ------ 所有节点通过检测总线从 隐性(空闲态,逻辑 1)→ 显性(逻辑 0)的下降沿跳变 来识别帧的开始并同步时序。
  • 仲裁段: 表示该帧优先级的段
    • ID --- 11位 (Identifier):这是报文的优先级标识。在仲裁段,ID 值越小,优先级越高,用于多节点间的仲裁,可以当地址用,但不是地址。
    • RTR --- 1位 (Remote Transmission Request):远程传输请求位
      • 0:表示这是一个数据帧(Data Frame),后面跟着数据段。
      • 1:表示这是一个遥控帧(Remote Frame),它不携带数据(没有数据段),而是请求具有相同 ID 的节点发送数据。
  • 控制段: 表示数据的字节数及保留位的段
    • IDE --- 1位 (Identifier Extension):标识符扩展位,用于决定此帧是标准数据帧还是扩展数据帧
      • 0:表示这是一个标准帧 (Standard Frame),使用 11 位 ID(范围是 0~0x7FF)。
      • 1:表示这是一个扩展帧 (Extended Frame),使用 29 位 ID(11位 + 18位,这 29 位整体叫做扩展 ID ,范围是 0~0x1FFF FFFF)。
    • RO --- 1位 (Reserved Bit):保留位,必须为隐性(逻辑1)。未来可能用于新功能。
    • DLC --- 4位(Data Length Code):数据长度码,指明数据段中包含的数据字节数。DLC 十进制取值范围是 0~8(0000~1000),表示数据段的数据长度可以是 0~8 个字节(0~64位)。例如 DLC=5 表示有 5 个字节的数据。
  • 数据段: 数据的内容,一帧可发送 0~8 字节数据
    • Data Field --- 0~64位:实际要传输的数据内容。长度由 DLC 决定。
  • CRC段: 检查帧的传输错误的段
    • CRC --- 15位(Cyclic Redundancy Check):循环冗余校验码,用于检测帧中从 SOF 到 CRC 段之前的所有位是否发生错误。接收方会重新计算 CRC 并与接收到的 CRC 进行比较。
    • DEL --- 1位 (Delimiter):分隔符,一个隐性位(逻辑 1 ),用于分隔 CRC 段和 ACK 段。
  • ACK段: 表示确认正常接收的段
    • ACK --- 1位(Acknowledgment):应答位,发送方在 ACK 段的时序内发送一个隐性位(逻辑1)后,正确接收并校验无误的接收方会在该位上发送一个显性位(逻辑0)作为应答(ACK),表示"我收到了"。
    • DEL --- 1位 (Delimiter):分隔符,一个隐性位(逻辑 1 ),用于分隔 ACK 段和 EOF 段。
  • 帧结束: 表示数据帧结束的段,7个隐性信号
    • EOF --- 7位(End of Frame):由 7 个连续的隐性位组成(1111111),标志着一个 CAN 帧的结束。

② 扩展帧的 仲裁段和控制段:

  • ......同标准帧......

  • 仲裁段: 表示该帧优先级的段

    • ID --- 11位(Identifier):......同标准帧......
    • SRR --- 1位 (Substitute Remote Request):替代远程请求位,始终为隐性位(逻辑1),它的存在是为了保持帧结构的对齐和一致性。
    • IDE --- 1位 (Identifier Extension):标识符扩展位,在扩展帧中,此位为显性位(逻辑0),明确指示这是一个扩展帧。
    • Extended Identifier --- 18位:扩展标识符,这是 29 位 ID 中的低 18 位,与前面的 11 位 ID 共同构成完整的 29 位标识符。
    • RTR --- 1位(Remote Transmission Request):......同标准帧......
  • 控制段: 表示数据的字节数及保留位的段

    • RO --- 1位(Reserved Bit):......同标准帧......
    • RO --- 1位(Reserved Bit):......同标准帧......两个 RO 位,这是为了保持与标准帧的控制段长度一致,方便硬件设计。
    • DLC --- 4位(Data Length Code):......同标准帧......
  • ......同标准帧......

③ 有关应答过程的描述(ACK 位的变化):

CAN 总线没有独立的回复帧,回复是在发送的那个数据帧中的 ACK 段的时序内完成的。正确机制( CAN 协议规定)如下:

  1. 发送方行为

    在 ACK 位时段内,发送方主动将总线驱动为隐性电平(逻辑1) ,但同时持续监听总线实际电平(发送与监听同步进行)。

  2. 接收方行为

    所有正确接收且 CRC 校验无误的节点,在同一时刻( ACK 位时段)将总线强制驱动为显性电平(逻辑0)

  3. 总线结果

    由于显性电平(0)具有物理层优先权,只要 至少有一个 接收节点发送显性位,总线电平即被"覆盖"为显性(0)。

  4. 发送方判断

    • 若监听到 ACK 位时段内总线为 显性(0) → 应答成功(至少一个节点正确接收)
    • 若监听到 ACK 位时段内总线仍为 隐性(1) → 应答失败(无节点应答,触发错误处理)

3.2)位时序

CAN 总线以 "位同步" 机制实现对电平的正确采样(和其他通信协议很不同)。

先简单说一下:

CAN 总线上有节点发出帧起始信号了,其余节点会自动对齐,利用自身的时钟源建立时序,并对总线电平进行采样,后续过程中会不断调整自身时序,来对齐 CAN 总线上的数据位信号(位同步),从而采样到可靠的位电平(总线只管传输信号,接收节点要自己对齐数据位去采样)。

详细解释如下:

CAN 总线上时序信号不是一直存在的,没有节点发数据时,就处于空闲状态,此时总线电平是隐性电平(逻辑 1 ),此时所有节点都在监听。当某个节点要发送数据帧(或其他功能帧)时,会先发出 SOF 信号(帧起始位),总线会从隐性(逻辑1)变成显性(逻辑0),此时其余节点检测到总线从隐性→显性跳变时,就会依赖自身(本地)的时钟源,立即重置自身(本地)时序,将当前时刻定义为 新位周期的 SS 段起点(无论之前计时到哪),那么这些节点就做到了 "听到预备的口令,自行占到了同一起跑线上" ......

(一脸懵的话,跳转到 "3.3.1)位时序和同步的总结",先大概了解以下整个过程)

...... 而这个位周期、SS 段的解释如下 ......

CAN 控制器(发送方/接收方)将 每个位的时间周期 划分为四段:同步段(SS)传播时间段(PTS)相位缓冲段1(PBS1)相位缓冲段2(PBS2) ,每段又由多个位时序 Tq 组成,用于同步、补偿延迟、确定采样点(解释:物理层上,每个数据位会以连续、完整的电平持续整个位时间,这个完整的位时间被划分为了四段)。

图解如下:

四个时间段的描述如下:

作用 关键说明
SS(Sync Segment) 检测位边沿,用于同步 理想边沿应落在 SS 内;若偏移,触发重同步
PTS(Propagation Segment) 补偿物理延迟(线缆传播+节点延迟),用于 "等待" 信号稳定 长度需 ≥ 最大往返延迟,总线长度越长,此值应越大
PBS1(Phase Buffer Segment 1) 采样点前的缓冲区 采样点通常位于 PBS1 结束处(如位时间75%~87.5%)
PBS2(Phase Buffer Segment 2) 采样点后的缓冲区 + 重同步调整区 重同步时可伸缩(缩短/延长)
  • Tq (Time Quantum) :时间量子,是位定时的最小单位(由系统时钟经预分频得到)。
    • 电平跳变时间显著短于 1 个 Tq 。
  • 位时间 = SS + PTS + PBS1 + PBS2(单位:Tq)。例如:1 + 3 + 5 + 4 = 13 Tq,根据位时序,可以计算 CAN 通信的波特率。。
  • 采样 :在 PBS1 结束时刻(即 SS+PTS+PBS1 处),控制器 单次采样 总线电平,作为该位的逻辑值。

3.3)位时序的同步

只看 隐性 → 显性 跳变边沿

节点监测到总线上信号的跳变在 SS 段范围内时,则表示节点与总线的时序是同步,此时采样点的电平可代表该位的电平(就是说:采样点的值要有效,跳变电平就必须位于 SS 段内)。但是由于每个节点自身的时钟频率误差、传输上的相位延迟,都会引起偏差。

CAN 为了实现对总线电平信号的正确采样,数据同步分为硬件同步和再同步。

① 硬件同步(用于帧起始位)

所谓硬件同步,就是在 3.2)位时序中 "详细解释如下" 说的,详细解释如下:

当 CAN 总线处于空闲时,每个节点的位时序是一直在循环运行的(比如说 SS(1Tq) → PTS(2Tq) → PBS1(3Tq) → PBS2(3Tq) → SS(1Tq) → ......),此时 CAN 总线上有个发送节点发送了帧起始信号(隐性 → 显性的跳变),此时:

  • 当接收节点检测到边沿并不处于它的位时序的 SS 段内 ,那么它会立即:清零当前位时间计数器,把这个边沿 "定义为新的 SS 段起点",这就叫硬件同步(硬同步)

  • 当接收节点检测到边沿恰好正处在它的位时序的 SS 段内,也会执行硬件同步,只是效果几乎不会变化。

总结就是: 当节点在空闲状态检测到 隐性 → 显性 跳变边沿(SOF帧起始信号)时,无论当前位时间处于哪个段,都会立即将该边沿定义为新的 SS 段起点,从而重新对齐位时间。

② 再同步(用于普通数据位)

也叫重同步

当帧起始位和接收节点位时序的 SS 段对齐之后,该节点就会持续循环 四段 位时序,并始终检查后续 每一个 隐性 → 显性 边沿 是否处于对应位周期的 SS 段内。即使接收节点对齐了发送节点的帧起始位,但是节点之间时钟频率的差异,仍会导致后续普通数据位的错位,因此有了 "再同步"。

再同步是利用普通数据位的边沿信号进行同步。情况分为两种:超前滞后(边沿信号相对于 SS 段,即边沿信号超前或滞后)。

  • 超前: 边沿信号位于接收节点的 PBS2 时段内

    • 描述: 边沿信号在 PBS2 时段内,也就是在下一个位时序的 SS 段前面不远处。可以理解为:上一个边沿信号过去之后,位时序还没来得及跑到新的 SS 段,新的边沿信号就来了(边沿信号有点快了,总是超前)。

    • 同步调整方式: 接收节点会减少当前位周期中 PBS2 时段的长度,超前多少就减少多少,比如说距离下一个 SS 段还有 2Tq,那就将 PBS2 减少 2Tq 。

    • 举例:

  • 滞后: 边沿信号位于接收节点的 PTS+PBS1 时间段内

    • 描述: 边沿信号在 PTS+PBS1 时段内,也就是在当前位时序的 SS 段后面不远处。可以理解为:上一个边沿信号过去之后,位时序已经跑到新的 SS 段后面一点了,新的边沿信号才来(边沿信号有点慢了,总是滞后)。

    • 同步调整方式: 接收节点会增加当前位周期中 PBS1 时段的长度,滞后多少就增加多少,比如说距离本周期的 SS 段已经过去 2Tq,那就将 PBS1 增加 2Tq 。

    • 举例:

(这个超前和滞后是有规定的,可以有多种理解,但是一般都按照上述理解来遵循)

同步调整中,增加或减少的长度(Tq个数)的最大值被叫做 SJW(再同步补偿宽度,单位是 Tq)其范围一般是为:1~4 Tq 。限定了 SJW 的最大值后,再同步时,就不能增加或减少超过 SJW 最大值的长度,当误差大于 SJW 最大值时,就会产生错误。

SJW 值较大时,吸收误差能力更强,但是通讯速度会下降。

③ 位时序同步的总结

CAN 位同步的本质是------每个节点以总线边沿为参考,动态校准自身位时序 。硬同步实现帧级对齐,再同步实现位级微调,共同保障在噪声/时钟漂移环境下,所有节点能在精确的采样点读取总线电平。这是CAN高可靠性的物理层基石。

同步机制全流程(以接收节点视角):

  1. 空闲监测

    • 节点持续检测总线是否连续 7 个隐性位(EOF 后空闲)。
    • 各节点的位时序发生器处于"待机"状态,自由推进四段计时
  2. 帧起始(SOF)→ 硬同步

    • 检测到隐性→显性跳变(SOF 下降沿)。
    • 立即重置本地时序 :将当前时刻定义为 新位周期的 SS 段起点(无论之前计时到哪)。
    • 效果:所有节点在 SOF 边沿实现"起跑线对齐"。
  3. 后续每位 → 重同步(动态微调)

    • 节点按预设四段推进时序(例如:SS=1Tq,PTS=2Tq,PBS1=6Tq,PBS2=4Tq)。

    • 在 SS 段预期窗口内监测边沿:

      • ✅ 边沿落在 SS 内 → 无需调整,按原定时推进。
  • ⚠️ 边沿提前(早于 SS 起点)→ 缩短 PBS1(下一位总时间减 n Tq)。

    • ⚠️ 边沿滞后(晚于 SS 终点)→ 延长 PBS1(下一位总时间加 n Tq)。
  • 调整量受 "再同步跳转宽度 SJW " 限制(通常≤4 Tq),避免过度抖动。

  • 采样点固定:在 PBS1 结束时刻(一般是位时间 75% 处)单次采样电平。

  1. 帧结束 → 回归空闲监测

    • 检测到 7 个隐性位(EOF)后,停止四段循环,返回步骤 1 。

3.4)CAN 总线仲裁

CAN 总线处于空闲状态时,最先开始发送消息的单元(节点)获得发送权。

多个单元(节点)同时开始发送时,从仲裁段(报文ID)的第一位开始进行仲裁。连续输出显性电平最多的单元可继续发送(ID越小的,可优先发送) ,即 首先出现隐性电平的单元失去对总线的占有权变为接收

比如有两个单元(节点)同时发送:

竞争失败的单元(节点),在转为接收状态工作之后,会自动检测总线空闲,在第一时间再次尝试发送。

4)控制器(只介绍 STM32 搭载的)

CAN 总线上的每个节点都有独立的控制器,可控制发送与接收

4.1)控制器介绍

STM32 F1/F4/F7 用的 CAN 控制器是 bxCAN ,支持 CAN 2.0A 和 CAN 2.0B Active 版本协议( H7 后来用的是 FDCAN )。

(bxCAN 是 ST 公司对这种控制器的命名)

CAN 2.0A 只能处理标准数据帧,扩展帧的内容会识别错误,而 CAN 2.0B Active 可以处理标准数据帧和扩展数据帧。另一种协议 CAN 2.0B Passive 只能处理标准数据帧,扩展帧的内容会忽略。所以一般都用 CAN 2.0B Active 。

bxCAN 主要特点:

  • 波特率最高可达 1M bps(取决于具体型号和时钟配置)
  • 支持时间触发通信(CAN 的硬件内部定时器可以在 TX/RX 的帧起始位的采样点位置生成时间戳)
    • bxCAN 内部有一个 16 位自由运行的定时器(称为 CAN Time Stamp Timer),它会在每次发送或接收到一帧 CAN 报文的起始位(SOF, Start of Frame)被采样的时刻,自动记录当前定时器的值。
  • 具有 3 级并行发送邮箱
    • bxCAN 内部有 3 个独立的发送缓冲区,每个叫一个 "Mailbox"(邮箱)。
    • 可以同时把最多 3 帧不同的 CAN 报文写入这 3 个邮箱,硬件会根据 报文 ID 的优先级(CAN 的仲裁机制)自动决定哪个先发,发送完成后,会产生中断或状态标志。3 个邮箱是并行的,优先级由 ID 决定,不是先进先出。
  • 具有 3 级深度的 2 个接收 FIFO
    • 接收 FIFO 是 CAN 控制器中用于临时存储接收到的 CAN 报文的一种硬件缓冲结构。先收到的报文,先被读取;后收到的,排在后面
    • bxCAN 有两个独立的接收队列(FIFO0 和 FIFO1),每个 FIFO 最多能缓存 3 帧接收到的报文,那么总共就是 6 个报文。
    • 接收到的报文根据过滤器配置,被自动分配到 FIFO0 或 FIFO1,如果某个 FIFO 已满(3 帧未读),再有新帧进来且匹配该 FIFO,就会发生 溢出错误(Overrun),新帧会被丢弃。
  • 可变的过滤器组 (最多 28 个,也叫筛选器)( F103 只有 14 个)
    • CAN 总线是广播式的,所有节点都能收到所有报文。为了只处理自己关心的消息,bxCAN 提供了 硬件过滤器(Filter Bank)。

4.2)控制器模式

总共有工作模式、测试模式、调试模式,调试模式用的很少,不展开说

① 工作模式

CAN 控制器的工作模式有三种:初始化模式正常模式睡眠模式

其中英文单词是寄存器的一些标志位。

有关睡眠模式和睡眠唤醒的函数配置后面也暂时不讲。

② 测试模式

CAN 控制器的测试模式有三种:静默模式环回模式环回静默模式 。测试模式是在初始化模式下进行配置的。

这些模式通过配置 CAN 控制器的 测试寄存器 来启用,不会影响总线上的其他节点 (尤其在静默类模式下),非常适合在不干扰实际通信的情况下进行软件验证或硬件诊断

  • 正常模式:节点正常工作的模式

    • 描述:可向总线发送或接收数据

    • 图解:

  • 静默模式:可用于节点监听统计总线的流量

    • 描述:只能向总线发送隐性电平 1 不能发送显性电平 0 ,可从总线接收数据,相当于"只听不说"。用于:

      • 在线监听/总线分析:可监控总线流量,而不会参与仲裁或影响通信。
      • 故障诊断:检测本节点是否因错误帧干扰总线。
    • 图解:

  • 环回模式:用于节点自检,但会影响总线

    • 描述:发送的数据直接传回到输入(总线可监测数据),不能从总线接收数据。用于:

      • 软件自检:验证 CAN 驱动、发送/接收逻辑、中断处理是否正常。
      • 协议栈测试:在没有外部 CAN 设备的情况下测试应用层逻辑。
      • 开发初期验证:无需连接真实 CAN 网络即可调试代码。
    • 图解:

  • 环回静默模式:用于节点自检,不会影响总线

    • 描述:发送的数据直接传回到输入(总线不可监测到数据),不能从总线接收数据。用于:

      • 最安全的自检模式:既能测试本机收发逻辑,又绝对保证不干扰外部CAN总线。
      • 适用于在线系统中进行后台自检(例如汽车 ECU 在运行中做健康检查)。
    • 图解:

总结如下:

模式 是否驱动总线 是否接收外部报文 发送数据去向 典型用途
正常模式 ✅ 是 ✅ 是 发送到总线 正常通信
静默模式 ❌ 否(不发显性位) ✅ 是 不发送(或仅隐性) 总线监听、嗅探
环回模式 ❌ 否(内部环回) ❌ 否(忽略总线) 内部接收FIFO 软件自检、离线测试
环回静默模式 ❌ 否 ❌ 否 内部接收FIFO 安全自检、在线诊断

4.3)控制器框图

bxCAN 控制器的结构框图主要包含四个部分:

  • CAN 内核:
    • 包含各种控制/状态/配置寄存器,可以配置模式、波特率等
  • 发送邮箱:
    • 用来缓存待发送的报文,最多可以缓存 3 个发送报文
  • 接收FIFO:
    • 缓存接收到的有效报文,最多可以缓存 6 个接收报文
  • 接收过滤器:
    • 筛选有效报文,STM32F103 系列的只有 14 组过滤器

图解如下:

F1 非互联型产品的 CAN 控制器框图如下:

4.4)发送处理

发送报文过程的图解如下:

一个完整发送周期的具体流程如下(先看看流程,寄存器位啥的先不用记住):

  1. 选择空置邮箱

    • 含义:在发送前,软件必须先选择一个当前处于"空置状态"的邮箱。

    • 如何判断?

      通过读取 CAN_TSR 寄存器中的 TME(Transmission Mailbox Empty)位来判断:

      • TME[i] = 1 → 邮箱 i 空闲,可写入
      • TME[i] = 0 → 邮箱 i 正在使用或已挂起
  2. 设置 ID/DLC/DATA → 使邮箱退出空置状态

    • 操作:软件向选中的邮箱写入:

      • TIDr:标识符(ID)
      • TDTR:数据长度码(DLC)和时间戳
      • TDLr / TDHR:数据内容(8 字节)
    • 结果 :该邮箱从"空置"变为"非空",即退出空置状态

    • 硬件行为:CAN 控制器检测到邮箱被填满后,会自动将其加入"待发送列表"。

  3. 请求数据发送 → 邮箱会进入挂号状态

    • 操作 :软件向 CAN_TSR 寄存器的 TXRQ 位写 1,表示"请求发送"。

    • 结果 :该邮箱进入"挂号状态"(Pending Transmission)。

    • 说明:"挂号"意味着它已经准备好发送,但还没有开始。

    • 此时邮箱不会立即发送,而是等待总线空闲且它成为最高优先级的候选者。

  4. 邮箱进入预定发送状态

    • 条件:当以下两个条件同时满足时,邮箱进入"预定发送状态":

      1. 总线进入空闲状态(即没有其他节点正在发送)

      2. 该邮箱中的报文 ID 是当前所有"挂号"邮箱中优先级最高的

        发送优先级由邮箱中报文的 ID 决定。ID 数值越低,优先级越高。如果 ID 值相同,邮箱编号小的先被发送(类似DMA,邮箱编号:0,1,2)。

    • 结果:该邮箱被调度为下一个发送者,进入"预定发送状态"。

  5. 发送状态

    • 动作 :控制器开始将该邮箱中的报文发送到 CAN 总线上。

      • 过程:
        • 发送 SOF(帧起始)
        • 发送仲裁段(ID)
        • 发送控制段(DLC)
        • 发送数据段
        • 发送 CRC 和 ACK 等
    • 状态标志CAN_TSR 中的 TXOK 位会被置 1(表示发送成功),RQCP 位也会置 1(表示请求完成)。

  6. 报文被成功发送 → 邮箱返回空置状态

    • 条件:报文完整发送完毕,并且未发生错误(无错误中断)。

    • 结果:

      • 邮箱恢复为空置状态(TME 位变 1)
        • 可以再次被用于发送新报文
        • 软件可通过查询 TXOK 或中断处理程序知道发送完成

4.5)接收处理

接收报文的图解如下:

一个完整接收周期的具体流程如下(先看看流程,寄存器位啥的先不用记住):

  1. 空置 FIFO

    • 含义:FIFO 中没有有效报文,当前为空。

    • 标志位CAN_RF0R 寄存器中的 FMP0(Fill Level of FIFO0)位为 0(表示 0 帧)。

    • 状态:等待总线上的报文到来并匹配过滤器。

  2. 收到有效报文 → FIFO 状态变成挂号_1

    • 有效报文:指的是数据帧直到 EOF 段的最后一位都没有错误,且通过过滤器组对标识符过滤。

    • 条件:总线上收到一帧 CAN 报文,并且该报文的 ID 通过了配置的过滤器(Filter)。

      • 结果:
        • 该报文被硬件自动存入 FIFO 的第一个位置(最前面)。
        • FIFO 状态变为 "挂号_1",表示有 1 帧待读。
    • 提示 :此时可以通过读取 FMP 位知道当前 FIFO 中有多少帧(这里是 1 帧)。例如:FMP0 = 1 → FIFO0 有 1 帧数据。

  3. 不读则继续 → 收到有效报文 → 状态变成挂号_2

    • 条件:CPU 没有及时读取第一帧报文。

    • 后续:又有一帧新的、符合过滤器的报文到达。

    • 结果:

      • 新报文被存入 FIFO 的第二个位置。
      • FIFO 状态变为 "挂号_2",表示有 2 帧待读。
    • 注意:FIFO 是先进先出,所以最早进来的在前面。

  4. 不读则继续 → 收到有效报文 → 状态变成挂号_3

    • 条件:前两帧仍未被读取。

    • 后续:第三帧有效报文到达。

      • 结果:
        • 第三帧被存入 FIFO 的第三个位置。
        • FIFO 状态变为 "挂号_3",表示已满(3 帧)。
    • 重要提醒:此时 FIFO 已达到最大容量!

  5. 继续收到有效报文,FIFO 已满 → 进入溢出状态

    • 条件:FIFO 已满(3 帧),又有新报文通过过滤器。

      • 结果:
        • 发生溢出(Overrun)!
        • 新报文无法存入,会被丢弃。
        • FIFO 状态变为 "溢出状态"
    • 标志位CAN_RF0R 寄存器中的 RF0O(Receive FIFO 0 Overrun)位被置 1,表示发生了溢出错误。

    • ⚠️ 关键问题丢掉哪个报文?

      • 这取决于是否启用了 FIFO 锁定功能(FIFO Locking Mode):

        是否启用锁定 丢弃规则
        ❌ 未启用(默认) 丢弃最新的报文(即第4帧) → 保留原来的3帧
        ✅ 启用 丢弃最旧的报文(即第1帧) → 保留最近的3帧

        如果系统要求实时性高,应启用锁定模式,防止"老数据"长期滞留。

  6. 出现报文丢失问题 → 读取全部报文 → 返回空置状态

    • 操作:CPU 必须尽快读取 FIFO 中的所有报文。

    • 读取方式 :连续调用 HAL_CAN_GetRxMessage() 三次(直到 FMP 变为 0)。

    • 结果:

      • 所有报文被清空。
        • FIFO 状态恢复为 "空置状态"
        • 如果之前发生了溢出,RF0O 位必须由软件清除(通常通过写 1 清除)。

4.6)接收过滤器

在 CAN 总线上,所有节点都能 "听到" 所有的报文(广播式通信)。如果一个节点不加选择地处理每一帧报文,会:占用大量 CPU 时间,增加中断频率,所以需要一个 硬件过滤器只让关心的报文进入 FIFO,其他全部丢弃 ------ 这就是 "接收过滤器" 的作用。

STM32F103 系列的只有 14 组过滤器(0~13)。

每个过滤器组都有两个 32 位寄存器 CAN_FxR1 和 CAN_FxR2(F1中,x=0~13,是过滤器组编号),这俩寄存器都是用来存储报文(数据帧)中的仲裁段信息(ID+IDE+RTR)的。

这两个寄存器所存储的 ID 长度叫做 位宽:可设置为 32 位或 16 位位宽决定了所存储的 ID 信息长度):

  • 32 位: 使用整个 32 位寄存器存储完整的 ID 信息(标准/扩展帧)

  • 16 位: 将 32 位寄存器分成两部分,每部分 16 位,用于存储部分 ID

  • 具体如下:

    过滤器组 x 32位(完整包含 标准ID的11位 + 扩展ID的18位) 16位(每部分仅包含 标准ID的11位 + 扩展ID的高3位)
    CAN_FxR1 STDID[10:0]、EXTID[17:0]、IDE、RTR STDID[10:0]、EXTID[17:15]、IDE、RTR
    CAN_FxR2 STDID[10:0]、EXTID[17:0]、IDE、RTR STDID[10:0]、EXTID[17:15]、IDE、RTR

每个过滤器组还可以设置 选择模式:屏蔽位模式、标识符列表模式选择模式决定了匹配 ID 的方式):

  • 屏蔽位模式:("关键词"机制,常用于范围匹配、字段匹配)
    • 功能: 可以选择出一组符合某个条件的报文(比如 ID 在某个范围内)
    • 寄存器存储内容: 使用 CAN_FxR1 存储 目标 ID (要匹配的值),CAN_FxR2 存储 屏蔽码(Mask)
    • 规则: 屏蔽码中为 1 的位 → 必须完全匹配,屏蔽码中为 0 的位 → 任意值都行,满足 (收到的 ID & 屏蔽码) == (目标 ID & 屏蔽码) 才算符合条件。
    • 补充: 位宽设置为 16 位的话,一个寄存器的整个 32 位就可以一半用来存储目标 ID ,一半用来存储屏蔽码,相当于虽然关键词变少了(能屏蔽的位少了,更宽泛了),但是能筛选出 2 组符合条件的报文。
  • 标识符列表模式:("白名单"机制,精确控制哪些 ID 能被接收)
    • 功能: 只接收几个特定的 ID 报文(比如只收 0x100 和 0x200)
    • 寄存器存储内容: CAN_FxR1CAN_FxR2 都用来存储 具体的 ID 值
    • 规则: 如果 收到的 ID 等于 CAN_FxR1CAN_FxR2 中的任何一个,就算匹配
    • 补充: 位宽设置为 16 位的话,一个寄存器的整个 32 位就可以存储两个具体 ID,相当于虽然只能规定 16 位的 ID ,但是能筛选出 4 个特定的报文。

位宽和选择模式的配置共同决定了这一组过滤器的功能。

具体举例如下:

配置了四组过滤器,各自的配置如下图所示:

以第一组过滤器(位宽:32位,选择模式:屏蔽位模式)为例:

假设过滤器的编号为 0 ,目标 ID 为 0xFFFF0000(11111111111111110000000000000000),屏蔽码为 0xFF00FF00(11111111000000001111111100000000),表示收到的报文的 ID 的 位 24~31 和 位 8~15 需要和目标 ID 一致,其余位随便。符合条件的报文才能进 FIFO 。

实际使用中,IDE 和 RTR 位其实也是要和目标 ID 一致的 。

4.7)STM32 的 CAN 位时序与波特率

前面 3.2)介绍的位时序是制定 CAN 协议的公司规定的,但 ST 公司向来喜欢做一些修改,不过还好只是时间段的合并。

STM32 的 CAN 外设位时序分为三段:同步段 SYNC_SEG(SS)时间段1(BS1)时间段2(BS2)。 其中 BS1=PTS+PBS1 。

注意,上述图片中:

  • 二进制系统中,比特率=波特率;
  • BRP[9:0] 是分频寄存器值,实际分频系数 = BRP[9:0] + 1
  • TS1[3:0] 是位时序控制寄存器中用来设置 BS1 时段的 tq 个数,实际 BS1 时段的 tq 个数 = TS1[3:0] +1
  • TS2[2:0] 是位时序控制寄存器中用来设置 BS2 时段的 tq 个数,实际 BS2 时段的 tq 个数 = TS2[2:0] +1

总结,STM32 中 CAN 通信的波特率计算公式如下::
波特率 = 1 1 ∗ t q + t q ∗ ( T S 1 [ 3 : 0 ] + 1 ) + t q ∗ ( T S 2 [ 2 : 0 ] + 1 ) \large 波特率=\frac{1}{1*tq+tq∗(TS1[3:0]+1)+tq∗(TS2[2:0]+1)} 波特率=1∗tq+tq∗(TS1[3:0]+1)+tq∗(TS2[2:0]+1)1
通信双方波特率需要一致才能通信成功

在 STM32F103 系列中,bxCAN1 挂载在 APB1 总线上, t P C L K t_{PCLK} tPCLK(APB1 时钟周期)= 1/APB1时钟频率 = 1/36000000(查手册)。

举例如下:

STM32F103ZET6,设 TS1=8、TS2=7、BRP=3,波特率 = 36000 / [( 9 + 8 + 1 ) * 4] = 500Kbps。

STM32F407ZET6,设 TS1=6、TS2=5、BRP=5,波特率 = 42000 / [( 7 + 6 + 1 ) * 6] = 500Kbps。

这两个板子的 CAN 通信波特率相同,那这两个板子就可以进行 CAN 通信。


相关推荐
willhuo2 小时前
RS485回响程序设计方案
单片机·lua
项目題供诗2 小时前
51单片机入门-LED点阵屏(九)
单片机·嵌入式硬件·51单片机
恶魔泡泡糖2 小时前
51单片机ADC模数转换
单片机·嵌入式硬件·51单片机
项目題供诗2 小时前
51单片机入门-DS1302时钟(十)
单片机·嵌入式硬件·51单片机
恶魔泡泡糖2 小时前
51单片机DAC数模转换
单片机·嵌入式硬件·51单片机
单片机设计星球2 小时前
51单片机的【智能台灯】仿真设计
单片机·嵌入式硬件·51单片机
智者知已应修善业2 小时前
【51单片机8位密码锁】2023-2-22
c语言·经验分享·笔记·单片机·嵌入式硬件·算法·51单片机
weiyvyy2 小时前
机器人嵌入式开发趋势-开源生态与标准化
人工智能·嵌入式硬件·机器人·开源·信息化
weiyvyy3 小时前
无人机嵌入式开发实战-飞控系统原理与架构
人工智能·嵌入式硬件·机器人·无人机