100 篇文章精通 STM32F103(第 2 篇):GPIO 深度解析与按键检测实操

100 篇文章精通 STM32F103(第 2 篇):GPIO 深度解析与按键检测实操

大家好!上一篇我们通过 “点亮 LED” 迈出了 STM32 学习的第一步,而控制 LED 的核心就是GPIO(通用输入输出口)。这一篇我们将深入 GPIO 的工作原理,掌握它的 8 种工作模式,并通过 “按键检测” 实操(输入模式)巩固知识,让你不仅 “会用” GPIO,更 “懂为什么这么用”。

一、GPIO 是什么?为什么它是 STM32 的 “手脚”?

GPIO(General Purpose Input/Output,通用输入输出口)是 STM32 与外部世界交互的 “桥梁”—— 它既能输出电信号(比如控制 LED 亮灭、驱动电机),也能读取外部电信号(比如检测按键是否按下、读取传感器数据)。

STM32F103 的 GPIO 分为多个端口(GPIOA、GPIOB...GPIOG),每个端口有 16 个引脚(Pin0-Pin15),不同型号的芯片引脚数量不同(如 STM32F103C8T6 有 GPIOA、GPIOB、GPIOC 共 3 个端口,37 个可用 GPIO 引脚)。

简单说:GPIO 就是 STM32 的 “手脚”—— 用 “手”(输入)接收外部信息,用 “脚”(输出)对外发出指令。

二、GPIO 硬件结构:从引脚到寄存器的 “信号通路”

要理解 GPIO 的工作模式,先得看懂它的硬件结构(简化版):

 

外部信号 → 引脚 → 保护二极管(防过压) → 上下拉电阻 → 输入缓冲器 → 内部寄存器(IDR)
                                 ↑
内部寄存器(ODR) → 输出控制器(推挽/开漏) → 引脚 → 外部设备

关键组件作用:

  • 保护二极管:当引脚电压过高(>VDD+0.3V)或过低(<GND-0.3V)时导通,保护芯片内部电路;
  • 上下拉电阻:默认情况下,未接外部信号的 GPIO 引脚电平是 “浮空” 的(不稳定,可能乱跳),上下拉电阻可将其稳定在高电平(上拉)或低电平(下拉);
  • 输入缓冲器:将外部模拟信号转为数字信号(0 或 1),存入输入数据寄存器(IDR);
  • 输出控制器:根据输出数据寄存器(ODR)的指令,控制引脚输出高 / 低电平,支持推挽或开漏两种模式。

三、GPIO 的 8 种工作模式:一文看懂 “输入 / 输出 / 复用 / 模拟”

STM32 的 GPIO 有 8 种工作模式,可分为4 类输入模式4 类输出模式(含复用功能),不同模式对应不同应用场景。

1. 4 种输入模式(“接收外部信号” 用)

模式名称核心特点典型应用
输入浮空(Floating input)引脚电平完全由外部信号决定,无上下拉,易受干扰需外部电路提供稳定电平的场景(如连接按键时外部接上下拉电阻)
输入上拉(Input pull-up)内部接 30-50kΩ 上拉电阻到 VDD,未接外部信号时默认高电平按键检测(外部按下时接地,电平变低)
输入下拉(Input pull-down)内部接 30-50kΩ 下拉电阻到 GND,未接外部信号时默认低电平同上(外部按下时接 VDD,电平变高)
模拟输入(Analog input)跳过输入缓冲器,直接将引脚信号送入 ADC(模数转换器)读取模拟信号(如光敏电阻、温度传感器的电压值)

关键区别:前 3 种是 “数字输入”(只能读 0 或 1),最后 1 种是 “模拟输入”(可读连续电压值,配合 ADC 使用)。

2. 4 种输出模式(“对外发信号” 用)

模式名称核心特点典型应用
推挽输出(Output push-pull)可直接输出高 / 低电平(高电平 = VDD,低电平 = GND),驱动能力强控制 LED、继电器、直接驱动小负载
开漏输出(Output open-drain)只能输出低电平(接地),高电平需外部上拉电阻提供,可实现 “线与” 功能I2C 通信(SDA/SCL 线)、多个设备共用一根线
复用推挽输出(Alternate function push-pull)引脚功能被外设(如 UART 的 TX)占用,输出由外设控制,推挽模式UART 发送、SPI 时钟线(SCK)
复用开漏输出(Alternate function open-drain)引脚功能被外设占用,输出由外设控制,开漏模式I2C 的 SDA/SCL(复用 I2C 外设时)

推挽 vs 开漏:用 “水龙头” 比喻

  • 推挽输出:像两个水龙头,一个接 VDD(高电平),一个接 GND(低电平),可主动切换出水(高)或排水(低),水流(电流)大;
  • 开漏输出:只有一个接 GND 的水龙头,能排水(低电平),但不能主动出水(高电平),要出水得靠外部 “水桶”(上拉电阻)倒水,水流大小由水桶决定。

3. 输出速度:为什么要分 “低速 / 中速 / 高速”?

输出模式还需配置 “输出速度”(Low/Medium/High),对应最大切换频率:

  • 低速(Low):2MHz;
  • 中速(Medium):10MHz;
  • 高速(High):50MHz。

作用:不是速度越快越好!高频切换会产生电磁干扰(EMI),且耗电更多。例如:

  • 控制 LED 用 “低速” 即可(切换频率低,无干扰);
  • 驱动高速 SPI 外设(如 OLED 屏幕)用 “高速”(保证信号同步)。

四、实操:用 GPIO 输入模式检测按键(从硬件到代码)

学会检测按键是 GPIO 输入模式的经典应用,我们用 “输入上拉模式” 实现:按键未按下时,引脚默认高电平;按下时,引脚接地变低电平,通过读取电平变化判断按键状态。

1. 硬件准备与连接

硬件规格作用
轻触按键4 脚直插(或 2 脚贴片)提供开关信号
STM32F103 核心板STM32F103C8T6核心控制
杜邦线公对公 / 公对母连接电路
面包板(可选)基础款搭建临时电路

接线方式(用 PB0 引脚检测按键):

  • 按键一端 → STM32 的PB0引脚;
  • 按键另一端 → STM32 的GND引脚;
  • (无需外部电阻,因为我们用 “输入上拉模式”,内部已接上拉电阻)

简化接线图:PB0 ←→ 按键 ←→ GND

2. 用 STM32CubeIDE 配置 GPIO 输入模式

步骤 1:新建工程(或在上一篇工程基础上修改)

  • 若新建工程:参考第 1 篇,选择 STM32F103C8T6,项目名设为 “Key_Detection”;
  • 若修改旧工程:打开上一篇的 “LED_Blink” 工程,直接修改配置。

步骤 2:配置 PB0 为 “输入上拉模式”

  1. 进入 “Pinout & Configuration” 界面,在右侧引脚图中找到 “PB0”;
  2. 点击 PB0,下拉菜单选择 “GPIO_Input”;
  3. 左侧 “Configuration”→“GPIO”→“PB0”,配置参数:
    • GPIO mode:Input pull-up(输入上拉);
    • 其他参数默认(无需改速度,输入模式速度参数无效);
  4. 保留上一篇的 PA5 配置(推挽输出,控制 LED),点击 “Generate Code” 生成代码。

3. 编写按键检测代码(含消抖处理)

按键是机械结构,按下 / 松开时会有5-20ms 的抖动(电平快速跳变),直接读取会导致误判。解决方法:延时消抖—— 检测到电平变化后,延时 10ms 再读一次,两次结果一致才确认状态。

main.cwhile(1)循环中添加如下代码:

/* USER CODE BEGIN WHILE */
while (1)
{
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
  // 读取PB0引脚电平(GPIO_PIN_SET=高电平,GPIO_PIN_RESET=低电平)
  uint8_t key_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);
  
  // 按键按下时(电平为低)
  if (key_state == GPIO_PIN_RESET)
  {
    // 延时10ms消抖
    HAL_Delay(10);
    // 再次读取,确认按键真的按下
    if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == GPIO_PIN_RESET)
    {
      // 点亮LED(PA5输出高电平)
      HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
    }
  }
  // 按键未按下时(电平为高)
  else
  {
    // 熄灭LED(PA5输出低电平)
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
  }
}
/* USER CODE END 3 */

代码解释

  • HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0):读取 PB0 引脚电平,返回GPIO_PIN_SET(1,高电平)或GPIO_PIN_RESET(0,低电平);
  • 消抖逻辑:第一次检测到低电平后,延时 10ms 再检测,若仍为低电平,才确认按键按下(过滤抖动);
  • 功能:按键按下时 LED 点亮,松开时 LED 熄灭。

4. 烧录验证与问题排查

  1. 烧录程序:用 ST-Link 连接核心板,点击 “Run” 烧录(步骤同第 1 篇);
  2. 验证效果:按下按键,LED 点亮;松开按键,LED 熄灭,说明功能正常。

常见问题排查:

  • 按键按下 LED 不亮:① 检查 PB0 引脚是否接对(别接成其他引脚);② 确认 GPIO 模式配置为 “Input pull-up”(若设为浮空,未按下时电平可能乱跳);③ 用万用表测 PB0 电平:未按下时应为 3.3V(上拉),按下时应为 0V(接地)。
  • LED 乱闪(抖动未解决):① 延长消抖延时(如 20ms);② 检查按键是否质量差(机械抖动过大)。

五、GPIO 寄存器:HAL 库背后的 “真相”

HAL 库帮我们简化了 GPIO 操作,但理解底层寄存器能让你更灵活地控制引脚(比如在对速度要求高的场景下直接操作寄存器)。

STM32F103 每个 GPIO 端口有 7 个关键寄存器(以 GPIOA 为例):

寄存器名称作用关键位
GPIOA_MODER模式寄存器(配置输入 / 输出 / 复用 / 模拟)每 2 位控制 1 个引脚(00 = 输入,01 = 输出,10 = 复用,11 = 模拟)
GPIOA_OTYPER输出类型寄存器(推挽 / 开漏)每 1 位控制 1 个引脚(0 = 推挽,1 = 开漏)
GPIOA_OSPEEDR输出速度寄存器每 2 位控制 1 个引脚(00 = 低速,01 = 中速,11 = 高速)
GPIOA_PUPDR上下拉寄存器每 2 位控制 1 个引脚(00 = 浮空,01 = 上拉,10 = 下拉)
GPIOA_IDR输入数据寄存器(只读)每 1 位对应 1 个引脚的电平(0 = 低,1 = 高)
GPIOA_ODR输出数据寄存器(可读可写)每 1 位控制 1 个引脚的输出电平
GPIOA_BSRR位设置 / 清除寄存器(快速操作 ODR)高 16 位清除(0 无效,1 = 清 0),低 16 位设置(0 无效,1 = 置 1)

举个例子:用寄存器实现 “PA5 输出高电平”

// 配置PA5为推挽输出(需先使能GPIOA时钟,后续章节讲时钟)
GPIOA->MODER &= ~(0x03 << (5*2)); // 清除原来的配置
GPIOA->MODER |= (0x01 << (5*2));  // 01=输出模式
GPIOA->OTYPER &= ~(0x01 << 5);    // 0=推挽输出

// 输出高电平(两种方式)
GPIOA->ODR |= (0x01 << 5);        // ODR第5位置1
// 或用BSRR(更高效,不影响其他位)
GPIOA->BSRR = (0x01 << 5);        // 低16位第5位置1(置位)

HAL 库与寄存器的关系HAL_GPIO_WritePin本质就是操作 ODR 或 BSRR 寄存器,HAL_GPIO_ReadPin就是读取 IDR 寄存器。

六、第 2 篇总结与下一篇预告

总结

这一篇我们掌握了:

  1. GPIO 的 8 种工作模式(4 输入 + 4 输出)及应用场景;
  2. 用 “输入上拉模式” 实现按键检测,理解了机械抖动与消抖处理;
  3. 初步认识 GPIO 寄存器,知道 HAL 库函数的底层原理。

下一篇预告

GPIO 的工作离不开时钟系统——STM32 所有外设(包括 GPIO)必须先 “使能时钟” 才能工作。第 3 篇我们将学习 STM32F103 的时钟树结构、如何配置系统时钟(从 8MHz 外部晶振到 72MHz 主频),以及时钟与外设性能的关系,让你明白 “为什么程序要先开时钟”。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值