复用功能(AF)概念:GPIO 的“第二人格”

GPIO 复用功能,说白了就是让一个引脚干两样活。默认情况下,引脚是普通的输入输出,用来点灯、读按键。但当你需要用 USART 发数据、用 SPI 读传感器、用 I2C 挂设备时,这个引脚就得切换到“第二人格”——也就是复用功能模式。

我刚开始学 STM32 时,总觉得复用功能很玄乎。后来调试一块板子,串口死活不工作,查了半天才发现是引脚模式配成了推挽输出,而不是复用推挽输出。嗯,从那以后我再也没犯过这个错。

什么是 GPIO 复用功能?

每个 GPIO 引脚在芯片内部都连接着多个外设的输入输出信号。比如 PA9,既可以做普通 GPIO,也可以连接到 USART1_TX,还可以做 TIM1_CH2。这些外设信号和 GPIO 之间通过一个多路选择器来切换,而这个选择器的控制权就在 AF 寄存器 手里。

核心要点:复用功能模式下,引脚的电平控制权从 GPIO 输出数据寄存器(ODR)移交给了外设模块。CPU 不再直接写引脚电平,而是由 USART、SPI、定时器等外设自动驱动。

AF 寄存器配置原理

STM32 的每个 GPIO 端口(如 GPIOA)有两组 AF 寄存器:AFRL(低 8 位引脚)和 AFRH(高 8 位引脚)。每个引脚占用 4 位,用来选择 0~15 共 16 种复用功能。

寄存器 覆盖引脚 每引脚位数 可选功能数
GPIOx_AFRL Pin 0 ~ Pin 7 4 bit AF0 ~ AF15
GPIOx_AFRH Pin 8 ~ Pin 15 4 bit AF0 ~ AF15

配置代码其实很简单,以 PA9 复用为 USART1_TX(AF7)为例:

// 使能 GPIOA 时钟
__HAL_RCC_GPIOA_CLK_ENABLE();

// 配置 PA9 为复用功能模式
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;    // 复用推挽输出
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Alternate = GPIO_AF7_USART1; // 选择 AF7
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

个人习惯:我每次配完 AF 寄存器后,都会读一下 AFRL/AFRH 寄存器确认写入值。曾经遇到过一次,代码里写的是 AF7,但实际读回来是 AF0——原因是时钟没使能,寄存器写不进去。

AF0 ~ AF15 映射表解读

不同 STM32 系列,AF 映射表略有差异。以 STM32F4 系列为例,常见的映射关系如下:

AF 编号 典型外设 说明
AF0 MCO、SWD/JTAG、RTC 系统功能,一般不动
AF1 TIM1、TIM2 定时器输入捕获/输出比较
AF2 TIM3~TIM5 通用定时器
AF3 TIM8~TIM11 高级/通用定时器
AF4 I2C1~I2C3 I2C 总线
AF5 SPI1、SPI2 SPI 总线
AF6 SPI3 SPI3
AF7 USART1~USART3 串口,最常用
AF8 UART4~UART5 串口
AF9 CAN1、CAN2、TIM12~TIM14 CAN 总线
AF10 OTG_FS、OTG_HS USB
AF11 ETH 以太网
AF12 FSMC、SDIO 外部存储器接口
AF13 DCMI 数字摄像头接口
AF14 保留 —
AF15 EVENTOUT 事件输出

避坑指南:我曾经在 F103 上直接把 F4 的 AF 表套用,结果 SPI 怎么都配不通。不同系列 AF 编号可能不同,比如 F1 系列没有 AF 寄存器,用的是重映射(REMAP)方式。一定要以你所用芯片的参考手册为准。

复用功能配置的完整流程

配置一个引脚为复用功能,我建议按这个顺序来:

  1. 查手册:确定你要用的外设信号映射到哪个引脚,以及对应的 AF 编号。
  2. 使能时钟:GPIO 时钟和外设时钟都要使能。
  3. 配置 GPIO 模式:设置为复用功能模式(AF_PP 或 AF_OD)。
  4. 设置 AF 寄存器:写入正确的 AF 编号。
  5. 配置外设:初始化外设本身(如 USART、SPI)。
  6. 验证:用逻辑分析仪或示波器看引脚波形。

你想想看,如果顺序搞反了——先配外设再配 GPIO,有些外设在初始化时会检测引脚状态,可能导致误触发。我习惯先配 GPIO 再配外设,这样更稳妥。

AF 映射的 SVG 结构图

下面这张图展示了 GPIO 引脚内部从外设信号到引脚输出的完整路径:

GPIO 复用功能内部结构图 USART1_TX SPI1_SCK I2C1_SCL TIM2_CH1 AF 多路选择器 AFRL[3:0] 或 AFRH[3:0] 控制 选择 AF0~AF15 PA9 引脚 AF 寄存器决定 哪个外设信号 连接到引脚

从图上可以看得很清楚:多个外设信号都连到了同一个引脚,但最终只有一路能通过 AF 多路选择器到达引脚。这个选择器就是 AFRL/AFRH 寄存器控制的 4 位开关。

常见配置错误与排查

我在项目里遇到过几次 AF 配置问题,总结下来就这几类:

  • AF 编号写错:比如 USART1 在 F4 上是 AF7,在 F7 上可能是 AF7 也可能是 AF8,一定要看 datasheet 的引脚复用表。
  • 模式配错:复用推挽输出(AF_PP)和复用开漏输出(AF_OD)搞混。I2C 必须用开漏,USART 用推挽。
  • 时钟遗漏:只开了 GPIO 时钟,没开外设时钟。这时候 AF 寄存器写不进去,引脚还是普通 GPIO。
  • 重映射冲突:有些引脚同时被多个外设占用,比如 PA9 既是 USART1_TX 也是 TIM1_CH2,如果两个外设都使能了,信号会打架。

调试技巧:我习惯在代码里加一段自检——配置完 AF 后,读取 AFRL 或 AFRH 寄存器,打印出来和预期值对比。如果值不对,先查时钟,再查代码逻辑。这招帮我省了不少时间。

复用功能配置其实不难,但细节多。只要把 AF 映射表记牢,配置流程走对,基本不会出大问题。记住:查手册、看时钟、配模式、写 AF、验结果,五步走稳,复用功能就稳了。