STM32F1 CAN 标准帧与扩展帧实战:6种过滤器配置与收发测试代码

STM32F1 CAN 标准帧与扩展帧实战:6种过滤器配置与收发测试代码
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位IDE0标准帧RTR0数据帧 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); }配置要点标准帧ID左移5位对齐到bit15-bit5IDE位显性0表示标准帧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 IDE1 RTR0 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 硬件连接方案双机通信硬件连接需注意CAN_H和CAN_L需使用双绞线总线两端各接120Ω终端电阻推荐使用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/(132)/6 1MHz CAN_Init(CAN1, CAN_InitStruct); // 配置过滤器示例FIFO0接收标准数据帧ID0x123 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 i0; irx_msg.DLC; i) { printf(%02X , rx_msg.Data[i]); } printf(\n); } CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0); }4. 常见问题与调试技巧4.1 过滤器配置失败排查检查过滤器编号确保不重复使用同一过滤器编号验证ID对齐方式标准帧ID左移5位扩展帧ID左移3位确认FIFO分配过滤器必须明确指定到FIFO0或FIFO14.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 性能优化建议过滤器分组策略高优先级消息分配到FIFO0低优先级消息分配到FIFO1中断优先级设置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);总线负载监控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 }在汽车电子控制系统中通常会采用混合帧设计关键控制指令使用高优先级的标准帧诊断和配置信息使用扩展帧。这种设计既保证了实时性又满足了大数据量传输需求。