STM32 GPIO

版权声明:署名-非商业性使用-相同方式共享

@@ Tags: STM32 GPIO
@@ Date: 2026-01-28 1238
@@ Note: ~

概念

在STM32中,GPIO 中有两个重要的概念:

端口

  • 定义:STM32 将 GPIO 分成了很多个组, 每一组称为端口。端口可以看做是一个独立的GPIO外设模块
  • 命名:端口用大写字母表示,如 GPIOAGPIOBGPIOCGPIODGPIOH(具体数量取决于芯片型号)。

引脚

  • 定义:引脚是芯片物理上伸出的金属“针脚”,是与外部电路进行电气连接的实际接口。它是端口的具体成员。
  • 命名:由“端口字母 + 引脚编号”组成。例如:
    • PA1:表示 GPIOA端口第1号引脚
    • PC13:表示 GPIOC端口第13号引脚

电路结构

STM32-GPIO


引脚模式

输入模式

1.浮空输入

  • Floating Input / Input floating

  • 纯粹的数字输入。引脚电平完全由外部电路决定。

  • 内部既不上拉也不下拉。如果外部悬空,引脚电平将是不确定的,极易受电磁干扰影响而翻转。

  • 施密特触发器开启。

  • 典型应用: 连接外部已有确定驱动能力的信号,如另一个MCU的推挽输出、通信总线中主设备驱动的信号线(如UART_RX,当外部有上拉时)。

2.上拉输入

  • Input pull-up

  • 数字输入,内部通过一个约40kΩ的电阻连接到VDD。

  • 当外部无驱动时,引脚被默认拉至高电平

  • 需要读取低电平时,外部电路需提供足够的“灌电流”将电平拉低。

  • 典型应用: 按键、开关,一端接地,按下时引脚被拉低。空闲时因上拉而保持高电平。

3.下拉输入

  • Input pull-down
  • 数字输入,内部通过一个约40kΩ的电阻连接到VSS。
  • 当外部无驱动时,引脚被默认拉至低电平
  • 需要读取高电平时,外部电路需提供足够的“拉电流”将电平拉高。
  • 典型应用: 按键、开关,一端接VCC,按下时引脚被拉高。空闲时因下拉而保持低电平。

4.模拟输入

  • Analog Mode
  • 引脚作为ADC(模数转换器)DAC(数模转换器) 的通道。
  • 数字功能被完全禁用。施密特触发器关闭,上下拉电阻断开。引脚上的电压被直接送至模拟外设。
  • 功耗最低的输入模式,因为数字输入电路不工作。
  • 典型应用: 连接温度传感器、电位器、麦克风等模拟信号源。

输出模式

1.通用推挽输出

  • General-purpose output push-pull
  • 最常用的输出模式。MCU可以主动驱动引脚为高电平(通过P-MOS管连接VDD)或低电平(通过N-MOS管连接VSS)。
  • 输出能力强(STM32通常单引脚最大±20mA,具体看数据手册),高低电平由MCU的VDD和VSS决定。
  • 两个MOS管像“推”和“挽”一样交替工作,因此得名。
  • 典型应用: 驱动LED、继电器、蜂鸣器、电平控制等绝大多数需要数字输出的场景。

2.通用开漏输出

  • General-purpose output open-drain
  • 只能主动拉低(N-MOS管工作),不能主动拉高(P-MOS管被禁用)。高电平状态需要依赖外部上拉电阻连接到正电源。
  • 优点1:电平转换。外部上拉电阻可以接到一个不同于MCU VDD的电压上(如5V),实现与不同电压器件的通信。
  • 优点2:“线与”功能。多个开漏输出的引脚可以直接连在一起,只要有一个拉低,总线即为低;所有都释放,总线才被上拉为高。这是I²C总线的基础。
  • 缺点:驱动高电平时,电流由外部上拉电阻提供,因此上升沿速度较慢(RC充电)。
  • 典型应用: I²C、SMBUS总线,驱动高于MCU电压的器件,需要“线与”逻辑的场景。

3.复用功能推挽输出

  • Alternate function output push-pull
  • 引脚的控制权交给片上外设(如SPI、USART、TIM等),而非CPU或GPIO寄存器。输出特性与通用推挽完全相同
  • 例如,配置UART_TX引脚为此模式,数据将由USART外设自动产生并输出。
  • 典型应用: 所有需要由硬件外设驱动输出功能:SPI_SCK/MOSIUART_TXI2S_WS/CKTIM_PWM输出等。

4.复用功能开漏输出

  • Alternate function output open-drain
  • 引脚的控制权交给片上外设输出特性与通用开漏完全相同
  • 典型应用: 需要开漏特性的硬件外设输出,最经典的就是 I²C 的 SDA 和 SCL 线。I²C外设会自动管理开漏输出和输入检测。

输出速度配置

在配置输出模式(推挽/开漏,通用/复用)时,MODEx位还用于选择输出速度,这对于信号完整性和EMI至关重要。

  • 2 MHz(低速): 用于低速信号,功耗和噪声低。
  • 10 MHz(中速): 常用平衡选择。
  • 50 MHz(高速): 用于高速信号(如SPIUSB),但需注意过冲和振铃。
  • (新一代STM32有更高速度选项,如Very High)

GPIO 相关寄存器

STM32 的 GPIO 分成了多组端口,端口所含引脚的工作模式通过多个寄存器(端口寄存器组)来进行配置,寄存器中的对应位控制该端口中对应的引脚。

比如 STM32F411 分成了5组端口:GPIOAGPIOBGPIOCGPIODGPIOH。每组端口由10个32位的寄存器来控制。

注意:

  • 寄存器常量中的 x 代表具体的端口号, 如 GPIOAGPIOB 等。
  • 寄存器中低位对应低序号引脚, 如 GPIOA_MODER0~1 位对应 PA0, 30~31 位对应 PA15

GPIOx_MODER(模式寄存器)

控制引脚的工作模式,每个引脚占2位:

  • 00:输入模式(复位值)
  • 01:通用输出模式
  • 10:复用功能模式
  • 11:模拟模式(用于ADC/DAC)

GPIOx_OTYPER(输出类型寄存器)

控制输出类型(每个引脚占1位):

  • 0:推挽输出(复位值)
  • 1:开漏输出

GPIOx_OSPEEDR(输出速度寄存器)

控制输出驱动速度(每个引脚占2位):

  • 00:低速(复位值)
  • 01:中速
  • 10:高速
  • 11:超高速(具体频率见芯片手册)

GPIOx_PUPDR(上拉/下拉寄存器)

(Pull-up Pull-down Register) 控制内部上下拉(每个引脚占2位):

  • 00:无上下拉(复位值)
  • 01:上拉
  • 10:下拉
  • 11:保留

GPIOx_IDR(输入数据寄存器)

读取引脚输入状态(每个引脚占1位, 高16位保留, 只读):

  • 0:低电平
  • 1:高电平

GPIOx_ODR(输出数据寄存器)

控制引脚输出电平(每个引脚占1位, 高16位保留):

  • 0:输出低电平
  • 1:输出高电平

GPIOx_BSRR(置位/复位寄存器)

(Bit Set Reset Register) 原子操作设置/清除输出位:

  • BSy(00-15位):设置对应引脚为高电平(写1有效,写0无影响)
  • BRy(16-31位):设置对应引脚为低电平(写1有效,写0无影响)

注意:

GPIOx_ODRGPIOx_BSRR 的区别在于, 设置某引脚的输出电平, 前者需要 读->改->写 后者可以直接写。

GPIOx_LCKR(配置锁寄存器)

锁定引脚配置,防止意外修改。

GPIOx_AFRL和GPIOx_AFRH(复用功能寄存器)

(Alternate Function Register Low/High) 选择引脚的复用功能(每个引脚占4位),AFRL用于引脚0-7,AFRH用于引脚8-15。

通过寄存器的编程

通过查阅芯片的参考手册可知, GPIO 各端口寄存器的在内存地址空间中的具体地址, 因此我们可以直接通过指针访问这些寄存器。

#define GPIOA_MODER *(volatile unsigned int *)(0x40020000UL)

GPIOA_MODER = 0x00000003;       // 向 GPIOA_MODER 寄存器中写入 0x03 
unsigned int a = GPIOA_MODER;   // 读取GPIOA_MODER寄存器的内容,并存入变量a

另外我们还可以通过预定义的 C 结构体指针访问寄存器, 这种方式要比直接使用地址友好得多。

// stm32f411xe.h
typedef struct
{
  __IO uint32_t MODER;   
  __IO uint32_t OTYPER;  
  __IO uint32_t OSPEEDR; 
  __IO uint32_t PUPDR;   
  __IO uint32_t IDR;     
  __IO uint32_t ODR;     
  __IO uint32_t BSRR;    
  __IO uint32_t LCKR;    
  __IO uint32_t AFRL;    
  __IO uint32_t AFRH;    
} GPIO_TypeDef;

#define GPIOA ((GPIO_TypeDef *)0x40020000UL)

GPIOA->MODER &= ~(3<<(5*2));      // 清除模式寄存器的 bit11:bit10 为 00 
                                  // - 操作的是第5个引脚 (5)
                                  // - 每个引脚占2个bit (*2)
                                  // - 清除2个bit (3 即 0b11)
GPIOA->MODER |=   1<<(5*2);       // 设置 bit11:bit10 = 01, 引脚PA5为推挽输出
GPIOA->BSRR   =   1<<5;           // 设置 bit5 为 1,PA5 输出高电平,开启LD2
GPIOA->BSRR   =   1<(5+16);       // 设置 bit21 为1,PA5 输出低电平,关闭LD2

通过 HAL 库的方式编程

// stm32f4xx_hal_gpio.h
typedef struct
{
  uint32_t Pin;         // 选择需要配置的引脚
  uint32_t Mode;        // 设置所选引脚的工作模式
  uint32_t Pull;        // 使能所选引脚的上拉/下拉电阻
  uint32_t Speed;       // 设置所选引脚的输出速度
  uint32_t Alternate;   // 设置引脚的复用功能
} GPIO_InitTypeDef;

typedef enum
{
  GPIO_PIN_RESET = 0u,  // 引脚低电平
  GPIO_PIN_SET          // 引脚高电平
} GPIO_PinState;

#define GPIO_PIN_0                 ((uint16_t)0x0001)  /* Pin 0 selected    */
                                                       /* ..............    */
#define GPIO_PIN_15                ((uint16_t)0x8000)  /* Pin 15 selected   */
#define GPIO_PIN_All               ((uint16_t)0xFFFF)  /* All pins selected */

#define GPIO_PIN_MASK              0x0000FFFFu /* PIN mask for assert test */

// 配置引脚 PA5和 PA15:推挽输出,无上拉/下拉,输出速度为低速
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin   = GPIO_PIN_5 | GPIO_PIN_15;
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);

// PA15 引脚设置为低电平
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);

参考