基于按键跳变状态的单击、双击、长按判断

使用stm32,修改key_read完成读取
按键之间互不干扰,如1长按过程中可以正常识别2的单击等
key.c 

/*
* key.c key.h
* 
* 基于按键跳变状态完成的按键单击、双击、长按的扫描
* 使用1ms自增的uwTick做时间判断
* 按键默认初始化,默认硬件消抖
*
* 使用说明:在循环或任务中调用key_proc 重写执行函数
*
* V1.0 2025.8.17
*/
#include "gpio.h"
#include "key.h"

/* 将KEY_TEST_PRINT置1开启按键状态打印 置0关闭 */
#define KEY_TEST_PRINT 0 

#if KEY_TEST_PRINT
     #include "user_uart.h"
#endif

typedef enum {
    KEY_IDLE = 0,      // 空闲
    KEY_PRESS,         // 按下中
    KEY_WAIT_SECOND,   // 等待第二次按下
    KEY_PRESS_SECOND   // 第二次按下中
} KeyStateEnum;

typedef struct {
    KeyStateEnum state;
    uint32_t t_down;
    uint32_t t_up;
} KeyState;

static KeyState key[5];   // 对应5个按键

/*
* @brief  按键当前状态读取函数
*         
* @param  void
*
* @return 按键按下的状态 在8位变量上以1表示
*
* */
uint8_t key_read(void){
    uint8_t key_val;
    key_val = HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_12)<<0|\
                    HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_10)<<1|\
                    HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_8)<<2|\
                    HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_7)<<3|\
                    HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_13)<<4;
    if(key_val != (HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_12)<<0|\
                    HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_10)<<1|\
                    HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_8)<<2|\
                    HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_7)<<3|\
                    HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_13)<<4)){
        key_val = 0x1f;
    }
    return key_val^0x1f;
}

/*
* @brief  检测按键跳变状态,触发单击 双击 长按执行函数
* 
* @note   1.按键的默认最小扫描间隔为10ms
*         2.执行函数为弱定义,重写执行函数完成所需功能
*         3.因需要同时判断双击长按,单击的响应将会较慢,如有需要使用key_read进行直接判断
*         4.单机的判断为按下时长小于1s,松开后0.5s内没有操作触发
*         5.双击的判断为第一次按下小于1s,松开到第二次按下小于0.5s触发
*         6.长按的判断为第一次按下到松开大于1s触发
*         7.按键的判断优先级为:长按>双击>单击
*         
* @param  void
*
* @return void
*
* */
void key_proc(void){
    static uint32_t uwTick_KEY;
    if(uwTick - uwTick_KEY < 10) return; // 10ms扫描
    uwTick_KEY = uwTick;

    uint8_t val = key_read();  // 当前按键状态
    for(int i=0;i<5;i++){
        uint8_t mask = 1<<i;
        KeyState *k = &key[i];

        switch(k->state){
        case KEY_IDLE: // 空闲 -> 第一次按下
            if(val & mask){
                k->t_down = uwTick;
                k->state = KEY_PRESS;
            }
            break;

        case KEY_PRESS: // 第一次按下中
            if(!(val & mask)){ // 松开
                k->t_up = uwTick;
                uint32_t dt = k->t_up - k->t_down;
                if(dt > 1000){   // 长按
                    key_click_long(mask);
                    k->state = KEY_IDLE;
                }else{
                    k->state = KEY_WAIT_SECOND; // 等待双击
                }
            }
            break;

        case KEY_WAIT_SECOND: // 等待第二次
            if(val & mask){ // 第二次按下
                if(uwTick - k->t_up < 500){
                    k->t_down = uwTick;
                    k->state = KEY_PRESS_SECOND;
                }else{ // 超时 -> 单击
                    key_click(mask);
                    k->state = KEY_PRESS; // 这次按下当作新的一次
                    k->t_down = uwTick;
                }
            }else if(uwTick - k->t_up >= 500){ // 超时未按 -> 单击
                key_click(mask);
                k->state = KEY_IDLE;
            }
            break;

        case KEY_PRESS_SECOND: // 第二次按下中
            if(!(val & mask)){ // 松开 -> 确认双击
                key_click_double(mask);
                k->state = KEY_IDLE;
            }
            break;
        }//end switch
    }//end for
}

__weak unsigned char key_click(unsigned char key_val){
    #if KEY_TEST_PRINT
        printf("key_click = %d\n",key_val);
    #endif
    return key_val;
}

__weak unsigned char key_click_double(unsigned char key_val){
    #if KEY_TEST_PRINT
        printf("key_click_double = %d\n",key_val);
    #endif
    return key_val;
}

__weak unsigned char key_click_long(unsigned char key_val){
    #if KEY_TEST_PRINT
        printf("key_click_long = %d\n",key_val);
    #endif
    return key_val;
}

key.h
 

#ifndef _KEY_H_
#define _KEY_H_

enum KEY_VAL{
    KEY1 = 1,
    KEY2 = 2,
    KEY3 = 4,
    KEY4 = 8,
    KEY5 = 16
};

unsigned char key_read(void);
void key_proc(void);
unsigned char key_click(unsigned char key_val);
unsigned char key_click_double(unsigned char key_val);
unsigned char key_click_long(unsigned char key_val);

#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值