搜索
bottom↓
回复: 24

抛砖引"砖",功能超多按键程序

  [复制链接]

出590入1001汤圆

发表于 2020-7-14 13:29:29 | 显示全部楼层 |阅读模式
论坛上有不少关于按键的帖子,本人也献丑一下。
为什么是:抛砖引"砖"。因为我这里准备发布两个代码:
先说一个以前遇到的一个bug,刚开始工作不久,用了一个按键代码,批量后发现几个板子:只要上电,就会误动作。结果发现是按键短路,上电启动后,误认为是一次按键操作。后期就把按键代码改了加了一个上电状态判定:上电判断按键是不是短路了。

1:一楼一个:多年年前自己写的:支持正常的单击,双击,长按,软件中可以直接通过定义屏蔽双击和长按。使用方法简单。
        a:用结构体定义了一个按键的属性。
        b:可以定义无数多个按键。
        c:每个按键的属性不一样,比如按键A,长按时间是1S,按键B可以定义长按时间为5S。
        e:支持组合键:把一个按键长按的时间设计短点,然后在应用的:当检测到另一个按键按下的时候,判断这个按键是否处于长按中。

2:二楼一个,也大概是两年前写的,一直在用,后来花了几天时间仔细的研究了按键的应用:使用方法简单,按键状态通过属性直接获得。
        a:有的按键是按下起作用,有的按键是弹起起作用。
        b:在多击事件中,有的是立刻起作用,有的是:确认后起作用。
                i:确认后起作用:比如一个按键:单击是一个功能,双击是一个功能。那么在判断上当发现按键按下一次后,需要超时等待确定没有第二次的按键按下,才可以判断是单击。同理三连击...
                ii:立刻起作用的:常用的windos中按下字母按键或者鼠标,就不需要判断。仔细观察发现:双击鼠标,其实是执行一次单击的。
                iii:如果按键用于:温度的增加,单击是增加1,双击是增加10,长按是一直增加,也是立刻起作用。
        c:多连击事件,有时候,一个按键功能比较多,可以需要支持:单击,双击,三击,五击。。
        d:组合键。类似于win的CTRL+XXX键。二楼,可以通过在应用层,用判断的方式实现。(比如设置按键A的长按时间比较短,然后在应用中,假设有B按下,再判断A的状态是不是长按。)
        e:双击,长按的时间可以设置。同时也可以屏蔽。
        f:按键取消功能(这个代码还没有实现),大家有没用用过,一种可以取消的按键的用法,比如:单击一个功能,长按一个功能(假设长按判定是5S)。单击后如果不松手,过个几秒(不超过5S)再松手,系统会不会执行单击的判定!

下面为第一种的代码(第二种的代码在二楼):
  1. 首先是定义:利用状态机的思想,定义了几种按键的状态。
  2. typedef enum
  3. {
  4.         keyOriginalStatus = 0,  // 解决按键一直被按下的问题
  5.         keyUp = 1,
  6.         keyPress = 2,
  7.         keyPressButNotReachLongPress = 3,
  8.         keyLongPress = 4,
  9.         keyDoublePress = 5,
  10.         doublePressKeyNotUp = 6,
  11. }keyStatus;
  12.        
  13. typedef struct{
  14.         keyStatus Status;
  15.         u16 longPressCount;  // 从按键按下到认为长按的计数!
  16.         u16 doublePressCount; // 用于确定双击的时间间隔!
  17.         u16 longPressThresholdValue; // 用于定义长按的判断时间。为0时禁止长按功能。
  18.         u16 doublePressThresholdValue; // 用于定义双击的判断时间。为0时禁止双击功能。
  19. }keyProperty;
复制代码

然后是主代码:
/**
* @brief 按键属性初始化,建议20ms的扫描时间。双击时间建议设置为:500ms,长按为1000ms
*/

void keyInit( keyProperty* key,u16 doubluePressThresholdValue, u16 longPressThresholdValue )
{
        memset(key,0,sizeof(keyProperty));
        key->doublePressThresholdValue = doubluePressThresholdValue;
        key->longPressThresholdValue = longPressThresholdValue;
}
/**
* @brief 下面程序应用于循环扫描10ms-20ms和任务或者定时器中
* 输入按键引脚的值,然后处理按键的状态。尽量少关连实际的硬件。
*/
void keyDetect( keyProperty* key,_Bool keyCurrentValue )
{
        if( keyCurrentValue == 1 ) // 表示按键当前松开,实际中需要根据情况来定。
        {
                key->Status = keyUp;
                if( key->doublePressCount != 0)  // 双击用的是递减!
                {
                        key->doublePressCount--;
                }
        }
        else if( keyCurrentValue == 0 ) // 表示按键当前按下
        {
                switch( key->Status )
                        {
                                case keyOriginalStatus: // 这种状态也可以作为上电初始化的一种选择!
                                        key->Status = keyOriginalStatus;  // 仍然不变
                                        break;
                                case keyUp: // 只有两种可能:要么进入keyDown状态,要么进入doublePress,显然是根据时间来判断的。
                                        if( key->doublePressCount == 0 )  //时间已超时
                                        {
                                                key->Status = keyPress;
                                                key->doublePressCount = key->doublePressThresholdValue;  // 这个值会在日后递减,用于计时。参考Winddows的时间间隔默认为500ms
                                        }
                                        else
                                        {
                                                key->Status = keyDoublePress;
                                                key->doublePressCount = 0;  // 下次Press事件,不会从doublePress再次回到Double
                                        }
                                        break;
                                case keyPress:
                                        key->Status = keyPressButNotReachLongPress;
                                        key->longPressCount = 0;
                                        if( key->doublePressCount != 0 )
                                        {
                                                key->doublePressCount--;
                                        }
                                        break;
                                case keyPressButNotReachLongPress:
                                        if( key->doublePressCount != 0 )
                                        {
                                                key->doublePressCount--;
                                        }
                                        if( key->longPressThresholdValue == 0) // 此时在这个状态循环,不进入长按状态。
                                        {
                                                break;
                                        }
                                        else
                                        {
                                                key->longPressCount++;
                                                if( key->longPressCount >= key->longPressThresholdValue )
                                                {
                                                        key->Status = keyLongPress;
                                                        key->longPressCount = 0;
                                                }
                                                break;
                                        }
                                case keyLongPress:
                                        key->doublePressCount = 0;  // 进入了长按,就不允许进入双击!
                                        key->Status = keyLongPress;  // 不变
                                        break;
                                case keyDoublePress:
                                        key->Status = doublePressKeyNotUp;
                                        key->doublePressCount = 0; // 防止进入两次双击
                                        break;
                                case doublePressKeyNotUp:
                                        key->Status = doublePressKeyNotUp; // 不变
                                default:
                                        break;
                        }
        }
        else // 不应该出现这样的值
        {
                printf("Error:Key value unexpected! Line:%d,Fun:%s\n",__LINE__,__func__);
        }
}

最后是应用:只需要绑定一个按键,然后20ms循环,进入程序读就行了。
  1. keyPropertyV2 KeyUp; //
  2.         keyInitV2( &KeyUp,25,50);
  3.         keyDetectV2( &KeyUp, GPIOC->IDR & 0x01 ) // PC1引脚的电平值输入到检测函数中。
  4. switch( KeyUp.Status ) // 可以这里进行处理。
  5. {
  6. ...
  7. }
  8. ;
复制代码



阿莫论坛20周年了!感谢大家的支持与爱护!!

曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……

出590入1001汤圆

 楼主| 发表于 2020-7-14 13:30:19 | 显示全部楼层
本帖最后由 SUPER_CRJ 于 2020-7-14 15:04 编辑

二楼抛砖:
首先说明:二楼的代码比一楼的多了:
1:多击事件判断。
2:按下/弹起起作用判断。
3:立刻起作用,还是确认起作用。
为什么是砖。
原因简单:虽然功能多点,但是有一个小问题,就是当时时间比较紧。而且说实话,里面的逻辑关系搞的我头疼。
于是我用了一个简单的方法增加多击事件:从双击到三击的状态其实和三击到四击的代码是类似的。如果结构设计的好,这部分代码算是比较简单的。
于是双击到三击用的100行代码,然后复制粘贴到三击到四击,把里面的状态改下,然后产生了:1000多行的代码。
最多支持到8连击。(这个代码我一直在用,效果是不错的,没有bug!)

代码在附件中,使用方式和一楼一样。

最终说明下:代码可以改的更小,但是要重新设计一些结构,比较费神。然后准备这几天改下代码,缩小的几百行。可以支持无限连击和按键取消功能。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入12汤圆

发表于 2020-7-14 13:32:26 | 显示全部楼层
支持楼主开源

出0入42汤圆

发表于 2020-7-14 14:38:59 | 显示全部楼层
支持楼主!按键输入虽然是个简单的操作,也是每个项目都必须的模块!

出0入0汤圆

发表于 2020-7-14 21:47:06 | 显示全部楼层
支持楼主~

出330入0汤圆

发表于 2020-7-14 22:05:27 来自手机 | 显示全部楼层
打赏20汤圆,把事情做到极致让人佩服。

出0入53汤圆

发表于 2020-7-14 22:41:36 | 显示全部楼层
感谢lz开源

出0入36汤圆

发表于 2020-7-15 01:12:43 | 显示全部楼层
支持楼主。  我一般枚举常量用全大写,变量名也不敢写这么长(偷懒不想输入那么多字母)

出0入0汤圆

发表于 2020-7-15 08:30:06 | 显示全部楼层
感谢楼主开源

出0入0汤圆

发表于 2020-7-15 08:48:18 | 显示全部楼层
请问楼主,支持矩阵键盘吗?网上很多不支持矩阵,都是单个GPIO控制的按键程序。

出16390入6832汤圆

发表于 2020-7-15 08:53:04 来自手机 | 显示全部楼层
打赏50汤圆!

庆祝论坛“打赏”功能实施, 现在开始发技术主题,可以获得打赏
https://www.amobbs.com/thread-5735948-1-1.html

出0入10汤圆

发表于 2020-7-27 22:21:54 | 显示全部楼层
收藏,感谢楼主开源

出0入0汤圆

发表于 2020-7-28 11:11:52 | 显示全部楼层
感谢楼主开源

出0入0汤圆

发表于 2020-7-28 11:34:36 | 显示全部楼层
感谢楼主,收藏备用

出0入0汤圆

发表于 2020-7-28 13:05:38 | 显示全部楼层
按键处理,mark。谢谢

出190入0汤圆

发表于 2020-7-28 14:00:21 | 显示全部楼层
本帖最后由 knight_sh 于 2020-7-28 14:03 编辑

楼主位的if-else好长...
我也借楼发一下我的按键状态图和代码吧,只实现了单击,双击,长按,不过要添加多按键的话完善现有状态图即可;
[编辑原因:修改错别字]

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2020-8-1 15:14:09 | 显示全部楼层
支持楼主~

出0入0汤圆

发表于 2020-8-17 11:01:20 来自手机 | 显示全部楼层
多谢两位分享,学习一下。

出0入0汤圆

发表于 2020-8-18 17:05:00 | 显示全部楼层
留个爪子,个人目前比较喜欢用MultiButton

出0入0汤圆

发表于 2020-8-18 22:49:23 来自手机 | 显示全部楼层
牛啊,留爪

出0入0汤圆

发表于 2020-8-19 05:58:35 | 显示全部楼层
希望支持休眠

出0入0汤圆

发表于 2020-8-19 16:59:21 | 显示全部楼层

多谢楼主分享,学习一下。

出0入0汤圆

发表于 2020-8-19 20:25:00 | 显示全部楼层
多谢楼主开源分享

出0入0汤圆

发表于 2020-8-19 21:54:23 | 显示全部楼层
MultiButton和这个比较类似

出0入0汤圆

发表于 2021-12-25 19:29:50 | 显示全部楼层
一个按键做多功能有时候客户会觉得非常麻烦
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-8-16 08:28

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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