第3章 时钟配置与引脚映射:FDCAN的“心跳”与“手脚”

各位好,欢迎来到第三章。

上一章我们把FDCAN的硬件模块结构理清了。但模块本身不会跑,你得给它“心跳”——时钟,还得告诉它用哪只“手脚”去收发数据——引脚。这两件事搞不定,后面代码写得再漂亮也是白搭。

我个人习惯,做任何外设驱动,第一步永远是先看时钟树。为什么?因为时钟错了,时序全乱,你连调试的机会都没有。FDCAN对时钟的要求尤其严格,选错了源,波特率怎么配都配不准。

3.1 FDCAN时钟源选择:PLL1Q、PLL2Q还是HSE?

STM32的FDCAN模块,时钟源主要有三个选项:

时钟源 典型频率 特点 适用场景
HSE 8MHz / 25MHz(外部晶振) 精度高,但频率固定 对时间同步要求高的应用
PLL1Q 可编程(如 80MHz) 与系统时钟同源,灵活 大多数常规项目
PLL2Q 可编程(独立分频) 独立于系统总线时钟 需要FDCAN跑独立频率的场景

说白了,选哪个取决于你的系统时钟怎么配。我一般这么选:

  • 如果项目对CAN总线时间同步要求不高,直接用PLL1Q,省事。
  • 如果系统主频很高(比如400MHz),但FDCAN只需要跑40MHz,那就用PLL2Q单独分频,避免大分频比带来的抖动。
  • 如果外部有高精度晶振(比如25MHz),且需要做TTCAN或时间触发,那就直接上HSE。
注意: 不是所有STM32系列都支持这三个源。比如STM32G4系列,FDCAN时钟只能从HSE或PLL1Q选,没有PLL2Q。选型时一定要看参考手册的时钟树章节。

嗯,这里有个小细节。FDCAN的时钟频率,直接决定了你后面能配出的波特率。比如你想跑5Mbps的CAN FD,那FDCAN时钟至少得是40MHz(8个时间份额)。如果时钟只有20MHz,那5Mbps就别想了。

3.2 时钟配置实战:以STM32H743为例

我们拿STM32H743来演示。假设系统时钟配到400MHz,我想让FDCAN跑在40MHz。

第一步,打开时钟树,找到FDCAN的时钟源选择寄存器——RCC_D1CCIPR里的FDCANSEL位段。

// 选择 PLL1Q 作为 FDCAN 时钟源
RCC->D1CCIPR &= ~(RCC_D1CCIPR_FDCANSEL_Msk);
RCC->D1CCIPR |= (0x01 << RCC_D1CCIPR_FDCANSEL_Pos); // 01: PLL1Q

第二步,确保PLL1Q确实输出了40MHz。这需要在配置PLL时就规划好。

// 假设 HSE=25MHz, 主PLL配置:
// PLLM=25, PLLN=160, PLLP=2, PLLQ=4
// 则 PLL1Q = 25/25 * 160 / 4 = 40MHz
RCC->PLLCKSELR = RCC_PLLCKSELR_PLLSRC_HSE;   // 源选HSE
RCC->PLL1DIVR  = (25-1) | (160-1)<<8 | (4-1)<<24; // M, N, Q
RCC->CR       |= RCC_CR_PLL1ON;
while(!(RCC->CR & RCC_CR_PLL1RDY)); // 等待锁定
我的习惯: 配完时钟后,我会用示波器量一下FDCAN的TX引脚,看有没有时钟信号漏出来。虽然这不是标准做法,但能快速验证时钟是否真的起来了。

3.3 FDCAN引脚映射:TX和RX的“选位”艺术

时钟有了,接下来得告诉芯片,FDCAN的TX和RX信号走哪两个引脚出去。

STM32的引脚映射,说白了就是GPIO的复用功能选择。FDCAN的TX和RX,通常有多个可选引脚。比如STM32H743上,FDCAN1的TX可以是PB9、PD1、PH13等。

选引脚时,我一般遵循几个原则:

  • PCB走线优先: 选离CAN收发器最近的引脚,减少走线长度。
  • 避免冲突: 确保该引脚没有被其他外设占用(比如UART、SPI)。
  • 电平兼容: FDCAN的TX/RX是3.3V逻辑,如果收发器是5V的,注意电平转换。
避坑指南: 我曾经在一个项目里,选了PD1和PD0做FDCAN,结果发现PD0同时被SDMMC的CMD线占用了。调试了两天才发现是引脚冲突。所以,配引脚前,一定先看GPIOx_AFRL/AFRH寄存器,确认没有其他外设配置了同一个引脚。

3.4 GPIO配置为FDCAN复用功能

选好引脚后,就是GPIO的初始化了。这一步其实很固定,但细节容易出错。

以PB9(FDCAN1_TX)和PB8(FDCAN1_RX)为例:

// 使能GPIOB时钟
__HAL_RCC_GPIOB_CLK_ENABLE();

// 配置PB9为FDCAN1_TX (AF9)
GPIOB->MODER   &= ~(GPIO_MODER_MODE9_Msk);
GPIOB->MODER   |= (0x02 << GPIO_MODER_MODE9_Pos); // 复用功能
GPIOB->AFR[1]  &= ~(GPIO_AFRH_AFSEL9_Msk);
GPIOB->AFR[1]  |= (0x09 << GPIO_AFRH_AFSEL9_Pos); // AF9

// 配置PB8为FDCAN1_RX (AF9)
GPIOB->MODER   &= ~(GPIO_MODER_MODE8_Msk);
GPIOB->MODER   |= (0x02 << GPIO_MODER_MODE8_Pos);
GPIOB->AFR[1]  &= ~(GPIO_AFRH_AFSEL8_Msk);
GPIOB->AFR[1]  |= (0x09 << GPIO_AFRH_AFSEL8_Pos);

// 配置速度与上下拉(TX推挽,RX浮空或上拉)
GPIOB->OSPEEDR |= (0x03 << GPIO_OSPEEDR_OSPEED9_Pos); // 高速
GPIOB->PUPDR   &= ~(GPIO_PUPDR_PUPD9_Msk);            // TX无上下拉
GPIOB->PUPDR   &= ~(GPIO_PUPDR_PUPD8_Msk);
GPIOB->PUPDR   |= (0x01 << GPIO_PUPDR_PUPD8_Pos);     // RX上拉

这里有个关键点:TX引脚要配成推挽输出,RX引脚配成浮空输入或带上拉。为什么RX要上拉?因为CAN总线在隐性状态时,RX引脚应该是高电平。如果浮空,噪声可能让RX误判。

注意: 不同系列芯片,FDCAN的复用功能号不一样。H7系列是AF9,G4系列是AF9,F4系列可能没有FDCAN。一定要查数据手册的“Alternate function mapping”表。

3.5 知识结构总览

为了让你对整个流程有个直观印象,我画了张图。这张图把时钟源选择、引脚映射、GPIO配置串在了一起。

FDCAN 时钟与引脚配置流程 时钟源选择 HSE / PLL1Q / PLL2Q 时钟配置 RCC_D1CCIPR.FDCANSEL 引脚选择 TX / RX 引脚号 GPIO配置 MODER / AFR / OSPEEDR FDCAN 就绪 说明: 时钟配置和GPIO配置可以并行进行,但时钟必须先稳定,才能配置FDCAN的波特率寄存器。

3.6 常见问题与避坑

最后,分享几个我实际项目中踩过的坑:

  1. 时钟源没使能: 选了PLL1Q,但忘了把PLL1打开。FDCAN初始化时直接卡死。解决办法:配完时钟后,检查RCC_CR里的对应就绪位。
  2. 引脚复用号配错: 把AF9写成了AF8。TX引脚死活没波形。解决办法:养成查数据手册的习惯,别凭记忆写。
  3. RX引脚没上拉: 总线空闲时,RX引脚电平不确定,导致FDCAN误判总线状态。解决办法:RX引脚配成内部上拉,或者外部加10kΩ上拉电阻。
我的调试小技巧: 初始化完成后,先读一下FDCANx->CCCR寄存器的INIT位,确认FDCAN是否退出了初始化模式。如果一直卡在初始化状态,八成是时钟或引脚配置有问题。