STM32 GPIO 基础

GPIO(General Purpose Input/Output)是微控制器最基本的外设。STM32 的 GPIO 功能强大,支持多种模式和配置。

GPIO 引脚特性

  • 多种模式:输入、输出、复用、模拟
  • 速度配置:2MHz 到 200MHz+
  • 上下拉电阻:内置可配置
  • 驱动能力:可配置输出强度
  • 中断支持:外部中断/事件

GPIO 工作模式

1. 输入模式

模式说明应用
浮空输入无上拉下拉按键(外部有电阻)
上拉输入内置上拉电阻按键(默认高电平)
下拉输入内置下拉电阻按键(默认低电平)
模拟输入ADC 采集传感器、电位器

2. 输出模式

模式说明应用
推挽输出高低电平驱动LED、继电器
开漏输出需要上拉电阻I2C、电平转换

寄存器编程(裸机)

GPIO 寄存器

// GPIO 寄存器结构
typedef struct {
    volatile uint32_t MODER;    // 模式寄存器
    volatile uint32_t OTYPER;   // 输出类型
    volatile uint32_t OSPEEDR;  // 输出速度
    volatile uint32_t PUPDR;    // 上下拉
    volatile uint32_t IDR;      // 输入数据
    volatile uint32_t ODR;      // 输出数据
    volatile uint32_t BSRR;     // 置位/复位
    volatile uint32_t LCKR;     // 锁定
    volatile uint32_t AFR[2];   // 复用功能
} GPIO_TypeDef;

LED 控制示例

// 配置 PA5 为推挽输出
void gpio_init(void) {
    // 1. 使能 GPIOA 时钟
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    
    // 2. 配置 PA5 为输出模式 (01)
    GPIOA->MODER &= ~(3 << 10);
    GPIOA->MODER |= (1 << 10);
    
    // 3. 配置推挽输出
    GPIOA->OTYPER &= ~(1 << 5);
    
    // 4. 配置高速
    GPIOA->OSPEEDR |= (3 << 10);
    
    // 5. 无上下拉
    GPIOA->PUPDR &= ~(3 << 10);
}

// LED 开关
void led_on(void) {
    GPIOA->BSRR = (1 << 5);  // 置位
}

void led_off(void) {
    GPIOA->BSRR = (1 << 21); // 复位 (5+16=21)
}

void led_toggle(void) {
    GPIOA->ODR ^= (1 << 5);  // 翻转
}

HAL 库编程

初始化代码

#include "stm32f4xx_hal.h"

GPIO_InitTypeDef GPIO_InitStruct = {0};

void MX_GPIO_Init(void) {
    // GPIO 时钟使能
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    // 配置 PA5
    GPIO_InitStruct.Pin = GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

// 使用
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);   // 开
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); // 关
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);                // 翻转

GPIO 中断

外部中断配置

// 配置 PA0 为中断输入
void gpio_interrupt_init(void) {
    // 1. 使能时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_SYSCFG_CLK_ENABLE();
    __HAL_RCC_GPIO_EXTI_CLK_ENABLE();
    
    // 2. 配置 PA0 为上拉输入
    GPIO_InitStruct.Pin = GPIO_PIN_0;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // 3. 配置 NVIC
    HAL_NVIC_SetPriority(EXTI0_IRQn, 1, 0);
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}

// 中断处理函数
void EXTI0_IRQHandler(void) {
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}

// 回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    if (GPIO_Pin == GPIO_PIN_0) {
        // 处理中断
        led_toggle();
    }
}

高级功能

复用功能

GPIO 可映射到外设(UART、SPI、I2C 等):

// 配置 PA9/PA10 为 USART1_TX/RX
GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Alternate = GPIO_AF7_USART1;  // 复用功能 7
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

锁定功能

防止意外修改 GPIO 配置:

// 锁定 PA5 配置
GPIOA->LCKR = (1 << 16) | (1 << 5);  // LCKK + LCK5
GPIOA->LCKR = (1 << 5);              // LCK5
GPIOA->LCKR = (1 << 16) | (1 << 5);  // LCKK + LCK5

最佳实践

✅ 推荐做法

  1. 初始化前使能时钟
  2. 使用 BSRR 代替 ODR(原子操作)
  3. 中断中快速处理(置标志,主循环处理)
  4. 添加去抖动(按键)
  5. 使用 HAL 库提高可移植性

❌ 避免的错误

  1. 忘记使能时钟(最常见)
  2. 直接写 ODR 影响其他引脚
  3. 中断处理时间过长
  4. 忽略引脚电气特性
  5. 未配置上下拉导致浮空

调试技巧

问题排查

问题可能原因解决方法
引脚无输出时钟未使能检查 RCC 配置
电平不对上下拉配置错误检查 PUPDR
速度不够速度配置过低调整 OSPEEDR
中断不触发NVIC 未配置检查中断优先级

测量工具

  • 万用表:测量电平
  • 示波器:观察波形
  • 逻辑分析仪:多通道时序分析

结语

GPIO 是 STM32 编程的基础。掌握 GPIO 编程后,你可以控制 LED、读取按键、连接传感器,为学习更复杂的外设打下基础。


参考资料:STM32 参考手册、STM32CubeMX 用户指南、Arm Cortex-M 技术文档