STM32之按键长短按设计【一键多用】

文章目录

  • STM32之按键长短按设计【一键多用】
  • 一、设计原理
  • 1. 核心需求
  • 2. 状态机设计
  • 二、代码实现
  • 三、流程图
  • 四、使用示例
  • 五、优化与扩展
  • 完整程序
  • STM32之按键长短按设计【一键多用】

    在嵌入式开发中,按键的短按长按功能是提升交互效率的关键。本文将详解如何在STM32中通过状态机和时间戳实现按键的“一键多用”,并提供完整的代码和设计思路。


    一、设计原理

    1. 核心需求

  • 消抖处理:消除机械按键的抖动(20ms)。
  • 长短按区分:按下时间 < 1s 为短按,≥1s 为长按。
  • 事件触发:通过标志位分离按键检测与事件处理。
  • 2. 状态机设计

    定义三种按键状态:

    typedef enum {
        BUTTON_STATE_RELEASED,     // 松开状态
        BUTTON_STATE_PRESSED,      // 按下状态
        BUTTON_STATE_DEBOUNCING    // 消抖状态
    } ButtonState;
    

    二、代码实现

    1. 按键结构体与宏定义(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;
    
    1. 中断回调函数(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(); //记录松开时刻,用于消抖计时
            }
        }
        // 其他按键类似...
    }
    
    1. 按键处理函数(核心逻辑)
    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");
        }
    }
    

    三、流程图

    四、使用示例

    1. 初始化按键
    Button key1 = {BUTTON_STATE_RELEASED, 0, 0, 0, 0, KEY_GPIO_Port, KEY_Pin};
    
    1. 主循环调用
    while (1) {
        Button_Process(&key1);  // 处理按键
        HAL_Delay(10);          // 适当延时
    }
    

    五、优化与扩展

    1. 多按键支持
      定义多个Button实例,独立管理每个按键。
    2. 回调函数
      可将事件处理替换为函数指针,增强灵活性。
    3. 参数可调
      通过宏定义修改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

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32之按键长短按设计【一键多用】

    发表回复