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
最佳实践
✅ 推荐做法
- 初始化前使能时钟
- 使用 BSRR 代替 ODR(原子操作)
- 中断中快速处理(置标志,主循环处理)
- 添加去抖动(按键)
- 使用 HAL 库提高可移植性
❌ 避免的错误
- 忘记使能时钟(最常见)
- 直接写 ODR 影响其他引脚
- 中断处理时间过长
- 忽略引脚电气特性
- 未配置上下拉导致浮空
调试技巧
问题排查
| 问题 | 可能原因 | 解决方法 |
|---|---|---|
| 引脚无输出 | 时钟未使能 | 检查 RCC 配置 |
| 电平不对 | 上下拉配置错误 | 检查 PUPDR |
| 速度不够 | 速度配置过低 | 调整 OSPEEDR |
| 中断不触发 | NVIC 未配置 | 检查中断优先级 |
测量工具
- 万用表:测量电平
- 示波器:观察波形
- 逻辑分析仪:多通道时序分析
结语
GPIO 是 STM32 编程的基础。掌握 GPIO 编程后,你可以控制 LED、读取按键、连接传感器,为学习更复杂的外设打下基础。
参考资料:STM32 参考手册、STM32CubeMX 用户指南、Arm Cortex-M 技术文档