找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1313|回复: 9
收起左侧

单片机状态机按键例程如何加入多个按键检测长按短按和连发?

[复制链接]
ID:686513 发表于 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;
}



回复

使用道具 举报

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

使用道具 举报

ID:686513 发表于 2022-12-13 11:31 | 显示全部楼层
wc86110 发表于 2022-12-13 10:38
长按和连按一个意思,有必要细分么?如音量分成 100 短按减 1 长按减 10 以秒来划定加减数值变化就行了
单 ...

比如我有五个按键,一个加,一个减 一个单位切换 一个开关LED灯,一个让电机启停;加减要短按+连按,单位切换要短按+长按,LED要短按+长按,电机要短按,在按键扫描中是不是要短按,长按和连按都要区分开来,才能控制?
回复

使用道具 举报

ID:161164 发表于 2022-12-13 13:49 | 显示全部楼层
zhth1979 发表于 2022-12-13 11:31
比如我有五个按键,一个加,一个减 一个单位切换 一个开关LED灯,一个让电机启停;加减要短按+连按,单位 ...

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


回复

使用道具 举报

ID:69038 发表于 2022-12-15 09:42 | 显示全部楼层
wc86110 发表于 2022-12-13 10:38
长按和连按一个意思,有必要细分么?如音量分成 100 短按减 1 长按减 10 以秒来划定加减数值变化就行了
单 ...

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

使用道具 举报

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

比较赞同您的理解,长按和连按(网上也有叫连发),连按如果是在长按的基础上判断,是不是不好区分出来?
回复

使用道具 举报

ID:996773 发表于 2022-12-30 10:31 | 显示全部楼层
连击的话各个人手速和频率不一样,连击很容易被识别成单击误操作,代码时间间隔比较难做到高识别率,我觉得一个按钮做单击和长按区分就可以了,不要为了少一个按钮反而增加操作设备复杂程度
回复

使用道具 举报

ID:161164 发表于 2022-12-30 10:57 | 显示全部楼层
2022-12-30_105338.png
回复

使用道具 举报

ID:651494 发表于 2022-12-30 11:05 | 显示全部楼层
按键的算法有问题,一个简单地短按,长按,组合键,连发,不需要这么繁杂的想法,应该把这个做法彻底抛弃!附上一个程序给楼主参考

key.rar

4.26 KB, 下载次数: 26

回复

使用道具 举报

ID:686513 发表于 2023-1-7 08:18 | 显示全部楼层
feeling1791 发表于 2022-12-30 11:05
按键的算法有问题,一个简单地短按,长按,组合键,连发,不需要这么繁杂的想法,应该把这个做法彻底抛弃! ...

按说用状态机检测按键很方便的,在这个扫描程序里同时检测短按,长按和连发确实不好实现,可能自己愚笨,现在改用别的方法了。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表