打印
[应用相关]

单个按键的状态机程序方式实现【单击】【双击】【三击...

[复制链接]
1220|9
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
wiba|  楼主 | 2021-9-3 13:53 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

按键的硬件方式为独立按键方法,原理图如下:

上图使用了两个按键,测试平台为:STM8S003,IAR环境

底层代码:

#include "fy_key.h"

void Key_Configuration(void){
        GPIO_Init(GPIOD,GPIO_PIN_3, GPIO_MODE_IN_PU_NO_IT);    //KEY1
        GPIO_Init(GPIOD,GPIO_PIN_4, GPIO_MODE_IN_PU_NO_IT);    //KEY2
}

//只读取初次按键电平状态,在状态机中进一步处理
static u8 Key_ReadPin(void){
        if(READ_KEY1)  
                return KEY1_PRES;
        if(READ_KEY2)  
                return KEY2_PRES;
        return KEY_NONE;
}

//返回不同按键的单击、长按、长按保持、长按抬起、无键5种状态
//此函数10ms调用一次
//状态机方式
u8 Key_Scan(void){
        static u8 cnt = 0;
        static u8 state = 0; //按键初始化状态
        static u8 key_value_last=0;//上一次的键值
        u8 key_value=0;//本次的键值
        u8 key_return=0;
       
        key_value = Key_ReadPin();//读按键值
       
        switch(state){
        case 0:{
                if(key_value != key_value_last)       
                        state = 1; //有按键按下
        }
        break;
        case 1:{
                if(key_value == key_value_last)       
                        state = 2; //消斗之后按键有效
                else
                        state = 0; //认为误触
        }
        break;
        case 2:{ //消斗之后
                if(key_value == key_value_last)        { //还是按下的状态
                        state = 3;
                }
                else{//松开了,短按
                        state = 0;
                        key_return = key_value_last|KEY_SHORT;  //返回键值短按
                }
        }
        break;
       
        case 3:{ //判断长按短按
                if(key_value == key_value_last){
                        if(++cnt > 150){ //1500ms
                                cnt = 190;
                                state = 4;
                                key_return = key_value_last|KEY_LONG; //返回键值长按
                        }
                }
                else{
                        cnt = 0;
                        state = 0;
                        key_return = key_value_last|KEY_SHORT; //返回键值短按
                }
        }
        break;
        case 4:{//长按松手检测
                if(key_value != key_value_last){ //松开了
                        cnt = 0;
                        state = 0;
                        key_return = key_value_last|KEY_LONG_UP; //返回键值保持
                }
                else if(++cnt>=200){
                        cnt = 190;//100ms出发一次
                        key_return = key_value_last|KEY_HOLD; //返回键值保持
                }
        }break;
        }//switch
       
        key_value_last = key_value; //更新
       
        return key_return;
}

//返回不同按键的单击、双击、三击、长按、保持、长按抬起、无键7种状态
//此函数10ms调用一次
u8 Key_Read(void)
{
        static u8 sta = 0;
        static u8 cnt = 0;
        static u8 key_value_last = KEY_NONE;
        u8 key_return = KEY_NONE;
        u8 key_value;
       
        key_value = Key_Scan();
       
        switch(sta) {
        case 0:{
                if(key_value & KEY_SHORT){
                        key_value_last = key_value;         //保存键值
                        cnt = 0;  //第一次单击,不返回,到下个状态判断是否会出现双击
                        sta = 1;
                }
                else
                        key_return = key_value;  //对于无键、长按,返回原事件
        }
        break;
       
        case 1:{
                if((key_value & KEY_SHORT) && (key_value == key_value_last)) { //又一次单击,时间间隔小于500ms
                        key_value_last = key_value;         //保存键值                               
                        sta = 2;
                        cnt = 0;//重新计时
                }
                else if(++cnt >= 50){         //第一次单击后超过500ms 视为单击                                                                               
                        key_return = key_value_last;  //500ms内没有再次出现单击事件,返回单击事件
                        sta = 0;  //返回初始状态                                                       
                }
        }
        break;
       
        case 2:{
                if((key_value & KEY_SHORT) && (key_value == key_value_last)){  //再一次单击,时间间隔小于500ms
                        key_return = (key_value & ~KEY_SHORT) | KEY_THREE;  //返回三击事件,回到初始状态
                        sta = 0;
                }
                else if(++cnt >= 50){ 第二次单击后超过500ms 视为双击                                                                                       
                        key_return = (key_value_last & ~KEY_SHORT) | KEY_DOUBLE;
                        sta = 0;  //返回初始状态                                                       
                }
        }
        break;
        }//switch
       
        return key_return;
}
/*********************************************END OF FILE**********************************************/


使用特权

评论回复
沙发
wiba|  楼主 | 2021-9-3 13:53 | 只看该作者
头文件定义:

#ifndef __FY_KEY_H
#define __FY_KEY_H

#include "stm8s.h"

#define KEY1_PRES 0x01
#define KEY2_PRES 0x02

#define KEY_SHORT          0x04
#define KEY_DOUBLE  0x08
#define KEY_THREE          0x10
#define KEY_LONG           0x20
#define KEY_HOLD           0x40
#define KEY_LONG_UP        0x80
#define KEY_NONE        0x00

#define KEY1_SHORT_PRES     (KEY1_PRES|KEY_SHORT)
#define KEY1_DOUBLE_PRES    (KEY1_PRES|KEY_DOUBLE)
#define KEY1_THREE_PRES     (KEY1_PRES|KEY_THREE)
#define KEY1_LONG_PRES      (KEY1_PRES|KEY_LONG)
#define KEY1_HOLD_PRES      (KEY1_PRES|KEY_HOLD)
#define KEY1_LONG_UP_PRES      (KEY1_PRES|KEY_LONG_UP)

#define KEY2_SHORT_PRES     (KEY2_PRES|KEY_SHORT)
#define KEY2_DOUBLE_PRES    (KEY2_PRES|KEY_DOUBLE)
#define KEY2_THREE_PRES     (KEY2_PRES|KEY_THREE)
#define KEY2_LONG_PRES      (KEY2_PRES|KEY_LONG)
#define KEY2_HOLD_PRES      (KEY2_PRES|KEY_HOLD)
#define KEY2_LONG_UP_PRES   (KEY2_PRES|KEY_LONG_UP)

#define READ_KEY1   !GPIO_ReadInputPin(GPIOD,GPIO_PIN_3)
#define READ_KEY2   !GPIO_ReadInputPin(GPIOD,GPIO_PIN_4)


void Key_Configuration(void);
void KeyInt_Configuration(void);
u8 Key_Scan(void);
u8 Key_Read(void);


#endif

/*********************************************END OF FILE**********************************************/


使用特权

评论回复
板凳
wiba|  楼主 | 2021-9-3 13:54 | 只看该作者
测试代码如下:

void key_test(void){
        Key_Configuration();

        while(1){
       
                key = Key_Read();
               
                if(key != KEY_NONE){
                        key = key;
                }
               
                Delay_ms(10);
        }
}


使用特权

评论回复
地板
xiumukezidiao| | 2022-2-16 18:44 | 只看该作者
wiba 发表于 2021-9-3 13:54
测试代码如下:

void key_test(void){

key等于key是什么鬼

使用特权

评论回复
5
mutable| | 2022-2-19 10:42 | 只看该作者
点击次数太多,感觉不是很好区分功能
单击和双击就好了,再多会不会体验感不好

使用特权

评论回复
6
everyrobin| | 2022-11-19 14:21 | 只看该作者
如何设定长短按键的判定时间呢?              

使用特权

评论回复
7
elsaflower| | 2022-11-20 14:28 | 只看该作者
网上有开源的按键判断的代码。              

使用特权

评论回复
8
rosemoore| | 2022-11-20 15:02 | 只看该作者
C语言怎么写用状态机实现按键控制

使用特权

评论回复
9
1988020566| | 2022-11-23 20:57 | 只看该作者
这个完整的代码有吗?如何移植操作呢?

使用特权

评论回复
10
fengm| | 2022-11-24 14:04 | 只看该作者
这个判断的方法比较精巧了。              

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

77

主题

3305

帖子

3

粉丝