8051识别按键单击双击和长按
Posted 汀洲杜若
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了8051识别按键单击双击和长按相关的知识,希望对你有一定的参考价值。
要识别一个按键的单击、双击和长按,至少需要哪些信息呢?这得先考察一下这些动作的具体过程。下图灰色部分为按键抖动,蓝色竖线代表单片机对按键所连接引脚的某次电平采样。假设按下按键后与单片机相连的引脚由高电平变为低电平。
如果是单击,那么就表现为图上的【ABCDE】和【EFGMHⅠ】,只不过这里的【E】点高电平要持续一段时间。如果是双击那么【E】的持续时间就要低于某个自定义的阈值。如果是长按,则是【ABCJKLGMHⅠ】,相当于一次单击操作【EFGMHⅠ】中按下状态的持续时间被延长了。所以我们需要一个状态码和一个计数器。状态码指示当前按键对应于上图的哪个位置,计数器告诉我们按键在这个位置上保持了多久。
很容易想到一个思路,就是给定时器设置10毫秒的中断,定时器一旦溢出就在中断服务例程中依照引脚的电位更新按键的状态码和计数器。虽然一个设备可能有很多状态,导致代码量的增加,看似对中断服务例程很不友好,实际上中断服务例程每次只执行对应设备当前状态的代码,和其它状态的处理逻辑无关,所以在整体上几乎无影响。
这么做的另一个好处是可以识别多个按键,如果系统中有十几个按键都应具备单击、双击和长按这三种功能,就在定时器的中断服务例程中使用一趟循环代码对每个按键的状态码和计数器进行逐个更新。假设更新一个按键的某状态平均消耗二十条指令,那么即使同时有十个按键都具备上述三种功能,时间开销也在两百指令周期左右。这我可以接受。实际上更新按键的状态用不着二十条指令。
简单的单片机程序通常采用下面的编写方法(听别人说的,是不是“通常”我也不清楚,反正我是这么做的)。编写程序之前还有一件事:我希望按键被长按的时候,每过一小段时间若仍被按着不撒手,那么就需要通知主循环“这个按键仍然没有被释放”。这就好比你按遥控器的时候按着音量键不放,它就自己慢慢调整音量值。
下面是我进行试验的代码,只涉及一个按键,连接在51单片机的P3.3口,按下是低电平状态。主循环逻辑很简单:初始化后就等待按键事件并处理之。定时器中断服务例程中对按键动作的识别部分(见下文)似乎有些难懂,这是使用状态机模型不可能避免的。重点不在于程序怎么写,而在于写程序的人应对按键的动作过程和程序的设计方法有自己的理解。
状态零:一开始什么都没发生,直到某一次发现按键出现了低电平,此时准备进入状态一。
状态一:距离状态零已过去十毫秒,若此时引脚是高电平,说明状态零检测到的低电平并不是一个稳定的按键动作,那么还回到状态零;若引脚是低电平说明在状态零检测到低电平后经过十毫秒依然检测到低电平,那么可以认为按键被稳定地按下了,随即进入状态二。也就是用软件的方法消除按键的抖动,随后的状态三、状态六都类似。因为我们要记录按下按键的时长信息以区分长按和短按,所以进入状态二之前要初始化计数器。
状态二:若引脚变为高电平,那么我们认为按键可能要被释放了,随即进入状态三;若一直没有检测到高电平,那就说明按键一直被按着不松手,此时计数值也是每过十毫秒增加一,当增加到预设的阈值0x30时就不用增加了,因为我们可以确定触发了一次长按,所以提交这一按键动作并准备进入状态五。随后状态五也要对十毫秒进行计数,每次达到阈值就递交一个按键动作以通知主循环按键依然未被释放,并不断循环。这就类似上文的按遥控器调音量的例子。检测到长按松开后进入状态九消除抬起按键时的抖动。
状态四记录了一次单击之后按键释放的时间,若计数在阈值内检测到引脚又变成低电平,那么这就是双击动作。状态七和八配合状态四完成对双击的完整识别。
如果有多个按键,那就把46~118行放进循环语句中对每个按键的状态和计数器逐个更新。可以看到,每次中断服务例程要执行的代码量并不大。另外,24~25行的写法只是为了试验,本质上是单字节的缓冲区,中断提交数据,主循环拿走数据。若处理多个按键则可以用队列。
以上是关于8051识别按键单击双击和长按的主要内容,如果未能解决你的问题,请参考以下文章
微信小程序实现单击双击和长按forEachclearTimeoutsetTimeoutsplitsetClipboardDatagetClipboardDatashowToast