开源键扫例程--- 无须延时消抖等待,能稳定可靠地一体满足普通、短按(单击/双击)、长按、组合等应用需求的键盘扫描程序

2019-07-20 22:07发布

本帖最后由 warship 于 2018-9-18 14:09 编辑

//////////////////////////////////////////////////////////////////////////////////         
本键盘扫描模块的特点:
一、使用灵活:一体实现按键的普通、单击、双击、长按、保持以及组合等功能,无须事前为每个按键每种键值逐一进行宏定义,也无须逐一编写各事件的条件判断,                     
                     只须为需要的按键事件编写相应的响应代码即可,同时留有特殊键组合等的扩展接口;
                     可以选择每一按键事件的处理实时性,从而能够使强实时性的紧急按键优先得到处理,可自由选择中断处理及查询处理或二者混合的处理方式,
                     灵活适配使应用项目能够兼备按键的强实时性要求以及超长(主循环执行一遍的时间长达1秒以上的)程序的适应性。

二、注重通用:模块设计时注重通用性,按键事件(键值)依简单易懂的标准事件格式编写;除能满足几乎所有按键应用需求外,在按键数量上,
                     从少到2-4个按键直到最大32个按键(包括端口直联、行列式矩阵、矩阵加直联混合)都可适用。(注:新写了一个4*4矩阵加4键直联混合共20个键的例子参见153楼)


三、稳定可靠:后台智能抖动消除、按键干扰杂波滤除措施有力,获取按键稳定可靠,不会产生重复按键,即使在CPU非常繁忙时也不会漏失按键。

         
四、移植简便:所有可调整参数(数量不多)均以宏定义列出,除与硬件相关(按键个数及连接端口)的部分须根据具体系统修改外,其它均无须变化,很容易移植。
                     程序可读性强,注释详尽丰富,其中包括函数调用关系及详细运用修改说明,如有未尽事宜,可提出探讨,本人尽量解答修改。


五、高效节能:消抖无须延时等待,同时采取自适应变频扫键、键盘闲置检测、消抖读键双进程周期差异等多项智能措施尽量减少占用CPU的计算资源。


//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//测试平台:ALIENTEK战舰STM32开发板
//按键驱动代码           
//正点原子@ALIENTEK
//引用请注明出处:http://www.openedv.com/forum.php?mod=viewthread&tid=277263,有问题可在本帖中提出讨论,谢谢。
//在网友@学习stm32f4 的建议下,部分源码上传到了https://github.com/ShuifaHe/STM32.git,新司机上路,请多关照。如果觉得对您有用的话,请按 “星” 号点一下赞
//修改日期:2018/9/1
//版本:V2.2,本帖研讨过程详见2楼,鸣谢相关坛友,感谢@正点原子 的支持肯定。
//Made by warship
//////////////////////////////////////////////////////////////////////////////////
下面给出经由原子试验3改造测试通过的程序范例:
回复后可获取解压密码,谢谢支持。游客,如果您要查看本帖隐藏内容请回复





友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
53条回答
warship
1楼-- · 2019-07-21 01:15
wdgao 发表于 2018-8-18 12:19
感谢楼主不断地更新、补充,不知楼主能否利用该按键实现思想将原子的按键实验改写一下,以便大家对比研究 ...

已分享工程,在一楼,欢迎下载测试。
warship
2楼-- · 2019-07-21 01:53
本帖最后由 warship 于 2018-9-2 12:56 编辑
学习stm32f4 发表于 2018-8-18 20:30
这个阻容消抖电路是抄自*火的板子,电路参数一样,只是多了个斯密特触发器,估计能用吧。(待证)
也可以 ...

(借楼)
本帖更新历程:

(2018年8月15日)受三行键盘扫描程序http://www.openedv.com/forum.php ... hlight=%C8%FD%D0%D0
的启发,使用SYSTICK系统嘀哒消抖,具体尝试了短按、长按、组合等实现方法,贴出源码供网友进行讨论;

(2018年8月18日)  应坛友@wdgao 的要求,将原子的按键实验(实验3)进行改写,分享完整工程,供网友下载测试;

(2018年8月23日) 最新附件优化了消抖代码,并采取了自适应变频扫键措施,在原子战舰开发板上反复测试通过,稳定可靠;

(2018年8月26日) 受到坛友@leiyitan 所提供信息https://github.com/0x1abin/MultiButton的启发,借鉴加入了可选的状态机组件,可以非常方便地实现单击、双击、
长按、保持等功能;

(2018年8月27日) 将读键变成后台队列处理,确保最大限度地容忍主循环长时间才查询一次所带来的影响,CPU非常忙碌时也绝对不丢键。
同时,统筹优化了自适应变频扫键、智能节省CPU算力等措施代码;

(2018年8月29日) 调整程序结构,使强实时性的紧急按键可优先得到处理,从而兼具按键的强实时性要求以及适应超长(主循环执行一遍的时间长达1秒以上的)程序。
弱化三行功能,采用带参宏一次性定义了几乎所有的按键宏值,使得移植者无须为各类按键定义大量宏值,直接处理标准化格式的按键事件即可。
和网友@xiatianyun探讨后进一步加固了消抖代码,最大限度地确保按键的稳定读取;

(2018年8月30日) 代码进行了一些小的调整,进一步增强通用性、可读性及可移植性。定版本为2.2;

(2018年9月1日) 增加了大量注释,其中包括详细的运用、移植修改方法;

(2018年9月2日) 新写了一个4*4矩阵加4键直联混合共20个键的例子(放在153楼/154楼),供使用行列式矩阵等复杂键盘开发者参考;

***********************************************************************************************************************0901X317
支持连续按与不支持连续按的区别:

支持连续按:按下不松开则认为是连续有效。具体过程:就是检测相应按键,只要是在键按下的状态,就执行相应的操作,持续按下则持续不断地(多次)执行用户相应的操作。

不支持连续按:按下不松开则认为是一次有效的。具体过程:就是检测相应按键,只有按键在松开后才被认为是一次有效按键,每次按键只执行一次用户相应的操作。

本按键扫描程序可轻松支持这两种模式。

我们一般的程序需求常常是后者,即每次按键只执行一次用户相应的操作。
而这种所谓不支持连续按(按键一次只执行一次操作)实际上又可以有两种实现方式:
一种是只要按下就执行操作(反正一般按键你总是要松开的,我先执行了再说,这种表现出来的就是响应速度快,用户体验好)
另外一种处理方式就是严格等按键释放后才被认为是一次按键,也就是说你按下键时是没有反应的,等你松开按键后才执行动作。显然用户的体验是反应速度慢。
这两种模式本键盘扫描程序都支持。
事实上等待按键释放后才被认为是一次按键还可以派生出一种按键,那就是长按,只有按下不松开超出指定时间(如2秒以上)就被认为是一次长按事件成立。
长按成立后又引申出两种模式:即长按超过指定时间后还不释放,是执行一次操作还是只要再不放就连续执行操作。而连续执行操作又可分为是真正连续操作还是每间隔一个短时间才执行一次操作(最后这种比较典型的应用就是大家熟悉的电子表调整时间的按键,比如“+”键,按下不放超过时间后变成连续增加,而这个连续增加实际是间隔比如100ms增加一次的,如果真正无间隔地连续你就来不及看清楚和反应了)
所有这些本键盘扫描程序都可轻松支持。



但从上面的描述可知,用户对按键的具体要求是千差万别的,我不可能把所有这些都罗列在例程里(并且不同需求之间还存在相互冲突),大家根据具体应用需求进行按键规划后,只须修改Get_Key()函数相应的判断条件就可以了。



warship
3楼-- · 2019-07-21 04:00
 精彩回答 2  元偷偷看……
warship
4楼-- · 2019-07-21 07:59
//*************** key.c 文件相应内容 ********
//按键初始化函数
void KEY_Init(void) //IO初始化
{
        GPIO_InitTypeDef GPIO_InitStructure;
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);

        /* PD8,9,10,11按键输入*/
        GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;                // 下拉输入
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;        //50M时钟速度
        GPIO_Init(GPIOD, &GPIO_InitStructure);
        /* PD12,13,14,15按键扫描输出*/
        GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;        //推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;        //50M时钟速度
        GPIO_Init(GPIOD, &GPIO_InitStructure);
       
        /* PD3,4,5,6按键输入,对应四个方向键*/
        GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;                //上拉输入
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;        //50M时钟速度
        GPIO_Init(GPIOD, &GPIO_InitStructure);
}


//硬件按键编码
//以上述20键为例(最大暂支持32键,对于键少的系统 KeyS_Type可定义为u16或u8)
KeyS_Type GetHalKeyCode(void)
{
        KeyS_Type ktmp=0;
        if(!KB_RIGHT_IN)         ktmp|=1<<KB_RIGHT;
        if(!KB_DOWN_IN)         ktmp|=1<<KB_DOWN;
        if(!KB_LEFT_IN)         ktmp|=1<<KB_LEFT;
        if(!KB_UP_IN)                 ktmp|=1<<KB_UP;
                //扫描行列式键盘
        KEY_OUT_LINE_NULL;
        KEY_OUT_LINE1;
        ktmp |= KEY_IN0<<4;                //或者直接KEY_IN>>4

        KEY_OUT_LINE_NULL;
        KEY_OUT_LINE2;
        ktmp |= KEY_IN0<<8;                //或者直接KEY_IN

        KEY_OUT_LINE_NULL;
        KEY_OUT_LINE3;
        ktmp |= KEY_IN0<<12;                //或者直接KEY_IN<<4

        KEY_OUT_LINE_NULL;
        KEY_OUT_LINE4;
        ktmp |= KEY_IN0<<16;                //或者直接KEY_IN<<8

        return ktmp;
}
warship
5楼-- · 2019-07-21 11:34
 精彩回答 2  元偷偷看……
warship
6楼-- · 2019-07-21 12:11
单片机32 发表于 2019-3-25 18:26
你好,求解压密码

回复后应该可以看到哦.

一周热门 更多>