
简介本资源是面向嵌入式开发初学者与STM32进阶工程师的CAN双通道通信实践例程聚焦STM32F105芯片在工业现场总线中的典型应用——实现CAN1独立接收、CAN2独立发送的双CAN协同通信功能。资源包共201个文件涵盖59个头文件h用于寄存器定义与接口声明、54个源文件c含核心CAN初始化、中断服务、收发逻辑及TIM/EXTI等外设驱动辅以编译中间文件o/d/crf、工程配置uvproj/uvopt、调试输出axf/hex/map及备份文件bak完整呈现Keil MDK环境下基于标准外设库的开发全流程压缩包大小为2.12MB。已有984人学习下载适合通过实操掌握CAN位定时配置、邮箱管理、滤波器设置、错误处理及双CAN时钟同步等关键技能尤其有助于理解多CAN控制器在冗余通信、网关桥接等真实工业场景中的分工设计思路。1. STM32F105双CAN隔离通信CAN1只收、CAN2只发的硬核配置专治多节点干扰与回环误触发你有没有遇到过这种场景在某工业现场模拟项目X中STM32F105需要同时接入两个物理隔离的CAN网络——一个接传感器子网只允许接收状态帧另一个连执行器总线只允许下发控制指令。但默认配置下只要CAN1和CAN2共用同一套滤波器或未禁用自发自收就极易出现“自己发的帧被自己收”的黑匣子现象导致状态机错乱、控制指令重复执行。这个STM32f105 CAN1收 CAN2发.rar资源包就是为这类强约束场景量身定制的实战工程它不讲CAN协议理论直接交付一套已验证的、零回环风险的双CAN分工方案——CAN1硬件滤波锁定只收指定ID段CAN2彻底关闭接收中断并屏蔽所有RX FIFO连调试时的自发自收都从寄存器级掐断。适合正在做电机驱动板、PLC扩展模块或车载诊断桥接器的嵌入式工程师尤其当你手头只有F105非F103/F407且必须用原生双CAN外设时这份资源能省掉至少三天寄存器手册翻查示波器抓包排错时间。2. 硬件资源与初始化逻辑为什么必须用F105CAN1/CAN2的APB总线映射与时钟树真相2.1 F105双CAN外设的不可替代性APB1分频与重映射限制STM32F105属于互联型Connectivity line其CAN外设与F103标准型有本质差异F105的CAN1和CAN2均挂载在APB1总线上但CAN2的基地址为0x4000_6400而CAN1为0x4000_6000二者物理地址完全独立不存在F103上CAN2需通过CAN1重映射才能启用的玄学配置。更重要的是F105的RCC_APB1ENR寄存器中CAN1ENbit 25和CAN2ENbit 26是两个独立使能位可分别开关时钟——这是实现“CAN1纯接收、CAN2纯发送”的硬件前提。若强行在F103上移植此方案会因CAN2无独立时钟门控、且无独立RX/TX引脚复用选项导致无法真正隔离收发通道。// RCC时钟使能仅开启CAN1和CAN2各自时钟不牵连其他外设 RCC-APB1ENR | RCC_APB1ENR_CAN1EN; // 使能CAN1时钟APB1ENR[25] RCC-APB1ENR | RCC_APB1ENR_CAN2EN; // 使能CAN2时钟APB1ENR[26] // 注意此处绝不能写成 RCC-APB1ENR | 0x06000000; —— 位操作必须显式避免误开TIM7等无关外设提示F105的CAN2时钟使能位bit 26在部分旧版标准外设库如V3.5.0中未定义宏需手动补全#define RCC_APB1ENR_CAN2EN ((uint32_t)0x04000000)否则编译报错。2.2 引脚复用与电气隔离设计PA11/PA12 vs PB12/PB13的真实布线约束F105的CAN1默认复用引脚为PA11CAN1_RX和PA12CAN1_TX而CAN2默认为PB12CAN2_RX和PB13CAN2_TX。但实际PCB布局中必须将CAN1_RXPA11和CAN2_TXPB13置于同一侧原因在于该方案要求CAN1只收、CAN2只发因此CAN1的TX引脚PA12在原理图中应悬空或接10kΩ下拉防浮空干扰而CAN2的RX引脚PB12必须通过0Ω电阻断开或直接不焊接——这是物理层杜绝回环的第一道防线。很多翻车案例源于开发者未断开CAN2_RX硬件连接导致即使软件关闭接收中断总线电平变化仍会触发CAN2的错误状态标志ESR[BOFF]置位。引脚复用功能本方案状态关键操作PA11CAN1_RX必须连接接CAN收发器RX端加120Ω终端电阻PA12CAN1_TX物理断开PCB上取消走线或贴片0Ω电阻改为NCPB12CAN2_RX物理断开原理图中标注“DNP”PCB不布线PB13CAN2_TX必须连接接CAN收发器TX端加120Ω终端电阻2.3 初始化流程从RCC到CAN寄存器的七步硬核配置初始化不是简单调用CAN_Init()而是按硬件依赖顺序逐级解锁开启GPIO时钟RCC_APB2PeriphClockCmd(RCC_APB2PERIPH_GPIOA | RCC_APB2PERIPH_GPIOB, ENABLE);配置复用推挽输出PA12/PB13设为GPIO_Mode_AF_PPPA11/PB12设为GPIO_Mode_IPU上拉输入适配CAN_RX高阻态开启CAN时钟如2.1节所示独立使能CAN1/CAN2退出睡眠模式CAN1-MCR ~CAN_MCR_SLEEP; CAN2-MCR ~CAN_MCR_SLEEP;进入初始化模式CAN1-MCR | CAN_MCR_INRQ; CAN2-MCR | CAN_MCR_INRQ;等待INAK标志置位配置波特率与工作模式重点设置CAN_BTR寄存器F105需严格匹配采样点本方案固定为87.5%退出初始化模式CAN1-MCR ~CAN_MCR_INRQ; CAN2-MCR ~CAN_MCR_INRQ;等待INAK0// CAN1波特率配置500kbpsSJW1tqBS16tqBS27tqBRP2 → 72MHz/(2*(167)) 514.3kbps实测容错 CAN1-BTR (1 CAN_BTR_SJW_Pos) | // SJW 1 (6 CAN_BTR_TS1_Pos) | // TS1 6 (BS1) (7 CAN_BTR_TS2_Pos) | // TS2 7 (BS2) (2 CAN_BTR_BRP_Pos); // BRP 2 → 分频系数 // CAN2同理但BS1/BS2可微调以补偿PCB走线差异如CAN2设BS15, BS28参数说明TS1Time Segment 1包含PROP_SEG和PHASE_SEG1TS2Time Segment 2即PHASE_SEG2。F105的CAN控制器要求TS1TS2 ≥ 2且TS1 ≤ 16本方案取6713确保采样点落在边沿后稳定区。BRP值由APB1频率36MHz和目标波特率反推非整数时取最接近值并实测验证。3. CAN1纯接收实现过滤器组分配与FIFO深度控制的精准拿捏3.1 过滤器组Filter Bank的独占式绑定为何必须用Bank 0给CAN1F105共有14个过滤器组Bank 0~13但CAN1和CAN2共享这些资源。关键规则是每个过滤器组只能分配给CAN1或CAN2中的一个不可共用。本方案将Bank 0~7全部绑定给CAN1Bank 8~13分配给CAN2尽管CAN2不收仍需占位防冲突。这样做的好处是CAN1可配置8个独立标准ID过滤如0x101, 0x102...0x108且每个Bank支持双16位模式能精确匹配IDRTRIDE字段。// 绑定Bank 0~7给CAN1CAN_FMR寄存器配置 CAN1-FMR | CAN_FMR_FINIT; // 进入过滤器初始化模式 CAN1-FM1R 0x00000000; // 全部设为标识符掩码模式32-bit CAN1-FS1R 0x000000FF; // Bank 0~7设为单个32-bit过滤器非双16-bit CAN1-FA1R 0x000000FF; // 启用Bank 0~7 CAN1-FMR ~CAN_FMR_FINIT; // 退出初始化模式逻辑说明FA1RFilter Active Register的bit0~bit7置1表示启用对应Bank。若此处误写FA1R 0xFF000000高位启用则Bank 0~7实际未激活CAN1将接收所有帧——这是新手最常踩的坑。3.2 FIFO0的深度锁定与溢出防护只收不处理的底层保障CAN1仅接收、不主动读取FIFO因此必须防止FIFO满导致丢帧。本方案将FIFO0深度设为3最小值并启用FIFO满中断CAN_IER_FOVIE0在中断中强制清空FIFO而非处理数据——因为业务逻辑只需知道“有新帧到达”具体解析由主循环轮询CAN_RF0R[FMP0]完成。// 配置CAN1 FIFO0深度3启用满中断 CAN1-RF0R CAN_RF0R_FWM; // FIFO0水位设为满3帧 CAN1-IER | CAN_IER_FOVIE0; // 使能FIFO0溢出中断 // 中断服务函数精简版 void USB_LP_CAN1_RX0_IRQHandler(void) { if (CAN1-RF0R CAN_RF0R_FOVR0) { // 检测到溢出 CAN1-RF0R | CAN_RF0R_RFOM0; // 清除溢出标志 CAN1-RF0R | CAN_RF0R_RFD0; // 强制释放FIFO0所有帧丢弃 } }参数说明FWMFIFO Watermark位决定FIFO满阈值。F105的FIFO深度可设为1/2/3帧设为3时FWM1若设为1帧则频繁进中断影响实时性。RFD0Release FIFO Descriptor是关键指令不执行此操作FIFO指针不会归位。3.3 标准ID过滤器配置四字节掩码模式下的精准拦截本方案采用32位标识符列表模式FM1R0每个Bank配置一个标准ID11位。例如只接收ID为0x105的帧// Bank 0配置只收0x105标准帧非扩展帧 CAN1-sFilterRegister[0].FR1 0x01050000; // ID0x105, RTR0, IDE0 → 低16位: 0x0105, 高16位: 0x0000 CAN1-sFilterRegister[0].FR2 0x00000000; // 掩码全0 → 精确匹配 // 注意FR1的bit31:16为ID[10:0]RTRIDEbit15:0为保留位必须清零血泪经验若FR1高16位未清零如写成0x0105FFFF会导致ID高位被截断实际匹配ID变为0x0105 0xFFFF 0x0105但RTR/IDE位异常可能误收远程帧。务必用 0xFFFF0000掩码清理。4. CAN2纯发送实现寄存器级关闭接收通路与TX邮箱抢占策略4.1 彻底禁用CAN2接收从时钟门控到FIFO的三重封锁“只发不收”不是关闭中断那么简单而是要从硬件层面切断接收路径步骤1关闭CAN2接收时钟错F105的CAN2时钟是整体使能的无法单独关RX时钟。步骤2禁用所有RX FIFOCAN2-RF0R 0; CAN2-RF1R 0;清空FIFO控制寄存器步骤3屏蔽RX中断CAN2-IER ~(CAN_IER_FMPIE0 | CAN_IER_FMPIE1 | CAN_IER_FOVIE0 | CAN_IER_FOVIE1);步骤4关键一步——清除接收使能位CAN2-MCR | CAN_MCR_NART;禁止自动重传CAN2-MCR | CAN_MCR_RFLM;接收FIFO锁定→ 但最有效的是直接写0到CAN2的RX引脚复用寄存器见2.2节物理断开。// 软件加固强制CAN2进入“只发”状态 CAN2-MCR | CAN_MCR_NART; // 禁止自动重传避免TX失败后反复尝试 CAN2-MCR | CAN_MCR_RFLM; // 接收FIFO锁定FIFO不更新但物理引脚已断开更可靠 CAN2-IER ~CAN_IER_TMEIE; // 禁用TX邮箱空中断本方案用轮询无需中断逻辑说明NARTNo Automatic Retransmission位至关重要。若CAN2因总线忙或ACK错误导致TX失败硬件会自动重发——而重发帧可能被CAN1误收若物理未隔离。设NART1后TX失败直接返回错误码由软件决定是否重发。4.2 TX邮箱抢占与优先级控制如何确保控制指令零延迟发出F105有3个TX邮箱Mailbox 0/1/2本方案固定使用Mailbox 0发送并通过CAN_TSR寄存器轮询其状态。关键技巧是在发送前检查TME0Transmit Mailbox Empty位而非等待RQCP0Request Completed因为后者是发送完成标志而TME0表示邮箱空闲可写——这能将指令发出延迟压缩到1个APB周期。// 发送一帧标准帧ID0x201, Data[0x01,0x02,0x03,0x04] CanTxMsg TxMessage; TxMessage.StdId 0x201; TxMessage.ExtId 0x00; TxMessage.RTR CAN_RTR_DATA; TxMessage.IDE CAN_ID_STD; TxMessage.DLC 4; TxMessage.Data[0] 0x01; TxMessage.Data[1] 0x02; TxMessage.Data[2] 0x03; TxMessage.Data[3] 0x04; // 轮询邮箱0空闲非阻塞 while (!(CAN2-TSR CAN_TSR_TME0)); // 等待TME0置位 CAN2-sTxMailBox[0].TIR (TxMessage.StdId 21) | (TxMessage.RTR 1) | TxMessage.IDE; // 构造TIR寄存器 CAN2-sTxMailBox[0].TDTR TxMessage.DLC; CAN2-sTxMailBox[0].TDLR (TxMessage.Data[0]) | (TxMessage.Data[1] 8) | (TxMessage.Data[2] 16) | (TxMessage.Data[3] 24); CAN2-sTxMailBox[0].TDHR 0x00000000; // 高4字节清零 CAN2-sTxMailBox[0].TIR | CAN_TI0R_TXRQ; // 触发发送参数说明TIRTransmit Identification Register的bit28:16为标准IDbit1为RTR位bit0为IDE位。TDLRTransmit Data Low Register存放Data[0]~Data[3]TDHRTransmit Data High Register在标准帧中必须清零。若TDHR残留旧值可能导致Data[4]~Data[7]被误发。4.3 错误状态监控为什么必须定期读取CAN2的ESR寄存器即使CAN2不收其错误状态寄存器ESR仍会反映总线健康度。若ESR[BOFF]Bus-Off Flag置位说明CAN2因连续发送错误进入Bus-Off状态此时所有TX邮箱将被锁定TME0永远不置位。必须检测并软复位// 定期调用如主循环每10ms if (CAN2-ESR CAN_ESR_BOFF) { CAN2-MCR | CAN_MCR_RESET; // 软复位CAN2 while (CAN2-MSR CAN_MSR_INAK); // 等待初始化完成 CAN2-MCR ~CAN_MCR_RESET; // 重新初始化波特率、过滤器可复用初始化函数 }注意RESET位是自清零的写1后硬件自动清零。若未等待INAK0就继续配置寄存器写入无效。5. 避坑指南五个真实翻车现场与血泪修复方案5.1 现象CAN1接收正常但CAN2发送后CAN1立即收到相同ID帧原因PCB上CAN2_RXPB12未物理断开且CAN2的RFLM位未设置导致CAN2硬件仍监听总线其TX帧被自身RX电路捕获再经内部总线转发至CAN1。解决① 原理图中将PB12标注“DNP”PCB不布线② 代码中CAN2-MCR | CAN_MCR_RFLM;③ 用万用表确认PB12对地电阻 1MΩ。5.2 现象CAN1 FIFO0频繁溢出FOVR0标志常置位原因FWMFIFO Watermark配置错误。F105的FIFO深度为3帧时FWM应设为1CAN_RF0R_FWM但开发者误用F103的配置逻辑设为0导致水位阈值为1帧轻微流量即溢出。解决检查CAN1-RF0R值确保FWM位为1若需更高吞吐改用FIFO1并设深度为3同时启用FOVIE1。5.3 现象CAN2发送失败TSR[TC0]0且ESR[EPVF]1Error Passive原因CAN2的波特率配置与总线其他节点不一致或终端电阻缺失导致信号反射。F105的CAN控制器对采样点敏感若BTR中TS1TS2之和偏离推荐值13~16易在错误被动状态下卡死。解决① 用示波器测量CANH-CANL差分电压确认显性电平 2.5V② 重算BTRBRP (APB1Freq / (BaudRate * (1TS1TS2))) - 1取整后验证③ 在CAN2_TX引脚串联33Ω电阻抑制振铃。5.4 现象系统运行数小时后CAN1停止接收MSR[INAK]1初始化模式未退出原因CAN1在接收过程中遭遇总线严重错误如长时间显性电平硬件自动进入初始化模式但软件未检测MSR[INAK]并重置。F105的CAN控制器在此状态下不响应任何RX请求。解决在主循环中添加检测if (CAN1-MSR CAN_MSR_INAK) { CAN1-MCR | CAN_MCR_RESET; while (CAN1-MSR CAN_MSR_INAK); CAN1-MCR ~CAN_MCR_RESET; // 重新加载BTR、启用过滤器 }5.5 现象使用ST-Link调试时CAN1接收中断偶尔丢失原因SWD调试接口与CAN1共用PA13/PA14引脚当调试器频繁读取内存时PA13SWDIO的电平波动耦合至PA11CAN1_RX引发误触发。解决① 调试时将CAN1_RX改用重映射引脚如PD0需开启AFIO-PCRCR | AFIO_PCRCR_PA11_REMAP;② 或在CAN1-IER中临时禁用FMPIE0改用主循环轮询RF0R[FMP0]。6. 实时性验证与抗扰增强用示波器抓取TX-RX时序差把抖动压到1.2μs内6.1 时序验证方法用CH1触发CAN2_TXCH2捕获CAN1_RX测端到端延迟真正的双CAN隔离效果必须用示波器量化。将CH1探头接CAN2_TX引脚非总线CH2接CAN1_RX引脚设置CH1为触发源上升沿触发。发送一帧ID0x201的帧观察CH2上对应ID帧的起始边沿。理想情况下由于物理隔离CH2应无信号若存在微弱耦合测量CH1上升沿到CH2上升沿的时间差——本方案实测值为0.8~1.2μs受PCB走线长度影响远低于CAN位时间2μs500kbps证明无有效数据回环。若测得5μs说明PCB上CAN1_RX与CAN2_TX平行走线过长需加地线隔离。6.2 抗扰增强技巧在CAN2_TX输出端增加RC低通滤波为抑制高频噪声干扰CAN1_RX可在CAN2_TX引脚后串联一个π型滤波器CAN2_TX → 33Ω → 节点A → 100pF → GND节点A → 100pF → GND。此结构将30MHz噪声衰减30dB且不影响CAN信号边沿上升时间仍200ns。实测在电机启停瞬间CAN1的误帧率从12%降至0.3%。6.3 固件升级安全机制双CAN协同实现Bootloader握手本方案的进阶价值在于支撑安全升级。例如CAN1接收来自上位机的固件包ID0x301校验通过后CAN2向Bootloader节点ID0x400发送启动指令Data[0]0xAA。关键在于CAN2发送指令后必须等待CAN1收到Bootloader的ACKID0x401才继续下一步。这要求CAN1的FIFO0深度足够缓存ACK帧且主循环中严格按“CAN1收ACK → CAN2发下一帧”顺序执行避免指令堆积。步骤CAN1动作CAN2动作超时处理1接收固件包ID0x301休眠若5s未收跳转失败2校验通过后置位boot_ready标志发送启动指令ID0x400若3s未收ACK重发指令最多3次3接收ACKID0x401休眠若ACK中Data[0]!0x55终止升级从那以后我每次做双CAN项目都强制走一遍“物理断开→寄存器封锁→示波器抓波”三步验证哪怕客户说“先跑通功能”。因为CAN总线的bug往往在量产半年后才爆发而示波器上的1.2μs抖动就是你未来半夜被电话叫醒的伏笔。希望帮到你。本文还有配套的精品资源点击获取