标题: 单片机状态机按键例程如何加入多个按键检测长按短按和连发? [打印本页]

作者: zhth1979    时间: 2022-12-13 09:08
标题: 单片机状态机按键例程如何加入多个按键检测长按短按和连发?
看了uid:130230的状态机按键扫描,感觉应该可以实现我想要的功能:多按键检测长按,短按和连按(按下不放连加或连减),之前看到的例程多为短按和长按组合,或短按和连按组合,没有看到过长按和连按如何识别,一直很纠结。
之前发过比较简单有效的状态机按键检测的例程,但那个例程里没有加入长按检测,如果哪位高手有兴趣,也可以在我之前的贴子里修改,不胜感激!
http://www.51hei.com/bbs/dpj-225323-1.html

下面这个例程是uid:130230高手写的,原例程只有一个按键,现在我把我需要的按键放进去了,还加了一些注释,不知道理解的对不对?还请各位大神们多多指点,先谢谢了!
  .h文件
#ifndef __key_H__
#define __key_H__

#include "xx.h"

#define PD 0x22|(P1&0x51)|(P2<<1&0x80)|(P3<<2&0x0c)   //P1.0/P3.0 p3.1/P4.2 p4.3组合5个按键为1个字节数据
//             |0101 0001   |0100 0000 |0000 0011
//   0010 0010 |0101 0001   |1000 0000 |0000 1100       1100 1101 移位后或:1111 1111  PD = 0xff;



sbit up   = P2^6;   //0111 1111  7f
sbit unit = P1^0;   //1111 1110  fe
sbit down = P3^0;   //1111 1011  fb
sbit md   = P1^6;   //1011 1111  bf
sbit k5   = P3^1;   //1111 0111  f7
sbit k6   = P1^4;   //1110 1111  ef

sbit md1  = P1^5;

//按键值
typedef enum
{
        NONE_CLICK = 0,    //无按键
        SINGLE_CLICK ,     //单击
        DOUBLE_CLICK,      //双击
        LONG_PRESS,        //长按
              LIANFA_CLICK,       //连发
        NUM_KEY,           
}KEY_VALUE_EN;

//按键状态
typedef enum
{
        KEY_IDLE = 0,          //空闲
        KEY_FILTER,            //去抖
        KEY_FIRST,             //第一次按下
        KEY_CHECK_FREE,        //抬起检测(长按)
        NUM_KEY_STAUS,                        
}KEY_STATUS_EN;


//按键扫描状态
typedef struct
{
        unsigned char Statu;
        unsigned char CntDly;
        unsigned char KeyValue;

}TASK_KEY_STATU_ST;


void Task_Keyscan(void);
void Switch_KeyState(unsigned char state);

#endif


.c文件
#include "key.h"


TASK_KEY_STATU_ST gstKey1 = {0};       //任务键状态


/***********************************************
功能:按键扫描
作者:jzb2019年12月27日
备注:50ms运行一次这个函数,理论上可以扩展到n连击
        最大响应延时500ms(单击时)
***********************************************/
void Task_Keyscan(void)
{
        unsigned char key;
  key=PD&0xFF;
        
  switch (gstKey1.Statu)
      {
        case KEY_IDLE:                      //按键空闲
                                         {
                                                if(key!= 0xFF)
                                                        {
                Switch_KeyState(KEY_FILTER);//按键按下了去抖
              }
            }
            break;
                                         
         case KEY_FILTER:
            {
                                                 if(key!= 0xFF)
                                                                {
                                                                        gstKey1.CntDly++;
                                                                        if(gstKey1.CntDly >= 60)
                                                                                {
                                                                                        gstKey1.KeyValue = LONG_PRESS;//长按
                                                                                       
                                                                                        Switch_KeyState(KEY_CHECK_FREE);//抬起检测
                                                                                }
                                                                 }
             else
                {
                                                                 if(gstKey1.CntDly == 0)
                                                                                {
                                                                                 Switch_KeyState(KEY_IDLE);//如果没有计数则证明是毛刺滤掉
                                                                                }
                                                                        else
                                                                                {
                                                                                                                Switch_KeyState(KEY_FIRST);//转到第一次按下状态
                                                                                }
                 }
               }
               break;
                                                         
                                        case KEY_FIRST:
                                                        {
                                                                if(key == 0xff)//释放了
                                                                        {        
                                                                                gstKey1.CntDly++;
                                                                                if(gstKey1.CntDly > 10)//500ms
                                                                                        {
                                                                                                gstKey1.KeyValue = SINGLE_CLICK;          //单击
                                                                                       
                                                                                                Switch_KeyState(KEY_IDLE);//回到空闲
                                                                                        }
                                                                        }
                  else//按下了
                     {
                                                                                        if(gstKey1.CntDly == 0)//没有计数证明是没有释放按键的动作
                                                                                          {
                                                                                            
                                                                                           }
                                                                                        else//这个分支里面已经是第二次按下了
                                                                                                {
                                                                                                        gstKey1.KeyValue = DOUBLE_CLICK;
                                                                                                
                                                                                                        Switch_KeyState(KEY_CHECK_FREE);//抬起检测
                                                                                                }
                                                                                        }
                }
                break;
                                                               
           case KEY_CHECK_FREE:
                                                         {
                                                                 if(key == 0xff)
                                                                                {        
                                                                                        Switch_KeyState(KEY_IDLE);
                                                                                }
                                                         }
                                             break;
                                                         
                                         default :
                                                        {
                                                                Switch_KeyState(KEY_IDLE);
                                                        }
                                                        break;        
        }
}


/****************************************************************************
功能:执行切换状态的动作,同时清掉当前状态内的计数
入口:要切换的状态
作者:jzb 2019年12月27日
备注:暂时先不考虑通用的问题,直接操作全局变量如果扩展按键再说
*****************************************************************************/
void Switch_KeyState(unsigned char state)
{
        gstKey1.CntDly = 0;
        gstKey1.Statu = state;
}




作者: wc86110    时间: 2022-12-13 10:38
长按和连按一个意思,有必要细分么?如音量分成 100 短按减 1 长按减 10 以秒来划定加减数值变化就行了
单键复用还是简单些实用,不然会造成操作者困惑按键处在何种状态

作者: zhth1979    时间: 2022-12-13 11:31
wc86110 发表于 2022-12-13 10:38
长按和连按一个意思,有必要细分么?如音量分成 100 短按减 1 长按减 10 以秒来划定加减数值变化就行了
单 ...

比如我有五个按键,一个加,一个减 一个单位切换 一个开关LED灯,一个让电机启停;加减要短按+连按,单位切换要短按+长按,LED要短按+长按,电机要短按,在按键扫描中是不是要短按,长按和连按都要区分开来,才能控制?
作者: lkc8210    时间: 2022-12-13 13:49
zhth1979 发表于 2022-12-13 11:31
比如我有五个按键,一个加,一个减 一个单位切换 一个开关LED灯,一个让电机启停;加减要短按+连按,单位 ...

按键扫描不用区分
得出的键值就要区分处理
短按和长按的键值用完要清零
连按的键值用完不用清零,放开才清零



作者: zhuls    时间: 2022-12-15 09:42
wc86110 发表于 2022-12-13 10:38
长按和连按一个意思,有必要细分么?如音量分成 100 短按减 1 长按减 10 以秒来划定加减数值变化就行了
单 ...

长按和连按一个意思?表示不解。
按下、保持按下、放开, 是一个按键的完整过程,
比如单位时间内:(有效按键时)
“长按”主要检测“保持按下”的时长,
“连按”则是检测“按下”和“放开”的次数。

作者: zhth1979    时间: 2022-12-30 09:21
zhuls 发表于 2022-12-15 09:42
长按和连按一个意思?表示不解。
按下、保持按下、放开, 是一个按键的完整过程,
比如单位时间内:( ...

比较赞同您的理解,长按和连按(网上也有叫连发),连按如果是在长按的基础上判断,是不是不好区分出来?
作者: hi等你    时间: 2022-12-30 10:31
连击的话各个人手速和频率不一样,连击很容易被识别成单击误操作,代码时间间隔比较难做到高识别率,我觉得一个按钮做单击和长按区分就可以了,不要为了少一个按钮反而增加操作设备复杂程度
作者: lkc8210    时间: 2022-12-30 10:57


作者: feeling1791    时间: 2022-12-30 11:05
按键的算法有问题,一个简单地短按,长按,组合键,连发,不需要这么繁杂的想法,应该把这个做法彻底抛弃!附上一个程序给楼主参考

key.rar

4.26 KB, 下载次数: 35


作者: zhth1979    时间: 2023-1-7 08:18
feeling1791 发表于 2022-12-30 11:05
按键的算法有问题,一个简单地短按,长按,组合键,连发,不需要这么繁杂的想法,应该把这个做法彻底抛弃! ...

按说用状态机检测按键很方便的,在这个扫描程序里同时检测短按,长按和连发确实不好实现,可能自己愚笨,现在改用别的方法了。
作者: 17337161031    时间: 2024-7-2 09:07
长按需要加入上时刻和当前状态判断  短按的话加个松手检测 连发的话 去掉松手检测




欢迎光临 (http://www.51hei.com/bbs/) Powered by Discuz! X3.1