2. 按键输入检测与消抖
任务说明
本案例基于STM32 HAL库,实现一个独立可运行的按键输入检测程序。核心目标为:通过GPIO读取按键原始电平,采用软件延时消抖(按下与释放均消抖),在确认按键有效按下后翻转LED状态。程序需包含完整的按键初始化、电平读取、消抖判定及LED控制逻辑,不依赖外部中断或定时器,仅使用HAL库的延时函数完成消抖。
硬件连接与原理
按键一端连接至MCU的PA0引脚,另一端接地(低电平有效);LED阳极通过限流电阻接至PC13引脚(STM32F103核心板载LED),阴极接地。当按键未按下时PA0被内部上拉电阻拉高,按下时被拉低。程序通过轮询方式读取PA0电平,检测到低电平后启动10ms延时消抖,若延时后仍为低电平则判定为有效按下,随后翻转PC13输出状态。释放时的抖动同样通过延时确认,避免一次按压产生多次翻转。
代码实现
// main.c 核心逻辑(基于STM32CubeMX生成的HAL工程)
#include "main.h"
// 按键与LED引脚定义(根据实际接线修改)
#define KEY_PIN GPIO_PIN_0
#define KEY_PORT GPIOA
#define LED_PIN GPIO_PIN_13
#define LED_PORT GPIOC
// 消抖延时(毫秒)
#define DEBOUNCE_TIME 10
// 按键状态机
typedef enum {
KEY_STATE_IDLE, // 空闲(未按下)
KEY_STATE_PRESSED, // 已确认按下
KEY_STATE_RELEASED // 已确认释放
} KeyState;
static KeyState keyState = KEY_STATE_IDLE;
// GPIO初始化(由CubeMX生成,此处为必要配置)
static void MX_GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
// 配置PA0为输入,上拉
GPIO_InitStruct.Pin = KEY_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(KEY_PORT, &GPIO_InitStruct);
// 配置PC13为推挽输出
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct);
// 初始LED熄灭(根据硬件极性调整)
HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_SET);
}
// 按键扫描函数:返回1表示检测到一次有效按下,0表示无
uint8_t Key_Scan(void) {
uint8_t ret = 0;
switch (keyState) {
case KEY_STATE_IDLE:
// 检测到低电平,进入按下消抖
if (HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN) == GPIO_PIN_RESET) {
HAL_Delay(DEBOUNCE_TIME);
// 消抖后仍为低电平,确认按下
if (HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN) == GPIO_PIN_RESET) {
keyState = KEY_STATE_PRESSED;
ret = 1; // 产生一次有效按下事件
}
}
break;
case KEY_STATE_PRESSED:
// 等待释放,释放时同样消抖
if (HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN) == GPIO_PIN_SET) {
HAL_Delay(DEBOUNCE_TIME);
if (HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN) == GPIO_PIN_SET) {
keyState = KEY_STATE_RELEASED;
}
}
break;
case KEY_STATE_RELEASED:
// 回到空闲,准备下一次检测
keyState = KEY_STATE_IDLE;
break;
default:
keyState = KEY_STATE_IDLE;
break;
}
return ret;
}
// 主循环
int main(void) {
HAL_Init();
MX_GPIO_Init();
while (1) {
// 检测到有效按下则翻转LED
if (Key_Scan()) {
HAL_GPIO_TogglePin(LED_PORT, LED_PIN);
}
}
}
状态机与消抖逻辑
程序采用三态状态机管理按键过程:空闲态等待首次低电平;按下态确认按键已稳定按下,等待释放;释放态确认释放完成后回到空闲。每次状态迁移均经过10ms延时验证,确保电平变化是人为操作而非机械抖动。有效按下事件仅在“空闲→按下”迁移时产生一次,因此每次完整按压(按下并释放)只触发一次LED翻转,不会因抖动产生多次触发。
验证方法
编译与烧录:使用STM32CubeIDE打开工程,编译后将程序下载至开发板。
功能测试:上电后LED保持熄灭(或初始状态)。手动按下按键并保持约0.5秒后松开,观察LED状态是否翻转(亮→灭或灭→亮)。连续快速按压多次,每次按压应只翻转一次,且无漏检或误触发。
判定标准:若每次完整按压均能稳定翻转LED一次,且快速连续按压时无抖动导致的多次翻转,则判定案例通过。若出现一次按压翻转多次或偶尔无响应,需检查消抖延时是否合适(可适当增大至20ms)或硬件连接是否可靠。
关键说明
本实现采用阻塞式延时消抖,适用于单按键轮询场景。若需同时处理多个按键或要求低功耗,可改用定时器扫描或外部中断+定时器的方式,但核心消抖逻辑(延时确认电平稳定)保持一致。代码中GPIO初始化部分仅列出必要配置,实际工程中由CubeMX生成的MX_GPIO_Init()会自动包含时钟使能与引脚设置,可直接替换使用。