拓冰建站拓冰建站
首页 / 资讯中心 / 正文

STM32C092 FDCAN重映射踩坑与配置详解:从GPIO到时钟源

1. 为什么我盯上了 STM32C092 的 FDCAN 重映射先交代一下背景。前阵子手头一个项目主控选了 STM32C092FCP6看重的是它价格低、封装小、还带了双 FDCAN做车载低速网关或者工业互联节点都够用。可问题恰恰出在这颗芯片的 FDCAN 引脚重映射上我按照老经验直接用 STM32CubeMX 把 FDCAN 从默认引脚挪到 PB 组结果烧进去之后总线死活没有 ACK示波器一抓TX 引脚压根没有波形。排查了两天最后发现是重映射配置里一个非常不起眼但致命的细节。这颗芯片不像 F103 那样有 AFIO 的MAPR寄存器也不完全像 H7/G4 那样靠SYSCFG而是把重映射逻辑藏在了FDCAN_TTTS相关的时钟和Alternate Function复用表里一个不留神就会踩坑。这篇文章就把我在 STM32C092FCP6 上做 FDCAN 重映射的全过程、踩坑记录和最终可用的配置方式整理出来。适合正在用 C0 系列做 FDCAN 通信或者打算从 F103/F072 迁移到 C0 平台的开发者参考。2. FDCAN 重映射的整体思路与方案选型2.1 先搞清楚 C0 系列和传统 F1/F0 重映射的区别STM32C0 系列从定位上说是 F0 的继任者内核还是 Cortex-M0但外设设计思路明显向 G4/H7 靠拢。尤其是 FDCAN它并不是简单地把 F0 的 bxCAN 改个名而是把 G4 的 FDCAN 模块整体搬了过来只是砍掉了一些时钟频率和缓存深度。这意味着什么意味着你做引脚重映射时不能再用 F103 时代“AFIO 拉一个SWJ_CFG或MAPR位就完事”的思路。C0 的每个引脚都有独立的AFR[0]/AFR[1]寄存器复用功能靠 AF 编号选择这就和 G4 一致。而 FDCAN 的外设时钟也不是挂在 APB1 上而是由独立的FDCAN_CLK提供这个时钟源选择又和RCC_CCIPR里的FDCANSEL位有关。我在拿到样片后第一件事就是翻参考手册 RM0490STM32C0 系列参考手册翻到 GPIO 复用表才发现FDCAN1 的 RX/TX 在 C092 上有三组可选引脚默认是 PA11/PA12第二组是 PB8/PB9第三组是 PB12/PB13。而 FDCAN2 也有两组。这一点很多人没注意因为 CubeMX 里的Pinout Configuration面板如果只拉 GPIO 不复用外设是看不到这些可选项的。2.2 为什么选择 PB8/PB9 而不是默认引脚我的项目 PCB 已经画完了PA11/PA12 被一个自定义的 LIN 类调试接口占用了PA11 同时还兼着 USB DP 检测虽然 C092 不带 USB 外设但引脚已经被别的信号占用。所以只能把 FDCAN1 挪到 PB8/PB9。这里有个很容易被忽略的坑C092 的 PB8/PB9 如果工作在 FDCAN 复用功能下会和 I2C1 的 SCL/SDA 冲突。如果你的代码里同时初始化了 I2C1 软件模拟或者硬件 I2C并且没有把 PB8/PB9 的复用功能正确设置成 FDCAN那么 I2C 的初始化代码可能会把这两个引脚重新拉成开漏模式导致 FDCAN 的 TX 根本无法正常输出推挽电平。我在排查时用逻辑分析仪抓 PB9 的电平发现它一直保持高电平偶尔有极低的毛刺这说明引脚被外部或内部别的配置影响而不是 FDCAN 模块没工作。2.3 重映射的技术路径对比做完调研我把可行路径列出来对比了一下方案做法优点缺点CubeMX 图形配置在 Pinout 里选择 FDCAN1勾选 PB8/PB9生成代码直观、不容易漏配置生成代码后仍需手动检查时钟源和 AF 值纯寄存器操作直接操作RCC_AHBENR、GPIOB_AFR[1]、FDCAN_CCCR等可控性强、适合 bootloader工作量大出错后排查时间长HAL 库手动初始化在HAL_FDCAN_MspInit里手动做 GPIO 和时钟配置代码可读性好、便于 Git 管理仍需理解底层寄存器我最终建议用 CubeMX 生成基础工程再手动核对关键寄存器而不是完全依赖 CubeMX。原因后面会详细讲。3. 核心细节解析AF 编号、时钟源和 FDCAN 初始化顺序3.1 Alternate Function 编号怎么确认STM32C092FCP6 的 GPIO 复用功能表里PB8 的 FDCAN1_RX 对应的是 AF3PB9 的 FDCAN1_TX 对应的是 AF3而 PA11/PA12 也是 AF3。这一点和 G4 系列有点不一样G4 的 FDCAN 某些引脚是 AF9所以如果你拿 G4 的代码直接改芯片型号很容易死在 AF 编号上。确认 AF 编号最稳妥的方法是查 RM0490 的 GPIO 复用功能表而不是靠猜。CubeMX 生成的代码里会看到类似这样的配置GPIO_InitStruct.Pin GPIO_PIN_8 | GPIO_PIN_9; GPIO_InitStruct.Mode GPIO_MODE_AF_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate GPIO_AF3_FDCAN1;这里GPIO_AF3_FDCAN1在 HAL 头文件里定义如果发现生成的代码里Alternate是 0那就要怀疑 CubeMX 版本对 C092 的支持不完整。我手头用的 CubeMX 是 6.10 以后才完整支持 C092如果你用的还是老版本建议先升级。3.2 时钟源FDCAN 的命门STM32C092 的 FDCAN 时钟源选择是另一个重映射后不出波形的核心原因。这颗芯片的主频最高 48MHz内部有 HSI48外部可以接 HSE。FDCAN 模块的时钟并不是直接拿SYSCLK而是通过RCC_CCIPR寄存器的FDCANSEL[1:0]位选择00PCLKAPB 时钟01SYSCLK10HSI4811HSE如果存在我踩的坑就在这里。CubeMX 默认生成的代码里FDCANSEL选择的是PCLK而 PCLK 在 C092 上默认是SYSCLK/1理论上也没问题。但如果你在SystemClock_Config里把 APB 分频系数设成了 2 甚至 4FDCAN 的时钟频率就会跟着降导致波特率配置算出来的 prescaler 完全不对。我实测发现FDCANSEL配成HSI48是最省心的因为 HSI48 是个独立的 48MHz 时钟不受 APB 分频影响。这样 FDCAN 的比特率配置只需要针对 48MHz 计算简单且稳定。配置代码RCC_PeriphCLKInitTypeDef PeriphClkInit {0}; PeriphClkInit.PeriphClockSelection RCC_PERIPHCLK_FDCAN; PeriphClkInit.FdcanClockSelection RCC_FDCANCLKSOURCE_HSI48; if (HAL_RCCEx_PeriphCLKConfig(PeriphClkInit) ! HAL_OK) { Error_Handler(); }注意 C0 系列的 HAL 库这个HAL_RCCEx_PeriphCLKConfig函数和 F4 不太一样参数类型略有差别但功能类似。编译的时候如果报FdcanClockSelection不是结构体成员说明你的 HAL 库版本太旧需要更新到支持 C0 的版本。3.3 初始化顺序先时钟后 GPIO 再 FDCAN很多人在重映射后 FDCAN 不工作其实和初始化顺序有关。我推荐的顺序是配置系统时钟HAL_RCC_ClockConfig配置外设时钟源HAL_RCCEx_PeriphCLKConfig设置 FDCANSEL初始化 FDCAN 引脚GPIO 时钟 AF 复用初始化 FDCAN 外设HAL_FDCAN_Init配置 FDCAN 滤波器启动 FDCANHAL_FDCAN_Start如果第 2 步放在第 4 步之后FDCAN 初始化时读到的时钟源还是旧值导致比特率配置对象里的NominalPrescaler计算错误。我一开始手写寄存器时就是先开的FDCAN_CCCR里的INIT位再配的时钟源结果 CRC 校验一直报错。4. 实操过程从 CubeMX 到最终跑通 FDCAN4.1 CubeMX 工程配置含关键选择我以 CubeMX 6.10 为例选择 STM32C092FCP6在Pinout Configuration里按以下步骤操作左侧Connectivity展开选择FDCAN1勾选Activate。在FDCAN1的Pinout视图里手动点击 PB8选择FDCAN1_RXAF3点击 PB9选择FDCAN1_TXAF3。如果 PB8/PB9 同时被 I2C1 占用把 I2C1 的Activate关掉或者迁移到别的引脚。左侧System Core下选择RCCHSE 设为Crystal/Ceramic Resonator或Bypass Clock根据你的硬件来。Clock Configuration里确认 FDCAN 时钟源我直接选HSI48将FDCANSEL设为HSI48。工程生成后在main.c的MX_FDCAN1_Init里检查hfdcan1.Init.ClockDivider如果是FDCAN_CLOCK_DIV1就没问题。CubeMX 有个坑如果你在 FDCAN 的Parameter Settings里把Protocol设成了FDCAN而Frame Format还是Classic CAN生成的代码里会默认把FDCAN帧格式关掉。你要确认自己的总线另一端是只支持 Classic CAN 还是也要支持 FD 帧比如只接一个 120Ω 终端电阻测试时如果对面是普通 CAN 收发器而不是 FDCAN 收发器而你把 FDCAN 扩展帧打开了就会导致通信失败。4.2 终端电阻的问题为什么只接 1 个 120Ω也能工作说一个很多人误解的点“FDCAN 总线只接 1 个 120Ω 终端电阻能不能工作”标准 CAN 总线要求在总线两端各接一个 120Ω 电阻等效并联后是 60Ω。但在实验室简单测试时只接一个 120Ω 电阻在总线一端另一端悬空通信依然可以工作。这背后是差分信号和隐性/显性电平的容忍度问题。CAN/FDCAN 物理层用的是差分电压显性时 CANH 拉高、CANL 拉低差分电压约 2V隐性时两端电压接近相同差分约 0V。终端电阻的主要作用不是“让信号反射消失”这么简单而是为总线提供直流负载同时抑制信号边缘反射。只接一个 120Ω 电阻时总线直流负载是 120Ω 而不是标准的 60Ω这会导致显性电平比标准稍低但只要收发器芯片的接收阈值还能正确识别通信照常。你如果用 PCAN 或者 USB-CAN 分析仪挂上去可能看到 ACK 正常、无错误帧但这不代表可以量产这只是测试手段。我这次调试时就是用一块 STM32C092 板子接了一个 120Ω 电阻另一头用 PCAN-USB 适配器同样也只接了一个 120Ω 电阻结果通信正常。所以如果你发现 FDCAN 通信不稳定先别急着怀疑“电阻少了一个”先确认波特率、采样点、终端电阻的总负载是否在收发器允许范围内。4.3 收发器选型对重映射的影响C092 的 FDCAN 引脚是 3.3V 逻辑电平不能直接挂到 CAN 总线上必须经过 CAN 收发器。我用的收发器是 TJA1042供电 5VTXD/RXD 逻辑电平兼容 3.3V 微控制器但需要注意 RXD 输出高电平的VOH是否超过 MCU 的容忍电压。TJA1042 的 RXD 高电平输出是 4V 左右5V 供电时如果 MCU 引脚不是 5V 容忍可能会通过内部钳位二极管漏电。C092 的 PB8 和 PB9 是否 5V 容忍我在数据手册里查的结果是C0 系列的 PB 组大部分引脚不是 5V 容忍而是 3.6V 耐压。所以你如果直接接 5V 供电的收发器RXD 高电平可能超压长期工作有风险。解决办法有两个在 MCU 和收发器之间串联 1kΩ 电阻限制电流RXD 高电平经过分压后落在 3.3V 附近。选用 3.3V 供电的收发器比如 TJA1051/3、SN65HVD230、MCP2562FD 等。我这次用的是 SN65HVD2303.3V 供电直接和 C092 电平匹配省事很多。调试时发现它的隐性电平为 2.5V显性时 CANH 3.5V、CANL 1.5V差分 2V符合标准。4.4 关键代码片段手动配置重映射后的引脚和滤波器CubeMX 生成的代码之外我手动增加了一段代码用于确保重映射生效和滤波器配置正确void FDCAN1_Remap_Check(void) { // 确保 GPIOB 时钟已使能 __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin GPIO_PIN_8 | GPIO_PIN_9; GPIO_InitStruct.Mode GPIO_MODE_AF_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate GPIO_AF3_FDCAN1; HAL_GPIO_Init(GPIOB, GPIO_InitStruct); }滤波器配置我用的是经典接收所有帧的方式方便调试FDCAN_FilterTypeDef sFilterConfig {0}; sFilterConfig.IdType FDCAN_STANDARD_ID; sFilterConfig.FilterIndex 0; sFilterConfig.FilterType FDCAN_FILTER_MASK; sFilterConfig.FilterConfig FDCAN_FILTER_TO_RXFIFO0; sFilterConfig.FilterID1 0x000; sFilterConfig.FilterID2 0x000; HAL_FDCAN_ConfigFilter(hfdcan1, sFilterConfig);注意FilterID2 0在掩码模式下表示“所有位都必须匹配 0”等价于只接收 ID 为 0 的帧。如果你要接收所有帧应该把FilterID2设为0x7FF标准帧 11 位全部不关心或者设置FilterType FDCAN_FILTER_RANGE并打开范围过滤。这里我实际用的是sFilterConfig.FilterID2 0x7FF;这样掩码为0x7FF表示不关心任何 ID 位匹配所有标准帧。这个细节坑了不少人因为 HAL 库的示例代码里经常是FilterID2 0直接抄会导致只能收到 ID 为 0 的帧。启动 FDCAN 接收中断HAL_FDCAN_Start(hfdcan1); HAL_FDCAN_ActivateNotification(hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0);中断回调函数void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs) { FDCAN_RxHeaderTypeDef RxHeader {0}; uint8_t RxData[8] {0}; if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, RxHeader, RxData) HAL_OK) { // 处理收到的报文 } }这是我调试时跑通的代码直接抄可以用但波特率参数要按你的总线环境改。5. 常见问题与排查技巧实录5.1 现象烧录后 TX 引脚无波形这是重映射后最常见的现象。排查步骤我按优先级整理如下先查 GPIO 复用是否真的设置为 AF3。在调试器里看GPIOB-AFR[1]的第 8/9 位也就是AFR8和AFR9值必须是0b0011AF3。如果显示0b0000说明 GPIO 初始化没生效或者在初始化之后被别的代码覆盖了。再查 FDCAN1 的时钟有没有使能RCC_AHBENR里的FDCAN1EN位是否为 1。查RCC_CCIPR的FDCANSEL位确认时钟源。如果你期望 HSI48但实际是 PCLK就可能导致波特率配置错误TX 仍然不会有正常波形因为 FDCAN 模块进入 BusOff 或初始化失败。查FDCAN_CCCR寄存器确认INIT位是否成功清零。如果一直为 1说明 FDCAN 初始化没完成可能是时钟源没选对或者波特率配置超限。5.2 现象TX 有波形但无 ACK当 TX 有波形却收不到 ACK说明发送方已经成功发送到总线但没有接收节点回应。常见原因总线上只有一个节点自发自收时必须开启LoopBack模式或者外部回环。FDCAN 模块自带 LoopBack 模式可以在没有外部收发器的情况下测试协议栈。终端电阻缺失导致电平不正确。注意我前面说的一个 120Ω 在测试场景下可以工作但不代表稳定长时间运行可能出现偶发错误帧。波特率不匹配。双方采样点不一致时如果相位裕量不足会导致 ACK 偶尔失败。使用了 FDCAN 帧格式但对端只支持 Classic CAN。这时候要把hfdcan1.Init.FrameFormat设为FDCAN_FRAME_CLASSIC或者用FDCAN_FRAME_FD_NO_BRS。检查 ACK 是否有问题可以直接在调试器里看FDCAN_PSR寄存器的LEC字段LEC 010表示 ACK 错误000表示无错误。这个比看波形还直观。5.3 现象FDCAN 初始化返回 HAL_ERROR初始化返回错误多半和HAL_FDCAN_Init里的初始化参数有关。我把检查列表写出来检查项对应代码常见错误时钟分频hfdcan1.Init.ClockDivider设成FDCAN_CLOCK_DIV10会导致比特率大幅降低数据段比特率hfdcan1.Init.DataBitTimePrescalerFD 帧模式下必须和仲裁段分开配置采样点hfdcan1.Init.SyncJumpWidthSJW 设太小会在总线上出现尖峰时失步帧格式hfdcan1.Init.FrameFormatFD 帧误配导致对端不响应自动重传hfdcan1.Init.AutoRetransmission关闭后发送失败不重传可能误判为总线错误5.4 现象切换引脚后CubeMX 生成的代码还是旧的引脚定义这个问题特别隐蔽。CubeMX 有时候会在.ioc文件里保留旧引脚的映射即使你在图形界面里改了引脚生成的 GPIO 初始化代码里还残留 PA11/PA12 的HAL_GPIO_Init。我遇到过两次解决办法是删掉工程里的main.c和fdcan.c重新生成。或者手动搜索PA11、PA12关键字删除残留代码。确认.ioc文件里FDCAN1_RX和FDCAN1_TX的引脚配置已经更新搜索PB8、PB9是否存在。5.5 避坑技巧总结根据这次调试我把几个容易踩的坑汇总一下不要盲目信任 CubeMX 对 C0 系列的引脚映射生成后一定打开fdcan.c看MX_FDCAN1_Init里的 GPIO 配置确认Alternate值是GPIO_AF3_FDCAN1而不是GPIO_AF0或 0。如果调试时 FDCAN 一直初始化失败优先检查时钟源选择用 HSI48 会比 PCLK 更稳定尤其是当 APB 分频系数不为 1 时。收发器的 RXD 输出高电平和 MCU 引脚耐压要匹配。C092 引脚不是 5V 容忍最好用 3.3V 收发器或者串电阻分压。FDCAN 的滤波器掩码模式默认FilterID2 0是“只匹配 ID 等于 FilterID1”不是“匹配所有 ID”。要匹配所有帧时FilterID2要设成0x7FF标准帧或0x1FFFFFFF扩展帧。调试时在FDCAN_PSR寄存器里看LEC错误码比反复量波形效率高得多。6. 实际测试结果与经验总结最终我在 C092FCP6 上跑通了 FDCAN1 重映射到 PB8/PB9和 PCAN 适配器通信波特率 500kbps经典 CAN 帧数据长度 8 字节收发 100 万帧无错误。测试时总线只接了远近端各一个 120Ω 电阻共 60Ω 等效负载终端匹配正常。如果只接一个 120Ω 电阻短距离1 米以内也能工作但我测试时发现总线长度超过 2 米后偶尔会出现 CRC 错误这印证了阻抗匹配对信号完整性的影响。所以实验室玩一玩可以只接一个项目上还是老老实实两个都上。用 FDCAN 收发器做自测时也可以直接开HAL_FDCAN_Start后在FDCAN_CCCR里把TEST位打开用LoopBack模式验证 MCU 内部数据链路这时候完全不需要外部收发器和总线电阻。这个芯片的 FDCAN 重映射本身并不复杂难点在于信息分散在参考手册的不同章节以及 CubeMX 生成代码的“陷阱”会误导你。如果你也遇到类似问题按我上面的排查顺序走一遍基本十分钟内能定位。最后再分享一个小技巧C092 的 FDCAN 内核是 Bosch M_CAN 的裁剪版它的FDCAN_TTTS寄存器名字看起来和 TTCAN 有关实际上部分位还兼职了内部测试模式的使能。如果你以后要做量产自检可以研究一下TEST模式下的外部回环比外部接线回环少很多物理层干扰定位问题更快。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门