
1. CAN BUS 总线CANController 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 数据帧由以下部分组成帧起始SOF1 个显性位标志一帧的开始。仲裁段包含 11 位标识符标准帧或 29 位标识符扩展帧以及 RTR 位用于总线仲裁。控制段包含 IDE 位、DLC 位DLC 表示数据长度0-8 字节。数据段0-8 字节的实际数据。CRC 段15 位 CRC 校验码用于错误检测。ACK 段接收节点发送应答位确认正确接收。帧结束EOF7 个隐性位标志一帧结束。下面以标准数据帧为例逐段说明各字段的作用与长度帧起始SOF占用 1 位固定为显性电平逻辑 0。它标志着新报文的开始同时用于同步总线上所有节点的位时序。总线空闲时处于隐性电平SOF 的显性跳变是各节点重新同步的基准。仲裁段标准帧包含 11 位标识符和 1 位 RTRRemote Transmission Request位共 12 位。标识符用于标识报文的优先级和内容类型数值越小优先级越高RTR 位为显性时表示数据帧为隐性时表示远程帧。扩展帧的仲裁段则为 29 位标识符加 1 位 RTR 位共 30 位。控制段包含 1 位 IDEIdentifier Extension位、1 位保留位r0和 4 位 DLCData 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。相位缓冲段 1PHASE_SEG1用于补偿上升沿和下降沿的相位误差可在同步时被延长。长度可配置通常为 1-8 个 TQ。相位缓冲段 2PHASE_SEG2同样用于补偿相位误差可在同步时被缩短。长度可配置通常为 1-8 个 TQ。采样点位于相位缓冲段 1 和相位缓冲段 2 之间。通过合理配置这四个段的长度可以调整采样点位置以适应不同的总线长度和传输速率。例如在高速短距离通信中传播时间段可以设置较短而在低速长距离通信中需要增大传播时间段以覆盖更长的传播延迟。位时间的计算公式为位时间 同步段 传播时间段 相位缓冲段 1 相位缓冲段 2。波特率 1 / 位时间。通过调整预分频器和各段长度可以精确设置所需的通信波特率。6. STM32F4xx CAN 控制器STM32F4xx 系列微控制器内置了 bxCANBasic 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 通信时通常需要以下步骤使能 CAN 和 GPIO 时钟配置 CAN_RX 和 CAN_TX 引脚为复用功能。初始化 CAN 控制器设置波特率、工作模式和过滤器。配置中断或轮询方式处理接收报文。通过发送邮箱发送报文通过接收 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 / (196) / 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 固件库函数API7.1 核心结构体在使用 STM32F4xx 固件库进行 CAN 开发时需要掌握几个核心结构体。这些结构体封装了 CAN 控制器的配置参数和报文数据是调用 API 函数时传递参数的关键载体。下面逐一详细说明。CAN_InitTypeDefCAN_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_FilterInitTypeDefCAN_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_FilterIdLow32 位过滤器标识符的高 16 位和低 16 位。在 32 位宽度下两者组合为一个完整的 32 位标识符在 16 位宽度下分别对应两个独立的 16 位过滤器。CAN_FilterMaskIdHigh / CAN_FilterMaskIdLow32 位屏蔽标识符的高 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_32bit32 位或 CAN_FilterScale_16bit16 位。CAN_FilterActivation过滤器激活状态置 ENABLE 使过滤器生效置 DISABLE 关闭过滤器。CanTxMsgCanTxMsg 用于描述待发送的报文是 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-0x7FF11 位。当 IDE 为 CAN_Id_Standard 时使用。ExtId扩展标识符取值范围 0-0x1FFFFFFF29 位。当 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 个字节有效。CanRxMsgCanRxMsg 用于描述接收到的报文是 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、时间段 1BS1、时间段 2BS2和预分频器。该函数会先使控制器进入初始化模式配置完成后自动退出初始化模式并返回 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)从指定 FIFOCAN_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_FMP0FIFO 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_RXCP0FIFO 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_CRCErrCRC 错误等用于定位通信异常原因。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 / (196) / 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 多节点通信系统。