☰
STM32F4xx CAN 总线通信:从协议原理到固件库实战
2026/10/13 6:56:02 网站建设 项目流程

1. CAN BUS 总线

CAN(Controller Area Network,控制器局域网)总线是一种串行通信协议,由德国博世公司于 1986 年开发,最初用于汽车电子系统中的多节点通信。如今,CAN 总线已广泛应用于工业控制、医疗设备、航空航天、机器人以及嵌入式系统等领域。

CAN 总线采用差分信号传输,通过两根线(CAN_H 和 CAN_L)进行通信,具有抗干扰能力强、传输距离远、实时性高等优点。它支持多主从结构,任意节点都可以主动发起通信,无需中心控制器,非常适合分布式控制系统。

2. CAN BUS 协议

CAN 协议定义了物理层和数据链路层,其核心是报文传输机制。CAN 报文有四种帧类型:数据帧、远程帧、错误帧和过载帧。其中数据帧用于传输实际数据,远程帧用于请求其他节点发送数据。

2.1 帧类型详解

CAN 协议定义了四种帧类型,每种帧在总线上承担不同的职责:

  • 数据帧:用于发送节点向接收节点传输实际数据,是使用最频繁的帧类型。
  • 远程帧:用于请求其他节点发送指定标识符的数据帧,本身不携带数据段。
  • 错误帧:当节点检测到总线错误时主动发出,由错误标志和错误界定符组成,用于通知所有节点本次传输出错。
  • 过载帧:当接收节点尚未准备好接收数据时发出,用于请求延迟下一帧的发送。

2.2 标准帧与扩展帧

CAN 协议支持两种帧格式,区别主要在于标识符的长度:

  • 标准帧(CAN 2.0A):使用 11 位标识符,适用于大多数常规应用场景。
  • 扩展帧(CAN 2.0B):使用 29 位标识符,由 11 位基本标识符和 18 位扩展标识符组成,适用于需要更多报文类型的复杂系统。

标准帧与扩展帧可以共存于同一条总线上,通过 IDE 位进行区分。当 IDE 位为显性时表示标准帧,为隐性时表示扩展帧。

2.3 数据帧结构详解

标准 CAN 数据帧由以下部分组成:

  • 帧起始(SOF):1 个显性位,标志一帧的开始。
  • 仲裁段:包含 11 位标识符(标准帧)或 29 位标识符(扩展帧),以及 RTR 位,用于总线仲裁。
  • 控制段:包含 IDE 位、DLC 位,DLC 表示数据长度(0-8 字节)。
  • 数据段:0-8 字节的实际数据。
  • CRC 段:15 位 CRC 校验码,用于错误检测。
  • ACK 段:接收节点发送应答位,确认正确接收。
  • 帧结束(EOF):7 个隐性位,标志一帧结束。

下面以标准数据帧为例,逐段说明各字段的作用与长度:

  • 帧起始(SOF):占用 1 位,固定为显性电平(逻辑 0)。它标志着新报文的开始,同时用于同步总线上所有节点的位时序。总线空闲时处于隐性电平,SOF 的显性跳变是各节点重新同步的基准。
  • 仲裁段:标准帧包含 11 位标识符和 1 位 RTR(Remote Transmission Request)位,共 12 位。标识符用于标识报文的优先级和内容类型,数值越小优先级越高;RTR 位为显性时表示数据帧,为隐性时表示远程帧。扩展帧的仲裁段则为 29 位标识符加 1 位 RTR 位,共 30 位。
  • 控制段:包含 1 位 IDE(Identifier Extension)位、1 位保留位(r0)和 4 位 DLC(Data Length Code)位。IDE 位用于区分标准帧(显性)和扩展帧(隐性);DLC 位以二进制编码表示数据段的字节数,取值范围为 0-8。标准帧的控制段共 6 位,扩展帧则多出 1 位保留位(r1),共 7 位。
  • 数据段:长度由 DLC 决定,范围为 0-8 字节。数据段承载实际要传输的信息,例如传感器读数、控制指令或状态参数。对于远程帧,数据段长度为 0。
  • CRC 段:包含 15 位 CRC 校验码和 1 位 CRC 界定符。CRC 校验码由发送节点根据帧起始、仲裁段、控制段和数据段的内容计算得出,接收节点用相同算法重新计算并比对,若不一致则判定为 CRC 错误。CRC 界定符固定为 1 个隐性位。
  • ACK 段:包含 1 位 ACK 槽和 1 位 ACK 界定符。发送节点在 ACK 槽发送隐性位,任何正确接收到该帧的节点都会在 ACK 槽发送显性位进行应答。若发送节点在 ACK 槽检测到显性位,说明至少有一个节点正确接收;若仍为隐性位,则产生应答错误。ACK 界定符固定为 1 个隐性位。
  • 帧结束(EOF):由 7 个连续的隐性位组成,标志一帧的结束。EOF 之后是 3 位的帧间空间(ITM),用于分隔相邻的两帧报文。

综合来看,一个标准数据帧的总位数为 44 位(不含位填充),加上数据段后,总长度 = 44 + 8 × DLC 位。例如,当 DLC = 8 时,标准数据帧的总长度为 108 位。理解各段的作用,有助于在实际开发中正确配置 CAN 控制器,并排查通信异常。

2.4 位填充机制

CAN 协议采用位填充(Bit Stuffing)机制来保证时钟同步的稳定性。当发送节点连续发送 5 个相同电平的位时,会自动插入 1 个相反电平的填充位。接收节点在接收时会自动移除这些填充位。位填充机制确保了总线上有足够的电平跳变,便于各节点进行时钟同步,同时也有助于错误检测。

2.5 错误处理机制

CAN 协议内置了完善的错误检测和处理机制,主要包括以下五种错误检测方式:

  • 位错误:发送节点在发送位的同时监听总线,若检测到的电平与发送的电平不一致,则产生位错误。
  • 填充错误:接收节点检测到连续 6 个相同电平的位时,判定为填充错误。
  • CRC 错误:接收节点计算的 CRC 校验码与发送节点附带的 CRC 校验码不一致时,产生 CRC 错误。
  • 格式错误:帧中固定格式的位段(如 EOF、CRC 界定符)出现非法电平时,产生格式错误。
  • 应答错误:发送节点在 ACK 段未检测到显性应答位时,产生应答错误。

当节点检测到错误时,会立即发送错误帧通知总线上的所有节点。每个节点维护发送错误计数器和接收错误计数器,当错误计数超过阈值时,节点会进入总线关闭状态,自动退出通信,避免干扰正常节点。

3. 总线仲裁

CAN 总线采用非破坏性位仲裁机制。当多个节点同时发送报文时,每个节点逐位比较标识符。显性位(逻辑 0)优先于隐性位(逻辑 1),因此标识符数值越小的报文优先级越高。

仲裁过程在仲裁段完成,发送节点在发送每一位时同时监听总线状态。如果发送的是隐性位但总线上检测到显性位,说明有其他更高优先级的节点在发送,该节点立即退出仲裁,转为接收状态。这种机制保证了高优先级报文能够无延迟地获得总线使用权,且不会破坏正在传输的数据。

4. CAN BUS 特点

CAN 总线具有以下显著特点:

  • 多主结构:任意节点均可主动发送报文,无需主机调度。
  • 实时性强:非破坏性仲裁机制确保高优先级报文优先传输,最坏等待时间可预测。
  • 可靠性高:差分信号传输抗干扰能力强,配合 CRC 校验和错误处理机制,误码率极低。
  • 错误处理完善:支持错误检测、错误通知和错误恢复,节点可自动退出故障总线。
  • 传输距离远:在 5 kbps 速率下传输距离可达 10 公里,在 1 Mbps 速率下可达 40 米。
  • 布线简单:仅需两根双绞线即可完成多节点通信,降低系统成本和复杂度。

5. 位传输时间段

在 CAN 总线中,每一位的传输时间由四个时间段组成,这些时间段共同决定了波特率和采样点位置。一个位时间(Bit Time)被划分为以下四个段:

  • 同步段(SYNC_SEG):固定为 1 个时间量子(TQ),用于同步总线上的所有节点。位同步发生在同步段的起始时刻。
  • 传播时间段(PROP_SEG):用于补偿信号在总线上的物理传播延迟和节点内部处理延迟。其长度可配置,通常为 1-8 个 TQ。
  • 相位缓冲段 1(PHASE_SEG1):用于补偿上升沿和下降沿的相位误差,可在同步时被延长。长度可配置,通常为 1-8 个 TQ。
  • 相位缓冲段 2(PHASE_SEG2):同样用于补偿相位误差,可在同步时被缩短。长度可配置,通常为 1-8 个 TQ。

采样点位于相位缓冲段 1 和相位缓冲段 2 之间。通过合理配置这四个段的长度,可以调整采样点位置,以适应不同的总线长度和传输速率。例如,在高速短距离通信中,传播时间段可以设置较短;而在低速长距离通信中,需要增大传播时间段以覆盖更长的传播延迟。

位时间的计算公式为:位时间 = 同步段 + 传播时间段 + 相位缓冲段 1 + 相位缓冲段 2。波特率 = 1 / 位时间。通过调整预分频器和各段长度,可以精确设置所需的通信波特率。

6. STM32F4xx CAN 控制器

STM32F4xx 系列微控制器内置了 bxCAN(Basic Extended CAN)控制器,支持 CAN 协议 2.0A 和 2.0B,具有以下主要特性:

  • 双 CAN 通道:STM32F407 等型号提供 CAN1 和 CAN2 两个独立控制器,可独立配置波特率和工作模式。
  • 三种工作模式:正常模式、静默模式和环回模式。环回模式用于自测,静默模式用于监听总线。
  • 硬件 FIFO:每个 CAN 控制器配备 3 个发送邮箱和 2 个接收 FIFO(每个 FIFO 深度为 3 个报文),支持硬件缓存。
  • 过滤器:提供 28 个可配置的标识符过滤器,支持屏蔽位模式和列表模式,可有效降低 CPU 负载。
  • 中断支持:支持发送完成、接收 FIFO 非空、错误状态等多种中断源,便于实时响应。

6.0 STM32F4xx CAN控制器

STM32F4xx 的 bxCAN 控制器支持三种工作模式:正常模式、静默模式和环回模式。工作模式通过 CAN_MCR 寄存器的 NART、RFLM 等位以及 CAN_BTR 寄存器的 SILM 位和 LBKM 位进行配置。三种模式的主要区别在于是否真正参与总线通信以及是否发送应答信号,下面逐一说明。

  • 正常模式(Normal Mode):这是最常用的工作模式。控制器正常参与总线通信,既能发送报文,也能接收报文,并会按照 CAN 协议对接收到的报文进行应答。在正常模式下,SILM 位和 LBKM 位均为 0。实际项目中的多节点通信均使用该模式。
  • 静默模式(Silent Mode):控制器只接收总线上的报文,不发送任何报文,也不发送应答信号。该模式通过置位 SILM 位使能。静默模式常用于总线监听、协议分析和故障诊断,例如在不干扰总线的前提下观察其他节点的通信内容。
  • 环回模式(Loopback Mode):控制器将自身发送的报文在内部直接回环接收,不经过外部总线,也不发送应答信号。该模式通过置位 LBKM 位使能。环回模式常用于自测,验证控制器发送和接收通路是否正常,无需连接外部节点即可完成功能验证。

此外,静默模式和环回模式还可以组合使用,即同时置位 SILM 位和 LBKM 位,形成静默环回模式。在该组合模式下,控制器内部回环接收自身发送的报文,同时不向外部总线发送任何信号,适合在完全隔离的环境下进行内部自检。

在实际开发中,建议先使用环回模式验证 CAN 控制器的收发通路是否正常,再切换到正常模式接入总线。若需要监听总线上的通信数据,可使用静默模式。合理选择工作模式,可以显著提高调试效率和系统可靠性。

6.1 标识符过滤器

bxCAN 的过滤器是接收报文的第一道关卡,用于在硬件层面筛选出本节点关心的报文,从而显著降低 CPU 的软件处理负担。STM32F4xx 的 CAN1 和 CAN2 共享 28 个过滤器组,每个过滤器组可独立配置为以下两种工作模式:

  • 屏蔽位模式:每个过滤器组由 1 个 32 位标识符寄存器和 1 个 32 位屏蔽寄存器组成。屏蔽寄存器中某一位为 0 时,表示对应标识符位不参与比较(即该位可任意匹配);为 1 时,表示对应标识符位必须与标识符寄存器中的值完全一致。这种模式适合过滤一段连续的标识符范围。
  • 列表模式:每个过滤器组由 2 个 32 位标识符寄存器组成,报文标识符必须与其中一个寄存器完全匹配才能通过。这种模式适合精确匹配少量特定标识符。

每个过滤器组还可以配置为 32 位宽度或 16 位宽度。在 16 位宽度下,一个过滤器组可拆分为两个独立的 16 位过滤器,从而支持更多过滤通道。过滤器组通过 CAN_FMR 寄存器配置为初始化模式后,再通过 CAN_FS1R、CAN_FM1R 和 CAN_FA1R 等寄存器分别设置宽度、模式和关联的 CAN 控制器。

过滤器匹配成功后,报文会被送入对应的接收 FIFO。若报文未通过任何过滤器,则会被硬件直接丢弃,不会占用接收 FIFO 空间。合理配置过滤器,可以让 CAN1 只接收特定标识符的报文,让 CAN2 接收另一组标识符的报文,实现多通道分工接收。

6.2 三个发送邮箱

每个 bxCAN 控制器内置 3 个发送邮箱(Mailbox),编号为 Mailbox 0、Mailbox 1 和 Mailbox 2。发送邮箱用于暂存待发送的报文,其工作流程如下:

  • 邮箱优先级:当多个邮箱同时有待发送报文时,硬件根据邮箱编号和标识符决定发送顺序。若 CAN_TXFP 位为 0,则按标识符优先级发送,标识符数值越小优先级越高;若 CAN_TXFP 位为 1,则按邮箱编号顺序发送,编号越小优先级越高。
  • 发送请求:软件将报文写入邮箱的标识符寄存器、数据寄存器和控制寄存器后,置位对应邮箱的 TXRQ 位,即可请求发送。硬件会自动完成仲裁、位填充、CRC 计算和错误检测等过程。
  • 发送完成:报文成功发送后,硬件置位 RQCP 位和 TXOK 位,并触发发送完成中断(若已使能)。软件可通过查询这些状态位或响应中断来释放邮箱,准备下一帧报文。
  • 发送中止:若需要取消尚未发送的报文,软件可置位 ABRQ 位请求中止发送。硬件会在当前位发送完成后中止该报文,并置位 RQCP 位(TXOK 位为 0)表示发送未完成。

三个发送邮箱可以同时缓存 3 帧待发送报文,配合中断机制,软件可以在发送完成中断中及时填充新报文,实现连续、高效的发送,避免因软件处理延迟而浪费总线带宽。

6.3 两个接收 FIFO

每个 bxCAN 控制器配备 2 个接收 FIFO,分别称为 FIFO 0 和 FIFO 1。每个 FIFO 深度为 3 个报文,即最多可缓存 3 帧接收报文。接收 FIFO 的工作机制如下:

  • 报文入队:当接收到的报文通过过滤器匹配后,硬件将其按顺序写入当前 FIFO。FIFO 采用先进先出(FIFO)原则,软件读取时总是先读到最早接收的报文。
  • FIFO 分配:每个过滤器组可通过 CAN_FA1R 寄存器指定其匹配的报文送入 FIFO 0 还是 FIFO 1。这样可以将不同优先级的报文分流到不同 FIFO,便于软件分类处理。例如,可将高优先级报文送入 FIFO 0,低优先级报文送入 FIFO 1。
  • 状态指示:每个 FIFO 都有对应的状态寄存器,包括 FIFO 空(FMP = 0)、FIFO 非空(FMP > 0)、FIFO 满(FMP = 3)和溢出标志(FOVR)。软件可通过查询这些状态位或响应接收中断来读取报文。
  • 报文读取:软件通过 CAN_RDTxR 寄存器读取 FIFO 中的报文,读取完成后置位 RFOM 位释放该报文占用的存储空间,FIFO 即可接收下一帧报文。
  • 溢出处理:当 FIFO 已满(3 帧报文)且又有新报文通过过滤器时,若 CAN_RFLM 位为 0,新报文会覆盖 FIFO 中最旧的报文;若 CAN_RFLM 位为 1,则新报文被丢弃,并置位溢出标志。实际应用中建议使能接收中断并及时读取,避免溢出导致报文丢失。

通过合理配置过滤器与两个接收 FIFO 的关联,可以实现多优先级报文的并行接收与分类处理,提升系统的实时响应能力。

在 STM32F4xx 上使用 CAN 通信时,通常需要以下步骤:

  1. 使能 CAN 和 GPIO 时钟,配置 CAN_RX 和 CAN_TX 引脚为复用功能。
  2. 初始化 CAN 控制器,设置波特率、工作模式和过滤器。
  3. 配置中断或轮询方式处理接收报文。
  4. 通过发送邮箱发送报文,通过接收 FIFO 读取报文。

以下是一个简单的 CAN 初始化代码示例:

void CAN_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; CAN_InitTypeDef CAN_InitStructure; CAN_FilterInitTypeDef CAN_FilterInitStructure; // 使能时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); // 配置引脚 PB8 (CAN_RX) 和 PB9 (CAN_TX) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_CAN1); GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_CAN1); // CAN 初始化 CAN_InitStructure.CAN_TTCM = DISABLE; CAN_InitStructure.CAN_ABOM = ENABLE; CAN_InitStructure.CAN_AWUM = DISABLE; CAN_InitStructure.CAN_NART = DISABLE; CAN_InitStructure.CAN_RFLM = DISABLE; CAN_InitStructure.CAN_TXFP = DISABLE; CAN_InitStructure.CAN_Mode = CAN_Mode_Normal; CAN_InitStructure.CAN_SJW = CAN_SJW_1tq; CAN_InitStructure.CAN_BS1 = CAN_BS1_9tq; CAN_InitStructure.CAN_BS2 = CAN_BS2_6tq; CAN_InitStructure.CAN_Prescaler = 4; // 波特率 = 42MHz / (1+9+6) / 4 = 500kbps CAN_Init(CAN1, &CAN_InitStructure); // 过滤器初始化:屏蔽位模式,32 位宽度,接收所有报文到 FIFO 0 CAN_FilterInitStructure.CAN_FilterNumber = 0; CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000; CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000; CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000; CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000; CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FIFO0; CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; CAN_FilterInit(&CAN_FilterInitStructure); }

通过以上配置,即可在 STM32F4xx 上建立 CAN 通信通道,实现多节点之间的可靠数据交换。实际项目中,可根据业务需求灵活配置过滤器、发送邮箱和接收 FIFO,以充分发挥 bxCAN 控制器的硬件能力。

7. STM32 固件库函数API

7.1 核心结构体

在使用 STM32F4xx 固件库进行 CAN 开发时,需要掌握几个核心结构体。这些结构体封装了 CAN 控制器的配置参数和报文数据,是调用 API 函数时传递参数的关键载体。下面逐一详细说明。

CAN_InitTypeDef

CAN_InitTypeDef 用于配置 CAN 控制器的工作参数,是 CAN_Init 函数的参数类型。其定义如下:

typedef struct { FunctionalState CAN_TTCM; // 时间触发通信模式使能 FunctionalState CAN_ABOM; // 自动总线恢复管理使能 FunctionalState CAN_AWUM; // 自动唤醒模式使能 FunctionalState CAN_NART; // 非自动重传模式使能 FunctionalState CAN_RFLM; // 接收 FIFO 锁定模式使能 FunctionalState CAN_TXFP; // 发送 FIFO 优先级使能 uint8_t CAN_Mode; // CAN 工作模式 uint8_t CAN_SJW; // 同步跳转宽度 uint8_t CAN_BS1; // 时间段 1 uint8_t CAN_BS2; // 时间段 2 uint16_t CAN_Prescaler; // 预分频器 } CAN_InitTypeDef;
  • CAN_TTCM:时间触发通信模式。置 ENABLE 时启用时间触发模式,报文发送时间由硬件精确控制;置 DISABLE 时使用普通模式。大多数应用使用 DISABLE。
  • CAN_ABOM:自动总线恢复管理。置 ENABLE 时,当节点进入总线关闭状态后,硬件会自动等待 128 个总线空闲周期并自动恢复通信;置 DISABLE 时需软件手动恢复。
  • CAN_AWUM:自动唤醒模式。置 ENABLE 时,检测到总线活动后硬件自动唤醒;置 DISABLE 时需软件唤醒。
  • CAN_NART:非自动重传模式。置 ENABLE 时,报文发送失败后不自动重传;置 DISABLE 时,硬件会自动重传直到发送成功。
  • CAN_RFLM:接收 FIFO 锁定模式。置 ENABLE 时,FIFO 满后新报文被丢弃;置 DISABLE 时,新报文覆盖最旧报文。
  • CAN_TXFP:发送 FIFO 优先级。置 ENABLE 时按邮箱编号顺序发送;置 DISABLE 时按标识符优先级发送。
  • CAN_Mode:工作模式,可选 CAN_Mode_Normal(正常)、CAN_Mode_Silent(静默)、CAN_Mode_LoopBack(环回)或 CAN_Mode_Silent_LoopBack(静默环回)。
  • CAN_SJW:同步跳转宽度,可选 CAN_SJW_1tq 至 CAN_SJW_4tq,用于补偿时钟偏差。
  • CAN_BS1:时间段 1,可选 CAN_BS1_1tq 至 CAN_BS1_16tq,包含传播时间段和相位缓冲段 1。
  • CAN_BS2:时间段 2,可选 CAN_BS2_1tq 至 CAN_BS2_8tq,即相位缓冲段 2。
  • CAN_Prescaler:预分频器,取值范围 1-1024,用于分频 APB1 时钟得到时间量子(TQ)。波特率 = APB1 时钟 / (1 + BS1 + BS2) / Prescaler。

CAN_FilterInitTypeDef

CAN_FilterInitTypeDef 用于配置标识符过滤器,是 CAN_FilterInit 函数的参数类型。其定义如下:

typedef struct { uint16_t CAN_FilterIdHigh; // 过滤器标识符高 16 位 uint16_t CAN_FilterIdLow; // 过滤器标识符低 16 位 uint16_t CAN_FilterMaskIdHigh; // 过滤器屏蔽标识符高 16 位 uint16_t CAN_FilterMaskIdLow; // 过滤器屏蔽标识符低 16 位 uint16_t CAN_FilterFIFOAssignment; // 关联的接收 FIFO uint8_t CAN_FilterNumber; // 过滤器编号(0-27) uint8_t CAN_FilterMode; // 过滤器模式 uint8_t CAN_FilterScale; // 过滤器宽度 FunctionalState CAN_FilterActivation; // 过滤器激活 } CAN_FilterInitTypeDef;
  • CAN_FilterIdHigh / CAN_FilterIdLow:32 位过滤器标识符的高 16 位和低 16 位。在 32 位宽度下,两者组合为一个完整的 32 位标识符;在 16 位宽度下,分别对应两个独立的 16 位过滤器。
  • CAN_FilterMaskIdHigh / CAN_FilterMaskIdLow:32 位屏蔽标识符的高 16 位和低 16 位。屏蔽位为 1 时对应标识符位必须匹配,为 0 时该位可任意匹配。列表模式下不使用屏蔽值。
  • CAN_FilterFIFOAssignment:指定匹配成功的报文送入哪个 FIFO,可选 CAN_FIFO0 或 CAN_FIFO1。
  • CAN_FilterNumber:过滤器组编号,范围为 0-27,共 28 个过滤器组。
  • CAN_FilterMode:过滤器模式,可选 CAN_FilterMode_IdMask(屏蔽位模式)或 CAN_FilterMode_IdList(列表模式)。
  • CAN_FilterScale:过滤器宽度,可选 CAN_FilterScale_32bit(32 位)或 CAN_FilterScale_16bit(16 位)。
  • CAN_FilterActivation:过滤器激活状态,置 ENABLE 使过滤器生效,置 DISABLE 关闭过滤器。

CanTxMsg

CanTxMsg 用于描述待发送的报文,是 CAN_Transmit 函数的参数类型。其定义如下:

typedef struct { uint32_t StdId; // 标准标识符(11 位) uint32_t ExtId; // 扩展标识符(29 位) uint8_t IDE; // 帧格式:标准帧或扩展帧 uint8_t RTR; // 帧类型:数据帧或远程帧 uint8_t DLC; // 数据长度(0-8) uint8_t Data[8]; // 数据内容 } CanTxMsg;
  • StdId:标准标识符,取值范围 0-0x7FF(11 位)。当 IDE 为 CAN_Id_Standard 时使用。
  • ExtId:扩展标识符,取值范围 0-0x1FFFFFFF(29 位)。当 IDE 为 CAN_Id_Extended 时使用。
  • IDE:帧格式选择,可选 CAN_Id_Standard(标准帧)或 CAN_Id_Extended(扩展帧)。
  • RTR:帧类型选择,可选 CAN_RTR_Data(数据帧)或 CAN_RTR_Remote(远程帧)。
  • DLC:数据长度代码,取值范围 0-8,表示 Data 数组中有效数据的字节数。
  • Data[8]:报文数据缓冲区,最多可存放 8 字节数据。发送时仅前 DLC 个字节有效。

CanRxMsg

CanRxMsg 用于描述接收到的报文,是 CAN_Receive 函数的参数类型。其定义如下:

typedef struct { uint32_t StdId; // 标准标识符(11 位) uint32_t ExtId; // 扩展标识符(29 位) uint8_t IDE; // 帧格式:标准帧或扩展帧 uint8_t RTR; // 帧类型:数据帧或远程帧 uint8_t DLC; // 数据长度(0-8) uint8_t Data[8]; // 数据内容 uint8_t FMI; // 过滤器匹配索引 } CanRxMsg;
  • StdId:接收报文的标准标识符,由硬件从报文仲裁段解析得到。
  • ExtId:接收报文的扩展标识符,仅当 IDE 为 CAN_Id_Extended 时有效。
  • IDE:接收报文的帧格式,由硬件根据 IDE 位解析得到。
  • RTR:接收报文的帧类型,由硬件根据 RTR 位解析得到。
  • DLC:接收报文的数据长度,由硬件从控制段解析得到。
  • Data[8]:接收到的数据内容,仅前 DLC 个字节有效。
  • FMI:过滤器匹配索引,表示该报文匹配了哪个过滤器组(0-27),可用于区分不同来源的报文。

掌握以上四个核心结构体的字段含义和配置方法,是熟练使用 STM32F4xx CAN 固件库的基础。在实际开发中,建议先使用 CAN_StructInit 将结构体填充为默认值,再按需修改个别字段,可有效避免遗漏配置项。

STM32F4xx 标准外设库(Standard Peripheral Library)为 bxCAN 控制器提供了一套完整的 API 函数,封装了寄存器操作,使开发者无需直接操作底层寄存器即可完成 CAN 的初始化、发送、接收和中断管理。下面按功能分类详细介绍常用 API 函数。

7.2 常用 API 函数

7.2.1 初始化与配置

初始化类函数负责完成 CAN 控制器的时钟使能、引脚配置、波特率设置和过滤器配置,是使用 CAN 通信的第一步。

  • CAN_Init(CAN_TypeDef* CANx, CAN_InitTypeDef* CAN_InitStruct):根据 CAN_InitStruct 中的参数初始化 CAN 控制器,包括工作模式、同步跳转宽度(SJW)、时间段 1(BS1)、时间段 2(BS2)和预分频器。该函数会先使控制器进入初始化模式,配置完成后自动退出初始化模式并返回 CAN_InitStatus_Success 或 CAN_InitStatus_Failed。
  • CAN_FilterInit(CAN_FilterInitTypeDef* CAN_FilterInitStruct):初始化指定的过滤器组,配置过滤器编号、工作模式(屏蔽位或列表)、宽度(32 位或 16 位)、标识符值、屏蔽值以及关联的接收 FIFO。配置完成后置位激活位使过滤器生效。
  • CAN_DeInit(CAN_TypeDef* CANx):将 CAN 控制器恢复为默认复位状态,清除所有配置和寄存器值,常用于系统复位或重新初始化前。
  • CAN_StructInit(CAN_InitTypeDef* CAN_InitStruct):将 CAN_InitStruct 结构体填充为默认值,避免因未初始化结构体导致配置错误。
7.2.2 报文发送

发送函数用于将待发送报文写入发送邮箱并触发发送请求,支持单帧发送和批量发送两种方式。

  • CAN_Transmit(CAN_TypeDef* CANx, CanTxMsg* TxMessage):将 TxMessage 指向的报文写入一个空闲发送邮箱,并置位 TXRQ 位请求发送。函数返回邮箱编号(0、1、2)或 CAN_TxStatus_NoMailBox(无空闲邮箱)。发送前需填充 TxMessage 的标识符、帧格式、帧类型和数据长度等字段。
  • CAN_TransmitStatus(CAN_TypeDef* CANx, uint8_t TransmitMailbox):查询指定邮箱的发送状态,返回 CAN_TxStatus_Failed、CAN_TxStatus_Ok、CAN_TxStatus_Pending 或 CAN_TxStatus_NoMailBox,用于判断报文是否发送成功。
  • CAN_CancelTransmit(CAN_TypeDef* CANx, uint8_t Mailbox):中止指定邮箱中尚未发送的报文,置位 ABRQ 位请求硬件取消发送。
7.2.3 报文接收

接收函数用于从接收 FIFO 中读取报文,并查询 FIFO 的状态信息。

  • CAN_Receive(CAN_TypeDef* CANx, uint8_t FIFONumber, CanRxMsg* RxMessage):从指定 FIFO(CAN_FIFO0 或 CAN_FIFO1)中读取最早接收的一帧报文到 RxMessage 结构体,读取完成后自动释放该报文占用的存储空间。
  • CAN_FIFORelease(CAN_TypeDef* CANx, uint8_t FIFONumber):手动释放指定 FIFO 中当前报文占用的空间,置位 RFOM 位,使 FIFO 可以接收下一帧报文。
  • CAN_MessagePending(CAN_TypeDef* CANx, uint8_t FIFONumber):返回指定 FIFO 中当前缓存的报文数量(0-3),用于判断是否有新报文到达。
7.2.4 中断管理

中断管理函数用于使能或禁用 CAN 的各种中断源,并获取中断状态标志。

  • CAN_ITConfig(CAN_TypeDef* CANx, uint32_t CAN_IT, FunctionalState NewState):使能或禁用指定中断源,如 CAN_IT_TME(发送邮箱空)、CAN_IT_FMP0(FIFO 0 非空)、CAN_IT_ERR(错误中断)等。
  • CAN_GetITStatus(CAN_TypeDef* CANx, uint32_t CAN_IT):查询指定中断是否发生,返回 SET 或 RESET,用于在中断服务函数中判断中断来源。
  • CAN_ClearITPendingBit(CAN_TypeDef* CANx, uint32_t CAN_IT):清除指定中断的挂起标志位,避免中断重复触发。
7.2.5 状态查询

状态查询函数用于获取 CAN 控制器的错误状态和总线状态,便于调试和故障诊断。

  • CAN_GetFlagStatus(CAN_TypeDef* CANx, uint32_t CAN_FLAG):查询指定状态标志,如 CAN_FLAG_RXCP0(FIFO 0 报文接收完成)、CAN_FLAG_TXOK0(邮箱 0 发送成功)等。
  • CAN_ClearFlag(CAN_TypeDef* CANx, uint32_t CAN_FLAG):清除指定的状态标志位。
  • CAN_GetErrorCode(CAN_TypeDef* CANx):返回最近一次错误类型,如 CAN_ErrorCode_StuffErr(填充错误)、CAN_ErrorCode_FormErr(格式错误)、CAN_ErrorCode_CRCErr(CRC 错误)等,用于定位通信异常原因。

7.3 发送与接收示例

下面给出一个完整的 CAN 通信开发流程示例,涵盖初始化、发送、接收和中断处理四个环节,演示如何使用固件库 API 完成从配置到实际通信的完整闭环。

下面给出一个完整的报文发送与接收示例,演示如何使用固件库 API 完成 CAN 通信。

// 发送一帧标准数据帧 CanTxMsg TxMessage; TxMessage.StdId = 0x123; // 标准标识符 TxMessage.ExtId = 0x00; // 扩展标识符(未使用) TxMessage.IDE = CAN_Id_Standard; // 标准帧 TxMessage.RTR = CAN_RTR_Data; // 数据帧 TxMessage.DLC = 4; // 数据长度 4 字节 TxMessage.Data[0] = 0x01; TxMessage.Data[1] = 0x02; TxMessage.Data[2] = 0x03; TxMessage.Data[3] = 0x04; uint8_t mailbox = CAN_Transmit(CAN1, &TxMessage); if (mailbox != CAN_TxStatus_NoMailBox) { // 等待发送完成 while (CAN_TransmitStatus(CAN1, mailbox) != CAN_TxStatus_Ok); } // 接收一帧报文(轮询方式) CanRxMsg RxMessage; if (CAN_MessagePending(CAN1, CAN_FIFO0) > 0) { CAN_Receive(CAN1, CAN_FIFO0, &RxMessage); // 处理接收到的报文 uint8_t received_id = RxMessage.StdId; uint8_t received_len = RxMessage.DLC; // 读取数据 for (uint8_t i = 0; i < received_len; i++) { // RxMessage.Data[i] } }

在实际项目中,建议将发送和接收逻辑封装为独立函数,并配合中断方式处理接收报文,以提高系统实时性。同时,使用 CAN_GetErrorCode 定期检查错误状态,便于及时发现并处理总线异常。

7.4 完整开发流程

一个完整的 CAN 通信工程通常包含以下步骤:先完成时钟和引脚配置,再初始化 CAN 控制器和过滤器,随后配置中断并编写发送与接收逻辑,最后在主循环中调度收发任务。下面按顺序给出完整代码。

#include "stm32f4xx.h" #include "stm32f4xx_can.h" #include "stm32f4xx_gpio.h" #include "stm32f4xx_rcc.h" #include "stm32f4xx_nvic.h" // 全局接收缓冲区 CanRxMsg RxMessage; // 发送一帧标准数据帧 void CAN_SendFrame(uint32_t id, uint8_t* data, uint8_t len) { CanTxMsg TxMessage; uint8_t mailbox; TxMessage.StdId = id; // 标准标识符 TxMessage.ExtId = 0x00; // 扩展标识符(未使用) TxMessage.IDE = CAN_Id_Standard; // 标准帧 TxMessage.RTR = CAN_RTR_Data; // 数据帧 TxMessage.DLC = len; // 数据长度 for (uint8_t i = 0; i < len; i++) { TxMessage.Data[i] = data[i]; } mailbox = CAN_Transmit(CAN1, &TxMessage); if (mailbox != CAN_TxStatus_NoMailBox) { // 等待发送完成(实际项目中可改用中断方式) while (CAN_TransmitStatus(CAN1, mailbox) != CAN_TxStatus_Ok); } } // 接收中断服务函数 void CAN1_RX0_IRQHandler(void) { if (CAN_GetITStatus(CAN1, CAN_IT_FMP0) != RESET) { CAN_Receive(CAN1, CAN_FIFO0, &RxMessage); // 在此处处理接收到的报文 // 例如:根据 RxMessage.StdId 和 RxMessage.Data 执行相应逻辑 CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0); } } // CAN 初始化函数 void CAN_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; CAN_InitTypeDef CAN_InitStructure; CAN_FilterInitTypeDef CAN_FilterInitStructure; NVIC_InitTypeDef NVIC_InitStructure; // 1. 使能时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); // 2. 配置引脚 PB8 (CAN_RX) 和 PB9 (CAN_TX) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_CAN1); GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_CAN1); // 3. 初始化 CAN 控制器 CAN_InitStructure.CAN_TTCM = DISABLE; CAN_InitStructure.CAN_ABOM = ENABLE; CAN_InitStructure.CAN_AWUM = DISABLE; CAN_InitStructure.CAN_NART = DISABLE; CAN_InitStructure.CAN_RFLM = DISABLE; CAN_InitStructure.CAN_TXFP = DISABLE; CAN_InitStructure.CAN_Mode = CAN_Mode_Normal; CAN_InitStructure.CAN_SJW = CAN_SJW_1tq; CAN_InitStructure.CAN_BS1 = CAN_BS1_9tq; CAN_InitStructure.CAN_BS2 = CAN_BS2_6tq; CAN_InitStructure.CAN_Prescaler = 4; // 波特率 = 42MHz / (1+9+6) / 4 = 500kbps CAN_Init(CAN1, &CAN_InitStructure); // 4. 过滤器初始化:屏蔽位模式,32 位宽度,接收所有报文到 FIFO 0 CAN_FilterInitStructure.CAN_FilterNumber = 0; CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000; CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000; CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000; CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000; CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FIFO0; CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; CAN_FilterInit(&CAN_FilterInitStructure); // 5. 配置接收中断 NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE); } // 主函数示例 int main(void) { uint8_t send_data[4] = {0x01, 0x02, 0x03, 0x04}; CAN_Config(); while (1) { // 周期性发送一帧数据 CAN_SendFrame(0x123, send_data, 4); Delay(100); // 延时 100ms,实际项目中可用定时器实现 } }

上述代码展示了完整的 CAN 通信开发流程:首先使能时钟并配置 GPIO 复用功能,然后初始化 CAN 控制器和过滤器,接着配置接收中断,最后在主循环中调用发送函数周期性地发送数据,接收到的报文则在中断服务函数中处理。实际项目中,建议将发送和接收逻辑封装为独立模块,并配合错误状态检查(CAN_GetErrorCode)和总线恢复机制,以提升系统的健壮性和实时性。

8. 总结

本文系统梳理了 STM32F4xx 上 CAN 总线通信的完整知识体系,从协议基础到硬件实现再到固件库开发,形成了一条清晰的学习路径。

在协议层面,CAN 总线凭借差分信号传输、非破坏性位仲裁和完备的错误处理机制,成为工业与汽车领域最可靠的现场总线之一。理解四种帧类型、标准帧与扩展帧的区别、数据帧各字段的作用,以及位填充和错误检测机制,是掌握 CAN 通信的基石。

在硬件层面,STM32F4xx 内置的 bxCAN 控制器提供了三种工作模式、28 个可配置过滤器、3 个发送邮箱和 2 个接收 FIFO。合理利用这些硬件资源,可以显著降低 CPU 负载并提升通信实时性。开发时建议先用环回模式验证收发通路,再切换到正常模式接入总线。

在开发层面,固件库将底层寄存器操作封装为简洁的 API 函数。掌握 CAN_InitTypeDef、CAN_FilterInitTypeDef、CanTxMsg 和 CanRxMsg 四个核心结构体,以及初始化、发送、接收、中断管理和状态查询五类 API,即可快速搭建可用的 CAN 通信工程。

最后给出几点实践建议:一是发送和接收逻辑应封装为独立模块,便于复用和维护;二是优先使用中断方式处理接收报文,避免轮询占用 CPU;三是定期调用 CAN_GetErrorCode 检查总线错误状态,并结合 CAN_ABOM 自动恢复机制提升系统健壮性。遵循以上要点,即可在 STM32F4xx 上构建稳定可靠的 CAN 多节点通信系统。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询