STM32之按键长短按设计【一键多用】
文章目录
STM32之按键长短按设计【一键多用】
在嵌入式开发中,按键的短按和长按功能是提升交互效率的关键。本文将详解如何在STM32中通过状态机和时间戳实现按键的“一键多用”,并提供完整的代码和设计思路。
一、设计原理
1. 核心需求
2. 状态机设计
定义三种按键状态:
typedef enum {
BUTTON_STATE_RELEASED, // 松开状态
BUTTON_STATE_PRESSED, // 按下状态
BUTTON_STATE_DEBOUNCING // 消抖状态
} ButtonState;
二、代码实现
- 按键结构体与宏定义(key.h)
#define DEBOUNCE_TIME 20 // 消抖时间20ms
#define LONG_PRESS_TIME 1000 // 长按阈值1000ms
typedef struct {
ButtonState state; // 当前状态
uint32_t buttonTime; // 状态切换时间戳
uint32_t presstime; // 按下时刻时间戳
uint8_t pressedFlag; // 短按标志
uint8_t longPressFlag; // 长按标志
GPIO_TypeDef* gpioPort; // GPIO端口(如GPIOA)
uint16_t gpioPin; // GPIO引脚(如GPIO_PIN_0)
} Button;
- 中断回调函数(key.c)
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if (GPIO_Pin == key1.gpioPin) {
if (key1.state == BUTTON_STATE_RELEASED) {
// 按下:进入消抖状态,记录时间戳
key1.state = BUTTON_STATE_DEBOUNCING;
key1.buttonTime = HAL_GetTick(); //记录按下时刻,用于消抖计时
key1.presstime = key1.buttonTime; // 记录按下时刻,用于长按计时
} else if (key1.state == BUTTON_STATE_PRESSED) {
// 松开:进入消抖状态,记录时间戳
key1.state = BUTTON_STATE_DEBOUNCING;
key1.buttonTime = HAL_GetTick(); //记录松开时刻,用于消抖计时
}
}
// 其他按键类似...
}
- 按键处理函数(核心逻辑)
void Button_Process(Button* button) {
// 1. 消抖处理
if (button->state == BUTTON_STATE_DEBOUNCING) {
if (HAL_GetTick() - button->buttonTime >= DEBOUNCE_TIME) {
// 读取引脚电平确认状态
if (HAL_GPIO_ReadPin(button->gpioPort, button->gpioPin) == GPIO_PIN_RESET) {
button->state = BUTTON_STATE_PRESSED; // 稳定按下
} else {
button->state = BUTTON_STATE_RELEASED; // 稳定松开
// 计算按下时长,设置标志位
if (HAL_GetTick() - button->presstime < LONG_PRESS_TIME) {
button->pressedFlag = 1; // 短按
} else {
button->longPressFlag = 1; // 长按
}
}
}
}
// 2. 处理短按事件
if (button->pressedFlag) {
button->pressedFlag = 0;
printf("KEY Short Press!\n");
}
// 3. 处理长按事件
if (button->longPressFlag) {
button->longPressFlag = 0;
printf("KEY Long Press!\n");
}
}
三、流程图
四、使用示例
- 初始化按键
Button key1 = {BUTTON_STATE_RELEASED, 0, 0, 0, 0, KEY_GPIO_Port, KEY_Pin};
- 主循环调用
while (1) {
Button_Process(&key1); // 处理按键
HAL_Delay(10); // 适当延时
}
五、优化与扩展
- 多按键支持
定义多个Button实例,独立管理每个按键。 - 回调函数
可将事件处理替换为函数指针,增强灵活性。 - 参数可调
通过宏定义修改DEBOUNCE_TIME和LONG_PRESS_TIME。
通过本文的设计,STM32的按键可实现精准的长短按检测,代码清晰且易于扩展。实际应用中,可根据需求调整阈值和事件处理逻辑。
完整程序
key.h文件:
#ifndef __KEY_H__
#define __KEY_H__
#include "main.h"
#define BUTTON_PIN GPIO_PIN_0
#define BUTTON_GPIO_PORT GPIOA
#define DEBOUNCE_TIME 20 // 消抖时间,单位ms
#define LONG_PRESS_TIME 1000 // 长按时间阈值,单位ms
typedef enum {
BUTTON_STATE_RELEASED,
BUTTON_STATE_PRESSED,
BUTTON_STATE_DEBOUNCING
} ButtonState;
typedef struct {
ButtonState state; // 按键状态
uint32_t buttonTime; // 按键按下或松开的时间戳
uint32_t presstime; // 按键按下的时间戳
uint8_t pressedFlag; // 单次触发标志
uint8_t longPressFlag; // 长按触发标志
GPIO_TypeDef* gpioPort; // 按键 GPIO 端口
uint16_t gpioPin; // 按键 GPIO 引脚
} Button;
extern Button key1, key2;
void Button_Process(Button* button);
#endif
key.c文件:
#include "key.h"
#include "stdio.h"
ButtonState buttonState = BUTTON_STATE_RELEASED;
uint32_t buttonTime = 0, presstime = 0;
uint8_t buttonPressedFlag = 0;
uint8_t buttonLongPressFlag = 0;
Button key1 = {BUTTON_STATE_RELEASED, 0, 0, 0, 0, KEY_GPIO_Port, KEY_Pin};
Button key2 = {BUTTON_STATE_RELEASED, 0, 0, 0, 0, KEY0_GPIO_Port, KEY0_Pin};
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == key1.gpioPin)
{
if (key1.state == BUTTON_STATE_RELEASED)
{
// 按键按下,进入消抖状态
key1.state = BUTTON_STATE_DEBOUNCING;
key1.buttonTime = HAL_GetTick();
key1.presstime = key1.buttonTime;
}
else if (key1.state == BUTTON_STATE_PRESSED)
{
// 按键松开,进入消抖状态
key1.state = BUTTON_STATE_DEBOUNCING;
key1.buttonTime = HAL_GetTick();
}
}
else if (GPIO_Pin == key2.gpioPin)
{
if (key2.state == BUTTON_STATE_RELEASED)
{
// 按键按下,进入消抖状态
key2.state = BUTTON_STATE_DEBOUNCING;
key2.buttonTime = HAL_GetTick();
key2.presstime = key2.buttonTime;
}
else if (key2.state == BUTTON_STATE_PRESSED)
{
// 按键松开,进入消抖状态
key2.state = BUTTON_STATE_DEBOUNCING;
key2.buttonTime = HAL_GetTick();
}
}
}
void Button_Process(Button* button)
{
if (button->state == BUTTON_STATE_DEBOUNCING)
{
// 消抖处理
if (HAL_GetTick() - button->buttonTime >= DEBOUNCE_TIME)
{
if (HAL_GPIO_ReadPin(button->gpioPort, button->gpioPin) == GPIO_PIN_RESET)
{
// 按键稳定按下
button->state = BUTTON_STATE_PRESSED;
}
else
{
// 按键稳定松开
button->state = BUTTON_STATE_RELEASED;
// 判断触发类型
if (HAL_GetTick() - button->presstime < LONG_PRESS_TIME)
{
button->pressedFlag = 1; // 单次触发
}
else
{
button->longPressFlag = 1; // 长按触发
}
}
}
}
if (button->pressedFlag)
{
button->pressedFlag = 0;
// 处理单次触发事件
if (button->gpioPin == KEY_Pin)
{
printf("KEY Pressed!\n");
}
else if (button->gpioPin == KEY0_Pin)
{
printf("KEY0 Pressed!\n");
}
}
if (button->longPressFlag)
{
button->longPressFlag = 0;
// 处理长按触发事件
if (button->gpioPin == KEY_Pin)
{
printf("KEY Long Pressed!\n");
}
else if (button->gpioPin == KEY0_Pin)
{
printf("KEY0 Long Pressed!\n");
}
}
}
作者:Gui_Zeng