第二篇:按键交互入门:STM32 GPIO输入与消抖处理

本文目标:掌握STM32 GPIO的输入功能,通过读取按键状态控制LED亮灭(实现"按键开关灯"),并学习硬件/软件消抖方法。这是人机交互的基础,也是后续更复杂输入设备(如传感器、触摸屏)的起点!

一、为什么学按键输入?

在嵌入式系统中,"输入"是与用户交互的核心方式之一。按键是最简单的输入设备------通过检测引脚电平变化(按下时接地/接VCC,松开时悬空/上拉),我们可以实现开关控制、模式切换等功能。通过本实验,你将学会:

  1. GPIO输入模式的配置方法(浮空/上拉/下拉输入);
  2. 如何读取引脚电平(GPIO_ReadInputDataBit());
  3. 按键抖动的原理与消抖技巧(硬件滤波 or 软件延时);
  4. 中断 vs 轮询(本实验用轮询,后续文章会讲中断优化)。

二、硬件准备(基于上一篇的LED实验扩展)

  1. 核心器件

• STM32F103C8T6开发板(与上一篇相同,已实现LED闪烁);

• 独立按键模块(或直接使用开发板上的用户按键,常见标注为"KEY"或"BTN");

• 杜邦线/面包板(若用分立元件搭按键电路)。

  1. 按键电路设计(重点!)

按键的本质是一个机械开关,按下时连通两个引脚,松开时断开。为了稳定检测按键状态,必须设计合理的电路:

方案1:上拉输入 + 按键接地(推荐!)

• 原理:GPIO引脚通过内部上拉电阻(或外接10kΩ上拉电阻)默认保持高电平(3.3V),当按键按下时,引脚被拉低至GND(0V),通过检测电平变化判断按键状态。

• 接线(以开发板常见按键为例):

• 按键一端接 GPIO引脚(如PA1);

• 按键另一端接 GND;

• GPIO引脚配置为 上拉输入模式(GPIO_Mode_IPU),无需外接电阻(若开发板已内置上拉则直接用,否则需外接10kΩ上拉电阻到VCC)。

常见开发板按键接法:比如某点原子战舰板的KEY0~KEY3通常是"低电平有效"(按下时接GND,松开时通过上拉电阻保持高电平),本文假设你的按键也是这种设计------按下时PA1为低电平(0V),松开时PA1为高电平(3.3V)。

方案2:下拉输入 + 按键接VCC(较少用)

• 按键一端接VCC(3.3V),另一端接GPIO引脚,GPIO配置为下拉输入(GPIO_Mode_IPD),按下时引脚为高电平,松开时为低电平。

本文选择方案1(上拉输入 + 按键接地),因为STM32内部通常提供上拉选项,且电路更简洁(无需额外电阻)。

三、软件工具链(复用上一篇环境)

• Keil MDK(已安装,无需重新配置);

• STM32标准库(已添加GPIO和RCC驱动,本文需新增按键逻辑)。

四、开发环境配置(复用上一篇工程)

直接在上一篇的"LED_Blink"工程中修改,无需新建工程:

  1. 打开之前的Keil工程(如 LED_Blink.uvprojx);
  2. 在工程中添加新的源文件 key.c 和头文件 key.h(可选,本文直接写在 main.c 中简化流程);
  3. 确保已包含标准库的头文件(stm32f10x.h)和上一篇配置的Include Paths。
    也阔以直接复制上次的工程,在里面增加按键的检测代码

五、按键控制LED的代码实现

  1. 硬件连接确认

• 假设按键连接到 PA1引脚(若你的按键接在其他引脚,如PC13或开发板自带KEY,修改代码中的 GPIOA → 对应端口,GPIO_Pin_1 → 对应引脚号);

• LED仍连接到 PA0引脚(与上一篇一致)。

• 电路逻辑:按键按下时PA1为低电平(0V),松开时PA1为高电平(3.3V);LED低电平点亮(共阳接法,即LED负极接PA0)。

  1. 代码逻辑设计

• 目标:当按键按下时,LED状态反转(亮→灭 或 灭→亮);松开后保持当前状态。

• 步骤:

  1. 初始化PA1为上拉输入(检测按键电平);

  2. 初始化PA0为推挽输出(控制LED);

  3. 主循环中不断读取PA1的电平:

    ◦ 若检测到低电平(按键按下),等待消抖后,反转LED状态;

    ◦ 否则(按键松开),不做操作。

  4. 完整代码(修改自上一篇的 main.c)

c 复制代码
#include "stm32f10x.h"  // STM32标准库头文件

// 延时函数(用于消抖)
void Delay(uint32_t time) {
    for(uint32_t i = 0; i < time; i++) {
        for(uint32_t j = 0; j < 5000; j++);  // 空循环消耗时间
    }
}

int main(void) {
    GPIO_InitTypeDef GPIO_InitStructure;  // GPIO配置结构体
    
    // 1. 开启GPIOA时钟(PA0和PA1都属于GPIOA)
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    // 2. 初始化PA0为推挽输出(控制LED)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;          // PA0引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   // 推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  // 输出速度
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 3. 初始化PA1为上拉输入(检测按键)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;          // PA1引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;      // 上拉输入(默认高电平,按下时低电平)
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    uint8_t led_state = 0;  // LED状态:0=灭(PA0高电平),1=亮(PA0低电平)

    while(1) {  // 主循环
        // 读取PA1的电平:GPIO_ReadInputDataBit(GPIOx, GPIO_Pin_x) 返回 0(低电平)或 1(高电平)
        if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 0) {  // 检测到按键按下(低电平)
            Delay(20);  // 简单软件消抖(延时20ms,过滤机械抖动)
            if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 0) {  // 再次确认按键仍按下(避免误触发)
                led_state = !led_state;  // 反转LED状态
                if (led_state == 1) {
                    GPIO_ResetBits(GPIOA, GPIO_Pin_0);  // LED亮(PA0低电平)
                } else {
                    GPIO_SetBits(GPIOA, GPIO_Pin_0);    // LED灭(PA0高电平)
                }
                // 等待按键释放(避免长按重复触发)
                while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 0); 
            }
        }
    }
}

关键代码解析:

  • GPIO输入配置:GPIO_Mode_IPU 表示上拉输入(引脚默认高电平,按下时被拉低到GND);若你的按键是"按下时接VCC,松开时接地"(低电平有效),则需改用 GPIO_Mode_IPD(下拉输入)。

  • 电平读取:GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) 返回 0(低电平,按键按下)或 1(高电平,按键松开)。

  • 消抖处理:机械按键在按下/释放瞬间会产生5~20ms的电平抖动(快速的高低跳变),直接读取会导致多次误触发。这里用两种消抖方法:

    • 软件延时消抖:检测到低电平后,延时20ms(大部分抖动已结束),再确认一次电平是否仍为低;

    • 按键释放等待:反转LED状态后,循环检测按键是否释放(避免长按时重复触发状态反转)。

关于按键逻辑的常见设计:

  • 如果你的按键是"按下时LED亮,松开时LED灭"(长按控制),只需去掉"反转状态"和"等待释放"的逻辑,直接通过当前电平控制LED即可;

  • 如果你的开发板按键是"低电平有效"(按下时PA1=0V,松开时PA1=3.3V),但代码中误判了电平(比如把高电平当按下),请检查 if (GPIO_ReadInputDataBit(...) == 0) 中的条件是否符合实际硬件。

六、常见问题解决

  1. 按键按下后LED无反应

    • 检查硬件:按键是否接对引脚(PA1?)、是否真的按下(用万用表测PA1电压:按下时应为0V,松开时为3.3V);

    • 检查GPIO配置:PA1是否配置为上拉输入(GPIO_Mode_IPU),PA0是否为推挽输出;

    • 检查电平逻辑:如果按键按下时PA1是高电平(比如接VCC),则需修改判断条件为 if (GPIO_ReadInputDataBit(...) == 1),并调整上拉/下拉模式。

  2. 按键抖动导致多次触发

    • 增加消抖延时(如从20ms改为30ms);

    • 更稳定的方法是"状态机消抖"(记录前几次的按键状态,通过连续稳定状态判断有效按下),后续文章会详细讲解。

  3. LED状态反转不符合预期

    • 确认LED的接法:如果LED正极接PA0、负极接GND(共阴接法),则高电平LED亮,低电平LED灭,需修改 GPIO_SetBits/ResetBits 的逻辑;

    • 本文假设LED是共阳接法(负极接PA0,正极接VCC),因此PA0低电平→LED亮,高电平→LED灭。

七、总结与下一步

现在,你已经实现了STM32的第一个交互功能------通过按键控制LED开关!这不仅巩固了GPIO输入/输出的配置方法,还学习了实际开发中必须处理的"按键消抖"问题。

下一步学习建议:

  1. 尝试修改按键引脚(如改用PC13,开发板常见用户按键),调整代码中的端口和引脚号;
  2. 实验不同消抖方法(比如去掉软件延时,观察按键抖动导致的误触发);
  3. 思考:如果想实现"长按按键LED渐亮/渐灭",该如何扩展代码?
相关推荐
蓬荜生灰3 小时前
第5章—STM32工程创建
stm32·单片机·嵌入式硬件
hazy1k3 小时前
51单片机基础-定时器中断
stm32·单片机·嵌入式硬件·51单片机
恒锐丰小吕4 小时前
矽塔 SA8204 输入耐压36V 2.5A可调过流保护 集成故障报告的智能过压/过流保护芯片
嵌入式硬件·硬件工程
太空1号5 小时前
VxWorks入门小白菜鸟教程3 —— 编译运行VxWorksSDK示例hello_cmake_rtp(Windows篇)
windows·嵌入式硬件
小莞尔6 小时前
【51单片机】【protues仿真】基于51单片机火灾报警控制系统
c语言·单片机·嵌入式硬件·物联网·51单片机
可爱的鸡仔7 小时前
STM32--------DMA
stm32·单片机·嵌入式硬件
D.....l7 小时前
STM32学习(MCU控制)(GPIO)
stm32·嵌入式硬件·学习
lzhdim7 小时前
雷蛇(Razer)炼狱蝰蛇V2X极速版无线鼠标开箱
单片机·嵌入式硬件·计算机外设
wuk9987 小时前
基于位置式PID算法调节PWM占空比实现电机转速控制
单片机·嵌入式硬件·算法