STM32F1 CAN 标准帧与扩展帧实战:6种过滤器配置与收发测试代码
2026/7/10 1:06:08 网站建设 项目流程

STM32F1 CAN标准帧与扩展帧实战:6种过滤器配置与双机通信全解析

在工业控制、汽车电子和物联网设备中,CAN总线因其高可靠性和实时性成为首选通信协议。STM32F1系列微控制器内置CAN控制器,但实际开发中,工程师常面临标准帧与扩展帧混用场景下的过滤器配置难题。本文将深入解析6种过滤器模式,并提供可直接移植的代码实现。

1. CAN帧类型与STM32过滤器核心机制

CAN协议定义了两种帧格式:标准帧(11位ID)和扩展帧(29位ID)。STM32F1的CAN控制器通过硬件过滤器实现高效帧过滤,其核心机制包含三个关键点:

  • 双FIFO结构:STM32F1提供FIFO0和FIFO1两个接收缓冲区,每个可存储3个完整报文
  • 14个共享过滤器:所有过滤器编号0-13,可灵活分配给FIFO0或FIFO1
  • 两种过滤模式
    • 标识符屏蔽模式(CAN_FilterMode_IdMask)
    • 标识符列表模式(CAN_FilterMode_IdList)

标准帧与扩展帧的ID结构差异如下表所示:

帧类型ID长度结构组成典型应用场景
标准帧11位11位基础ID简单控制指令、传感器数据
扩展帧29位11位基础ID + 18位扩展ID复杂系统、大容量数据传输

关键提示:扩展帧的IDE位恒为1(隐性电平),标准帧IDE位恒为0(显性电平)。这一特性是硬件过滤器区分帧类型的基础。

2. 6种过滤器配置模式详解

2.1 标准数据帧过滤(模式1)

// 只接收指定ID的标准数据帧 void CAN_Filter_StdDataFrame(uint8_t fifo, uint8_t filter_num, uint32_t id) { CAN_FilterInitTypeDef filter; filter.FilterNumber = filter_num; filter.FilterMode = CAN_FilterMode_IdMask; filter.FilterScale = CAN_FilterScale_32bit; // 高16位:标准ID左移5位(对齐到bit15-bit5) filter.FilterIdHigh = (id << 5) & 0xFFFF; // 低16位:IDE=0(标准帧),RTR=0(数据帧) filter.FilterIdLow = (id << 5) | CAN_ID_STD | CAN_RTR_DATA; filter.FilterMaskIdHigh = 0xFFFF; // 必须完全匹配ID filter.FilterMaskIdLow = 0xFFFF; // 必须匹配IDE和RTR位 filter.FilterFIFOAssignment = fifo ? CAN_FIFO1 : CAN_FIFO0; filter.FilterActivation = ENABLE; CAN_FilterInit(&filter); }

配置要点:

  1. 标准帧ID左移5位对齐到bit15-bit5
  2. IDE位显性(0)表示标准帧
  3. RTR位显性(0)表示数据帧

2.2 标准遥控帧过滤(模式2)

// 只接收指定ID的标准遥控帧 void CAN_Filter_StdRemoteFrame(uint8_t fifo, uint8_t filter_num, uint32_t id) { CAN_FilterInitTypeDef filter; filter.FilterNumber = filter_num; filter.FilterMode = CAN_FilterMode_IdMask; filter.FilterScale = CAN_FilterScale_32bit; filter.FilterIdHigh = (id << 5) & 0xFFFF; filter.FilterIdLow = (id << 5) | CAN_ID_STD | CAN_RTR_REMOTE; filter.FilterMaskIdHigh = 0xFFFF; filter.FilterMaskIdLow = 0xFFFF; filter.FilterFIFOAssignment = fifo ? CAN_FIFO1 : CAN_FIFO0; filter.FilterActivation = ENABLE; CAN_FilterInit(&filter); }

特殊处理:

  • 遥控帧的RTR位置1(隐性电平)
  • 接收方需在中断处理中回复数据帧

2.3 标准帧通用过滤(模式3)

// 接收指定ID的所有标准帧(数据帧+遥控帧) void CAN_Filter_StdAll(uint8_t fifo, uint8_t filter_num, uint32_t id) { CAN_FilterInitTypeDef filter; filter.FilterNumber = filter_num; filter.FilterMode = CAN_FilterMode_IdMask; filter.FilterScale = CAN_FilterScale_32bit; filter.FilterIdHigh = (id << 5) & 0xFFFF; filter.FilterIdLow = (id << 5) | CAN_ID_STD; filter.FilterMaskIdHigh = 0xFFFF; filter.FilterMaskIdLow = 0xFFFC; // 不检查RTR和保留位 filter.FilterFIFOAssignment = fifo ? CAN_FIFO1 : CAN_FIFO0; filter.FilterActivation = ENABLE; CAN_FilterInit(&filter); }

应用场景:

  • 需要同时处理数据帧和遥控帧
  • 屏蔽位设置为0xFFFC忽略RTR位差异

2.4 扩展数据帧过滤(模式4)

// 只接收指定ID的扩展数据帧 void CAN_Filter_ExtDataFrame(uint8_t fifo, uint8_t filter_num, uint32_t id) { CAN_FilterInitTypeDef filter; filter.FilterNumber = filter_num; filter.FilterMode = CAN_FilterMode_IdMask; filter.FilterScale = CAN_FilterScale_32bit; // 高16位:扩展ID高16位(ID28-ID13) filter.FilterIdHigh = (id << 3) >> 16; // 低16位:扩展ID低13位(ID12-ID0)+ IDE=1 + RTR=0 filter.FilterIdLow = ((id << 3) | CAN_ID_EXT | CAN_RTR_DATA) & 0xFFFF; filter.FilterMaskIdHigh = 0xFFFF; filter.FilterMaskIdLow = 0xFFFF; filter.FilterFIFOAssignment = fifo ? CAN_FIFO1 : CAN_FIFO0; filter.FilterActivation = ENABLE; CAN_FilterInit(&filter); }

关键点:

  • 29位ID左移3位后拆分到高低16位寄存器
  • IDE位置1(隐性电平)表示扩展帧

2.5 扩展遥控帧过滤(模式5)

// 只接收指定ID的扩展遥控帧 void CAN_Filter_ExtRemoteFrame(uint8_t fifo, uint8_t filter_num, uint32_t id) { CAN_FilterInitTypeDef filter; filter.FilterNumber = filter_num; filter.FilterMode = CAN_FilterMode_IdMask; filter.FilterScale = CAN_FilterScale_32bit; filter.FilterIdHigh = (id << 3) >> 16; filter.FilterIdLow = ((id << 3) | CAN_ID_EXT | CAN_RTR_REMOTE) & 0xFFFF; filter.FilterMaskIdHigh = 0xFFFF; filter.FilterMaskIdLow = 0xFFFF; filter.FilterFIFOAssignment = fifo ? CAN_FIFO1 : CAN_FIFO0; filter.FilterActivation = ENABLE; CAN_FilterInit(&filter); }

注意事项:

  • 接收到遥控帧后需在1ms内回复数据帧
  • 总线负载较高时需考虑响应实时性

2.6 扩展帧通用过滤(模式6)

// 接收指定ID的所有扩展帧(数据帧+遥控帧) void CAN_Filter_ExtAll(uint8_t fifo, uint8_t filter_num, uint32_t id) { CAN_FilterInitTypeDef filter; filter.FilterNumber = filter_num; filter.FilterMode = CAN_FilterMode_IdMask; filter.FilterScale = CAN_FilterScale_32bit; filter.FilterIdHigh = (id << 3) >> 16; filter.FilterIdLow = ((id << 3) | CAN_ID_EXT) & 0xFFFF; filter.FilterMaskIdHigh = 0xFFFF; filter.FilterMaskIdLow = 0xFFFC; // 不检查RTR和保留位 filter.FilterFIFOAssignment = fifo ? CAN_FIFO1 : CAN_FIFO0; filter.FilterActivation = ENABLE; CAN_FilterInit(&filter); }

典型应用:

  • 需要处理同一ID的数据帧和遥控帧
  • 工业设备双向通信场景

3. 双机通信实战:环回模式与正常模式

3.1 硬件连接方案

双机通信硬件连接需注意:

  1. CAN_H和CAN_L需使用双绞线
  2. 总线两端各接120Ω终端电阻
  3. 推荐使用TI的SN65HVD23x系列CAN收发器
# STM32F103C8T6最小系统连接示例 +-----------------+ +-----------------+ | STM32 | | STM32 | | | | | | PA11 --------+---------->| PA12 (CAN_RX) | | PA12 <-------+-----------| PA11 (CAN_TX) | | | | | +--------+--------+ +--------+--------+ | | \|/ \|/ +-------+-------+ +-------+-------+ | CAN_H | | CAN_H | | CAN_L | | CAN_L | +-------+-------+ +-------+-------+ | | \|/ \|/ +-----------------+ +-----------------+ | 120Ω 终端电阻 | | 120Ω 终端电阻 | +-----------------+ +-----------------+

3.2 环回模式测试代码

环回模式用于单节点自测试,无需实际硬件连接:

void CAN_Mode_Config(uint8_t mode, uint32_t baudrate) { CAN_InitTypeDef CAN_InitStruct; CAN_StructInit(&CAN_InitStruct); // 模式选择 if(mode == 0) { CAN_InitStruct.CAN_Mode = CAN_Mode_LoopBack; // 环回模式 } else { CAN_InitStruct.CAN_Mode = CAN_Mode_Normal; // 正常模式 } // 波特率配置(以1MHz为例) CAN_InitStruct.CAN_SJW = CAN_SJW_1tq; CAN_InitStruct.CAN_BS1 = CAN_BS1_3tq; CAN_InitStruct.CAN_BS2 = CAN_BS2_2tq; CAN_InitStruct.CAN_Prescaler = 6; // 36MHz/(1+3+2)/6 = 1MHz CAN_Init(CAN1, &CAN_InitStruct); // 配置过滤器(示例:FIFO0接收标准数据帧ID=0x123) CAN_Filter_StdDataFrame(0, 0, 0x123); }

3.3 中断处理与数据收发

完整的通信流程需要配合中断处理:

// 发送标准数据帧 int CAN_SendStdData(uint32_t id, uint8_t* data, uint8_t len) { CanTxMsg msg; msg.StdId = id; msg.IDE = CAN_ID_STD; msg.RTR = CAN_RTR_DATA; msg.DLC = len > 8 ? 8 : len; memcpy(msg.Data, data, msg.DLC); uint8_t mailbox = CAN_Transmit(CAN1, &msg); return (mailbox != CAN_NO_MB) ? 0 : -1; } // 接收中断处理 void USB_LP_CAN1_RX0_IRQHandler(void) { if(CAN_GetITStatus(CAN1, CAN_IT_FMP0)) { CanRxMsg rx_msg; CAN_Receive(CAN1, CAN_FIFO0, &rx_msg); if(rx_msg.IDE == CAN_ID_STD) { printf("StdID:0x%03X Data:", rx_msg.StdId); } else { printf("ExtID:0x%08lX Data:", rx_msg.ExtId); } for(int i=0; i<rx_msg.DLC; i++) { printf("%02X ", rx_msg.Data[i]); } printf("\n"); } CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0); }

4. 常见问题与调试技巧

4.1 过滤器配置失败排查

  1. 检查过滤器编号:确保不重复使用同一过滤器编号
  2. 验证ID对齐方式
    • 标准帧:ID左移5位
    • 扩展帧:ID左移3位
  3. 确认FIFO分配:过滤器必须明确指定到FIFO0或FIFO1

4.2 通信异常处理

当通信异常时,可通过以下寄存器状态诊断:

寄存器关键位含义
CAN_ESRLEC[2:0]最后错误代码(0=无错误)
CAN_MSRSLAKI睡眠模式确认中断
CAN_TSRTME[2:0]发送邮箱空状态

错误处理参考代码:

void CAN_ErrorHandler(void) { uint32_t esr = CAN->ESR; if(esr & CAN_ESR_BOFF) { printf("Bus-off状态,需重新初始化CAN\n"); } else { switch((esr & CAN_ESR_LEC) >> CAN_ESR_LEC_Pos) { case 1: printf("填充错误\n"); break; case 2: printf("格式错误\n"); break; case 3: printf("ACK错误\n"); break; case 4: printf("隐性位错误\n"); break; case 5: printf("显性位错误\n"); break; case 6: printf("CRC错误\n"); break; } } // 清除错误状态 CAN->MSR |= CAN_MSR_ERRI; }

4.3 性能优化建议

  1. 过滤器分组策略
    • 高优先级消息分配到FIFO0
    • 低优先级消息分配到FIFO1
  2. 中断优先级设置
    NVIC_InitTypeDef NVIC_InitStruct; NVIC_InitStruct.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; // 高于其他通信接口 NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct);
  3. 总线负载监控
    float CAN_GetBusLoad(void) { uint32_t esr = CAN->ESR; uint32_t rec = (esr & CAN_ESR_REC) >> CAN_ESR_REC_Pos; uint32_t tec = (esr & CAN_ESR_TEC) >> CAN_ESR_TEC_Pos; return (rec + tec) / 255.0f * 100; // 返回百分比 }

5. 进阶应用:混合帧网络设计

在实际项目中,常需同时处理标准帧和扩展帧。推荐两种设计方案:

方案A:双过滤器组策略

// FIFO0处理标准帧,FIFO1处理扩展帧 void CAN_MixedFilter_Init(void) { // FIFO0过滤器组:标准帧ID 0x100-0x103 CAN_Filter_StdDataFrame(0, 0, 0x100); CAN_Filter_StdDataFrame(0, 1, 0x101); // FIFO1过滤器组:扩展帧ID 0x1000000-0x1000003 CAN_Filter_ExtDataFrame(1, 2, 0x1000000); CAN_Filter_ExtDataFrame(1, 3, 0x1000001); }

方案B:优先级分流策略

// 高优先级消息(标准帧)用FIFO0,普通消息(扩展帧)用FIFO1 void CAN_PriorityFilter_Init(void) { // FIFO0:关键控制指令(标准帧) CAN_Filter_StdDataFrame(0, 0, 0x01); // 紧急停止 CAN_Filter_StdDataFrame(0, 1, 0x02); // 状态查询 // FIFO1:普通数据(扩展帧) CAN_Filter_ExtDataFrame(1, 2, 0x10000001); // 传感器数据1 CAN_Filter_ExtDataFrame(1, 3, 0x10000002); // 传感器数据2 }

在汽车电子控制系统中,通常会采用混合帧设计:关键控制指令使用高优先级的标准帧,诊断和配置信息使用扩展帧。这种设计既保证了实时性,又满足了大数据量传输需求。

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

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

立即咨询