1. GPIO 概述与硬件结构解析
GPIO(General Purpose Input/Output,通用输入输出端口)是单片机最基础的外设,任何按键输入、LED 控制、Sensor 传感器读取或通信总线(如 I2C、SPI 模拟)都建立在 GPIO 基础之上。
STM32 的每一个 GPIO 引脚内部结构都非常精密,支持独立配置:
GPIO 引脚结构包含以下核心部件:
保护二极管:防止引脚输入电压过高或过低损坏芯片内部电路(输入电压限制在 $V_{DD} + 0.3\text{V}$ 和 $V_{SS} - 0.3\text{V}$ 之间)。
上拉/下拉电阻:内部开关控制接上拉电阻(接 $V_{DD}$)或下拉电阻(接 $V_{SS}$)。
施密特触发器(Schmitt Trigger):将输入的模拟电平整形转换为逻辑数字信号 $0$ 或 $1$。
P-MOS 和 N-MOS 管:构成输出驱动电路,实现推挽或开漏输出。
2. GPIO 的 8 种工作模式详解
STM32 的 GPIO 模式可以通过软件配置为以下 8 种:
2.1 输入模式(Input)
输入浮空(GPIO_MODE_INPUT):
不接上拉也不接下拉电阻,引脚电平完全由外部输入决定。
适用场景:USART 接收引脚(RX)、外部按键(外部已有上下拉)。
输入上拉(GPIO_MODE_INPUT + Pull-up):
内部连接上拉电阻,默认无输入时保持高电平。
适用场景:低电平有效的按键检测。
输入下拉(GPIO_MODE_INPUT + Pull-down):
内部连接下拉电阻,默认无输入时保持低电平。
适用场景:高电平有效的按键检测。
模拟输入(GPIO_MODE_ANALOG):
关闭施密特触发器和数字通道,输入直接连到 ADC 转换器。
适用场景:ADC 电压采集、低功耗省电模式。
2.2 输出模式(Output)
开漏输出(GPIO_MODE_OUTPUT_OD):
上管 P-MOS 始终关闭,仅 N-MOS 导通。输出 $0$ 时接 GND,输出 $1$ 时呈现高阻态。
适用场景:需要线与逻辑的通信总线(如 I2C)、或者需要通过外部上拉驱动不同电压等级驱动(如 5V 逻辑)。
推挽输出(GPIO_MODE_OUTPUT_PP):
P-MOS 和 N-MOS 交替导通。输出 $1$ 时强拉高,输出 $0$ 时强拉低,驱动能力强(通常最大可达 20mA)。
适用场景:驱动 LED、蜂鸣器、开关管、SPI 时钟线等。
复用开漏(GPIO_MODE_AF_OD):
由片上外设(如 I2C)直接控制引脚,硬件自动切换开漏输出模式。
复用推挽(GPIO_MODE_AF_PP):
由片上外设(如 USART_TX、PWM、SPI_SCK)控制推挽输出。
3.完整代码实现(基于 STM32CubeMX + HAL 库)
3.1 STM32CubeMX 配置步骤
打开 CubeMX,选择
STM32F103C8T6。将
PC13引脚配置为GPIO_Output(默认 High,推挽模式)。将
PA0引脚配置为GPIO_Input,并在 GPIO 配置栏将GPIO Pull-up/Pull-down设置为Pull-up。在
SYS中将 Debug 设置为Serial Wire。生成工程并在 Keil 中打开。
3.2 C 语言代码实现 (main.c)
#include "main.h" /* 私有函数声明 */ void SystemClock_Config(void); static void MX_GPIO_Init(void); /** * @brief 应用程序入口 */ int main(void) { /* 复位所有外设,初始化 Flash 接口和 SysTick */ HAL_Init(); /* 配置系统时钟 (72MHz) */ SystemClock_Config(); /* 初始化所有已配置的 GPIO 引脚 */ MX_GPIO_Init(); /* 初始化 LED 状态:设置为高电平(灭) */ HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); /* 主循环 */ while (1) { /* 读取 PA0 引脚的电平状态 */ if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) { /* 软件消抖:延时 20ms */ HAL_Delay(20); /* 再次确认按键是否依然被按下 */ if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) { /* 翻转 PC13 引脚电平,实现 LED 亮灭切换 */ HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); /* 等待按键释放 (松手检测) */ while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) { // 空等待 } } } } }