“ GPIO 是 STM32 与外部交互的核心接口。本文以 STM32F407 为例,详解通用 IO 的模式分类、工作原理、寄存器配置及库函数用法,附 LED 控制实例,快速掌握 GPIO 配置与应用。”
01
—
简介
STM32F4xx 系列(含 STM32F407)最多提供 9 组通用 I/O 端口(GPIOA~GPIOH),总计最多 140 个可用 GPIO 引脚。每个引脚可独立配置为以下 4 种基本模式之一,并支持多种子模式:
|
模式大类 |
子模式 |
典型应用 |
|
输入 |
浮空、上拉、下拉、模拟 |
按键检测、ADC 输入 |
|
输出 |
推挽、开漏(带上下拉) |
LED、蜂鸣器、电平转换 |
|
复用 |
复用推挽/开漏 |
USART、SPI、I²C、PWM |
|
模拟 |
无数字功能 |
ADC/DAC 专用 |
关键硬件特性:
5V 电压容忍:所有 GPIO 可直接承受 5V 输入;
快速翻转:最快 2 个时钟周期完成电平翻转(168 MHz 下约 11.9 ns);
保护电路:内置钳位二极管、上/下拉电阻(30~50 kΩ);
复用灵活性:每个引脚最多支持 16 种复用功能(AF0~AF15)。
02
—
主要模式和功能
下图为stm32f4xx内部GPIO基本结构,主要包括输入驱动器、输出驱动器、输入/输出数据寄存器、复位/置位寄存器、内部上/下拉、内部保护二极管、I/O引脚等。程序中使用模式寄存器(GPIOx_MODER)、输出类型寄存器(GPIOx_OTYPER)、上拉/下拉寄存器(GPIOx_PUPDR)和复用功能寄存器(GPIOx_AFRL/GPIOx_AFRH)进行配置所有模式。
输入浮空
作为输入浮空时,内部上/下拉断开,若外部也未接上/下拉,则此引脚为高阻态,容易受到外界噪声和感应电压影响。输入浮空模式常用于电平检测(如按键输入电路),为了检测到准确的电平,需要外部接上/下拉。
输入上拉
I/O引脚内部相当于连接一个30KΩ~50KΩ的上拉电阻至VDD。若外部未接上/下拉,则I/O引脚会被拉到高电平;若外部接下拉电阻到地,则引脚被强制下拉到低电平(由于内部有上拉电阻,此时产生微弱电流从VDD经内部上拉、外部下拉至Vss,不会影响外部电平)。此模式常用于电平检测,相比于输入浮空模式,输入上拉模式适用于高电平有效的情况(默认输出低电平,使电路保持无效状态),且可省去外接上拉电阻,简化电路设计。
输入下拉
与输入上拉模式相反,默认输出高电平VDD,适用于低电平有效的情况。
模拟功能
模拟功能常用于模拟信号处理,包括模拟输入(如ADC)和模拟输出(如DAC),此模式下内部上下拉无效。作为模拟输入时,I/O引脚输入的模拟信号不经过施密特触发器直接连接到外设;作为模拟输出时,模拟电压直接输出到I/O引脚。
具有上拉或下拉功能的开漏输出
开漏输出功能:仅N-MOS管参与输出控制。当输出寄存器写入0时,N-MOS导通,I/O引脚被拉到Vss(低电平);当输出寄存器写入1 时, N-MOS截止, I/O引脚进入高阻态,此时电平由外部电路决定(内部上拉/下拉电阻的阻值(约 30–50 kΩ)无法提供足够的驱动电流来建立明确的高电平)。
上/下拉功能:当N-MOS截止时,内部上拉电阻将引脚拉到VDD(高电平),内部下拉电阻将引脚拉到Vss(低电平)。上拉:默认输出高电平(需外部电路拉低才能输出低电平)。下拉:默认输出低电平(需外部电路拉高才能输出高电平)。
此模式常用于I2C总线:开漏输出+上拉电阻实现“线与”功能,多设备共享总线时,任一设备拉低即可使总线为低。
具有上拉或下拉功能的推挽输出
推挽输出功能:由一对互补的P-MOS和N-MOS管组成。当输出寄存器写入1时,P-MOS导通、N-MOS关断 ,引脚直接连接VDD(高电平)。当输出寄存器写入0时 ,P-MOS关断、N-MOS导通,引脚直接连接VSS(低电平)。
上/下拉功能:在推挽输出模式下,内部上拉/下拉电阻仅影响引脚未配置为输出时的默认状态。若配置为上拉,引脚在空闲时会被拉高(防止悬空)。若配置为下拉,引脚在空闲时会被拉低。
推挽输出时,上下拉电阻不参与输出驱动,仅作为备用保护机制。
典型应用场景为LED驱动和按键检测。
具有上拉或下拉功能的复用功能推挽输出
复用功能:复用功能寄存器(GPIOx_AFRL/GPIOx_AFRH)这2个寄存器可将每个I/O引脚配置为某个复用功能,每个I/O引脚有16个复用功能可选,由于I/O引脚的复用功能存在共用情况(如PA9和PB6都可配置为USART1_TX,PA10和PB7都可配置为USART1_RX…….),因此为了避免I/O引脚与复用功能发生冲突,一个复用功能只能连接到一个I/O引脚。
例如,将USART1_TX连接到PA9引脚,配置方法如下:
// 配置GPIOGPIO_InitStruct.Pin = GPIO_PIN_9; // PA9GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽输出GPIO_InitStruct.Pull = GPIO_PULLUP; // 上拉(或GPIO_PULLDOWN)GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 速度50MHzGPIO_InitStruct.Alternate = GPIO_AF7_USART1; // 复用为USART1HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
推挽输出功能:同具有上拉或下拉功能的推挽输出模式。
上/下拉功能:同具有上拉或下拉功能的推挽输出模式。
具有上拉或下拉功能的复用功能开漏输出
复用功能:同具有上拉或下拉功能的复用功能推挽输出模式。
开漏输出功能:同具有上拉或下拉功能的开漏输出模式。
上/下拉功能:同具有上拉或下拉功能的开漏输出模式。
GPIO时钟
STM32F4xx 系列的所有通用 GPIO 端口(GPIOA ~ GPIOI)全部挂载在 AHB1 总线上,它们的时钟源完全相同,均由 RCC_AHB1ENR 寄存器统一控制。理论最大频率为168MHz(与HCLK相等),实际最高可配置为100MHz,但受限于信号完整性,建议不超过50MHz。可使用GPIO 端口输出速度寄存器 (GPIOx_OSPEEDR) 进行配置。
复用功能
可使用GPIO 复用功能低位寄存器 (GPIOx_AFRL)和GPIO 复用功能高位寄存器 (GPIOx_AFRH) 进行配置。
03
—
寄存器和库函数
1.寄存器介绍
STM32F4xx系列单片机GPIO共有10个寄存器,每个寄存器占用32位地址空间,可通过字节(8 位)、半字(16 位)或字(32 位)对 GPIO 寄存器进行访问。
|
序号 |
寄存器名称 |
有效位宽 |
功能 |
基地址 |
偏移地址 |
|
1 |
GPIOx_MODER(端口模式寄存器) |
32 bit |
设置引脚为输入(00)、输出(01)、复用功能(10)或模拟(11)。 |
GPIOx基地址 |
0x00 |
|
2 |
GPIOx_OTYPER(输出类型寄存器) |
低 16 bit 有效 |
0=推挽,1=开漏。 |
GPIOx基地址 |
0x04 |
|
3 |
GPIOx_OSPEEDR(输出速度寄存器) |
32 bit |
00=2 MHz,01=25 MHz,10=50 MHz,11=100 MHz。 |
GPIOx基地址 |
0x08 |
|
4 |
GPIOx_PUPDR(上拉/下拉寄存器) |
32 bit |
00=浮空,01=上拉,10=下拉,11=保留。 |
GPIOx基地址 |
0x0C |
|
5 |
GPIOx_IDR(输入数据寄存器) |
低 16 bit |
读取引脚当前电平状态 |
GPIOx基地址 |
0x10 |
|
6 |
GPIOx_ODR(输出数据寄存器) |
低 16 bit |
直接控制输出电平。 |
GPIOx基地址 |
0x14 |
|
7 |
GPIOx_BSRR(置位/复位寄存器) |
32 bit |
写 1 有效,写 0 无影响。 |
GPIOx基地址 |
0x18 |
|
8 |
GPIOx_AFRL (复用功能低位寄存器) |
32 bit |
选择 16 种复用功能 |
GPIOx基地址 |
0x20 |
|
9 |
GPIOx_AFRH(复用功能高位寄存器) |
32 bit |
选择 16 种复用功能 |
0x24 |
|
|
10 |
GPIOx_LCKR(配置锁定寄存器) |
17 bit |
锁定引脚的 MODER/OTYPER/OSPEEDR/PUPDR 配置,防止误改写。 |
GPIOx基地址 |
0x1C |
例如:GPIOA_BSRR 的地址 = GPIOA基地址(0x4002 0000) + 0x18 = 0x4002 0018。
2.GPIO常用库函数
|
序号 |
函数名称 |
功能 |
|
1 |
HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init) |
根据结构体参数配置引脚模式、输出类型、速度、上下拉功能,常用于普通GPIO功能初始化。 |
|
2 |
HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin) |
将指定引脚恢复为复位默认值。 |
|
3 |
HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState) |
设置引脚输出高/低电平。 |
|
4 |
HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin) |
翻转引脚电平。 |
|
5 |
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin) |
读取引脚当前输入电平,返回 GPIO_PIN_SET 或 GPIO_PIN_RESET。 |
|
6 |
HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init) |
配置引脚为复用推挽/开漏输出,并指定复用功能编号,常用于复用功能初始化。 |
|
7 |
HAL_GPIO_LockPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin) |
锁定引脚配置寄存器(MODER/OTYPER/OSPEEDR/PUPDR),防止误改写。 |
|
8 |
__HAL_RCC_GPIOx_CLK_ENABLE() |
使能 GPIO 端口时钟(宏,非函数,但必用)。 |
04
—
硬件设计说明
STM32 与 PC 通过 USB 转 TTL 模块连接,TX 接 RX,RX 接 TX,GND 共地。
05
—
程序说明
1.初始化流程
依次完成系统初始化、时钟配置、LED 控制引脚初始化和串口配置,为后续功能提供基础硬件环境。
2.交互逻辑
通过循环结构持续获取串口输入字符,回显确认后根据字符内容执行对应 LED 控制指令,形成完整的人机交互闭环。
3.状态控制
采用 switch-case 结构实现 8 种指令响应(7 种颜色 + 关闭)。
main.c
int main(void){ char ch; HAL_Init(); /* 配置系统时钟为168 MHz */ SystemClock_Config(); /* 初始化RGB彩灯 */ LED_GPIO_Config(); /*初始化USART 配置模式为 115200 8-N-1,中断接收*/ DEBUG_USART_Config(); /* 打印指令输入提示信息 */ Show_Message(); while(1) { /* 获取字符指令 */ ch=getchar(); printf("接收到字符:%c\n",ch); /* 根据字符指令控制RGB彩灯颜色 */ switch(ch) { case '1': LED_RED; break; case '2': LED_GREEN; break; case '3': LED_BLUE; break; case '4': LED_YELLOW; break; case '5': LED_PURPLE; break; case '6': LED_CYAN; break; case '7': LED_WHITE; break; case '8': LED_RGBOFF; break; default: /* 如果不是指定指令字符,打印提示信息 */ Show_Message(); break; } } }
bsp_led.c
#include "./led/bsp_led.h" /** * @brief 初始化控制LED的IO * @param 无 * @retval 无 */void LED_GPIO_Config(void){ /*定义一个GPIO_InitTypeDef类型的结构体*/ GPIO_InitTypeDef GPIO_InitStruct; /*开启LED相关的GPIO外设时钟*/ LED1_GPIO_CLK_ENABLE(); LED2_GPIO_CLK_ENABLE(); LED3_GPIO_CLK_ENABLE(); /*选择要控制的GPIO引脚*/ GPIO_InitStruct.Pin = LED1_PIN; /*设置引脚的输出类型为推挽输出*/ GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; /*设置引脚为上拉模式*/ GPIO_InitStruct.Pull = GPIO_PULLUP; /*设置引脚速率为高速 */ GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; /*调用库函数,使用上面配置的GPIO_InitStructure初始化GPIO*/ HAL_GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStruct); /*选择要控制的GPIO引脚*/ GPIO_InitStruct.Pin = LED2_PIN; HAL_GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStruct); /*选择要控制的GPIO引脚*/ GPIO_InitStruct.Pin = LED3_PIN; HAL_GPIO_Init(LED3_GPIO_PORT, &GPIO_InitStruct); /*关闭RGB灯*/ LED_RGBOFF; }
bsp_debug_usart.c
void DEBUG_USART_Config(void){ UartHandle.Instance = DEBUG_USART; UartHandle.Init.BaudRate = DEBUG_USART_BAUDRATE; UartHandle.Init.WordLength = UART_WORDLENGTH_8B; UartHandle.Init.StopBits = UART_STOPBITS_1; UartHandle.Init.Parity = UART_PARITY_NONE; UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE; UartHandle.Init.Mode = UART_MODE_TX_RX; HAL_UART_Init(&UartHandle); /*使能串口接收中断 */ __HAL_UART_ENABLE_IT(&UartHandle,UART_IT_RXNE); } /** * @brief UART MSP 初始化 * @param huart: UART handle * @retval 无 */void HAL_UART_MspInit(UART_HandleTypeDef *huart){ GPIO_InitTypeDef GPIO_InitStruct; DEBUG_USART_CLK_ENABLE(); DEBUG_USART_RX_GPIO_CLK_ENABLE(); DEBUG_USART_TX_GPIO_CLK_ENABLE(); /**USART1 GPIO Configuration PA9 ------> USART1_TX PA10 ------> USART1_RX */ /* 配置Tx引脚为复用功能 */ GPIO_InitStruct.Pin = DEBUG_USART_TX_PIN; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = DEBUG_USART_TX_AF; HAL_GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStruct); /* 配置Rx引脚为复用功能 */ GPIO_InitStruct.Pin = DEBUG_USART_RX_PIN; GPIO_InitStruct.Alternate = DEBUG_USART_RX_AF; HAL_GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStruct); HAL_NVIC_SetPriority(DEBUG_USART_IRQ ,0,1); //抢占优先级0,子优先级1 HAL_NVIC_EnableIRQ(DEBUG_USART_IRQ ); //使能USART1中断通道 } /***************** 发送字符串 **********************/void Usart_SendString(uint8_t *str){ unsigned int k=0; do { HAL_UART_Transmit(&UartHandle,(uint8_t *)(str + k) ,1,1000); k++; } while(*(str + k)!='\0'); }
06
—
测试结果
电脑端使用串口调试助手,选择电脑与STM32相连的COM口,设置为115200-N-8-1。下载程序后,电脑端发送字符到stm32,stm32会回显该字符到电脑端,同时STM32解析命令点亮RGB彩色灯。

07
—
代码链接
工程代码链接:
https://gitee.com/ylm1101111/stm32_basic3.git
0