1. GPIO 基础概念

GPIO(General Purpose Input/Output,通用输入输出)是 MCU 与外部世界交互最基本的数字接口。理解 GPIO 的引脚功能、输出模式、上下拉配置及电气特性,是编写可靠驱动代码的前提。

1.1 GPIO 引脚功能

MCU 的每个 GPIO 引脚通常可配置为以下三种功能之一:

  • 通用输入:读取外部数字电平(0 或 1),用于按键检测、传感器状态读取等。
  • 通用输出:驱动外部负载(LED、继电器、蜂鸣器),输出高电平或低电平。
  • 复用功能:引脚被分配给片内外设(如 UART、SPI、I2C、PWM、ADC),不再由 GPIO 模块直接控制。
驱动开发要点:在配置复用功能前,务必先关闭该引脚的 GPIO 输出驱动,否则可能造成信号冲突或芯片损坏。

1.2 推挽输出与开漏输出

GPIO 输出模式直接影响驱动能力、电平兼容性和功耗。两种主流输出模式对比如下:

特性 推挽输出(Push-Pull) 开漏输出(Open-Drain)
输出高电平 由 VDD 直接驱动,速度快 需外部上拉电阻,高电平由上拉电压决定
输出低电平 由 GND 直接驱动,灌电流能力强 由 GND 直接驱动,灌电流能力强
电平兼容性 固定为 MCU 供电电压 可通过上拉电阻匹配不同电压域(如 3.3V→5V)
多驱动冲突 多个输出直接并联会短路 支持线与(Wired-AND)逻辑,多个开漏输出可并联
典型应用 LED 驱动、普通数字信号输出 I²C 总线、电平转换、中断信号共享
常见错误:在 I²C 总线上错误配置为推挽输出,会导致总线锁定或器件损坏。I²C 必须使用开漏输出 + 外部上拉电阻。
推挽输出 vs 开漏输出结构对比 推挽输出(Push-Pull) PMOS NMOS 输出 VDD GND 开漏输出(Open-Drain) 上拉电阻 NMOS 输出 VDD(外部) GND

1.3 上拉与下拉电阻

当 GPIO 配置为输入模式时,引脚处于高阻态,外部无驱动时电平不确定。上拉/下拉电阻为引脚提供确定的默认电平:

  • 上拉电阻(Pull-Up):将引脚默认电平拉高到 VDD,外部驱动低电平时才拉低。常用于按键输入(按下为低)、开漏总线(I²C)。
  • 下拉电阻(Pull-Down):将引脚默认电平拉低到 GND,外部驱动高电平时才拉高。常用于按键输入(按下为高)、复位信号。
配置 默认电平 外部驱动高电平 外部驱动低电平 典型应用
上拉 高(VDD) 保持高 拉低 按键(按下低)、I²C SDA/SCL
下拉 低(GND) 拉高 保持低 按键(按下高)、复位输入
浮空(无上下拉) 不确定 高 低 外部有强驱动时使用
驱动代码建议:在初始化输入引脚时,始终显式配置上拉或下拉电阻,避免引脚浮空导致逻辑误判和额外功耗。
上拉与下拉电阻电路示意 上拉电阻配置 RPU GPIO VDD 按键 GND 下拉电阻配置 RPD GPIO GND 按键 VDD

1.4 GPIO 电气特性

驱动开发必须关注以下关键电气参数,否则可能导致信号错误或芯片损坏:

参数 符号 典型值(3.3V MCU) 说明
输入高电平阈值 VIH ≥ 0.7 × VDD(约 2.31V) 高于此值被识别为逻辑 1
输入低电平阈值 VIL ≤ 0.3 × VDD(约 0.99V) 低于此值被识别为逻辑 0
输出高电平 VOH VDD - 0.4V(约 2.9V) 输出高时带载后的实际电压
输出低电平 VOL 0.4V 输出低时带载后的实际电压
最大源电流 ISOURCE 8~20 mA(因 MCU 而异) 输出高时引脚能提供的最大电流
最大灌电流 ISINK 8~20 mA(因 MCU 而异) 输出低时引脚能吸收的最大电流
总电流限制 ITOTAL 100~200 mA(全芯片) 所有 GPIO 同时输出电流之和不能超过此值
硬件设计红线:直接驱动 LED 时务必串联限流电阻(通常 330Ω~1kΩ),否则超过最大源/灌电流会永久损坏 GPIO 引脚或 MCU。

1.5 GPIO 配置代码示例(基于 STM32 HAL)

/* 将 PA5 配置为推挽输出,驱动 LED */
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();

GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;      // 推挽输出
GPIO_InitStruct.Pull = GPIO_NOPULL;               // 输出模式通常无上下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;      // 低速即可
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

/* 将 PA0 配置为上拉输入,检测按键 */
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;           // 输入模式
GPIO_InitStruct.Pull = GPIO_PULLUP;               // 内部上拉
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

/* 读取按键状态 */
uint8_t key_state = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);

/* 输出控制 LED */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);   // 点亮
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); // 熄灭
驱动开发习惯:每次配置 GPIO 前先使能对应 GPIO 端口的时钟;配置完成后立即验证引脚电平,避免硬件初始化遗漏。