当前位置: 首页 > news >正文

基于定时器的多种非阻塞式按键(单击、双击、长按)

本篇文章来源于B站UP主江协科技,基于视频内容,本人再做一些整理与学习心得。

在学习STM32时,我们常常会需要使用按键来做许多功能切换事件,那么如何利用有限的按键个数,来实现多种不同的功能呢?

本篇文章主要介绍如何利用定时器来实现花式按键。

首先我们可以整理出一个大致的程序框架,在这个框架下,我们代码思路会更加清晰。

该代码框架就是基于定时器,在不同的时刻给不同的事件置不同的标志位,这样就能根据标志位来判断出按键事件了,然后根据不同的按键事件执行对应的操作即可。

那么重点就在如何在不同的时刻给不同的事件置不同的标志位,这里我们利用状态转移图来帮助我们理清思路,那么程序就跟着状态转移图来执行即可。

由下图可知,当按键按下的一瞬间,DOWN事件置1,同时定时器开始记录HOLD(按住不松开)的时间,假设该状态为S1(按键按下瞬间),此时要开始等待的事件为1.长按时间到,2.按键松开。如果先等到的事件是长按时间到,就说明这次按键为长按,如果先等到的事件是按键松开,那就说明此次事件要么是单击,要么是双击。

我们先来看,如果先等到的事件是按键松开,那么到底是单击还是双击呢?这就需要我们在按键松开的一瞬间计时,假设该状态为S2(按键松开瞬间),此时要开始等待的事件有两件1.双击时间到2.双击时间内按键再次按下。如果先等到的事件是双击时间到,当双击时间到的一瞬间SINGLE标志位置1,就说明这是单击按键。反之,如果先等到的事件是双击时间内按键再次按下,在按键按下瞬间DUBLE标志位就会瞬间置1,说明这是双击按键。

其次再来看,如果先等到的事件是长按时间阈值到,就说明这是长按事件,当长按时间一到,LONG标志位就会置1,然后就会开始等待重复时间阈值,每次到达设定的重复时间阈值之后,REPEAT标志位就会置一次1,直到检测到按键松开。

                                                                  按键判断示意图

由上图可以看出,第一次计时是为了判断是否为长按,第二次计时是为了判断是否为单击。经过以上分析之后,我们便可以画出程序的状态转移图了。

                                                                      程序状态转移图

我们整理好大致框架之后,便可以按照这个状态转移图开始编写代码了。首先规定好相关宏定义,1表示按键按下,0表示按键未按下。2双击时间为200ms,长按时间为2000ms,重复时间为100ms。

#define KEY_PRESSED				1
#define KEY_UNPRESSED			0#define KEY_TIME_DOUBLE			200
#define KEY_TIME_LONG			2000
#define KEY_TIME_REPEAT			100

这里省略初始化代码的讲解,定义getstate函数来判断每个按键的引脚状态。

uint8_t Key_GetState(uint8_t n)//获取按键状态
{if (n == KEY_1){if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0){return KEY_PRESSED;}}else if (n == KEY_2){if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0){return KEY_PRESSED;}}else if (n == KEY_3){if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13) == 1){return KEY_PRESSED;}}else if (n == KEY_4){if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_15) == 1){return KEY_PRESSED;}}return KEY_UNPRESSED;
}

做好相关规定后,便可以开始写状态转移程序了。

这里有四个按键,所以要定义好大小为4的数组变量。循环对相应的按键进行相关操作。time表示设置好的时间阈值,可以表示双击、长按、重复时间阈值。当我们设置好时间阈值之后,程序下一次进入到中断函数中就会执行--的操作,因为程序进入中断函数的时间很快,1ms进入一次,所以这里的时间差可以忽略不计。

当检测到对应按键按住不放(当前状态和上一次状态都是按下)时,HOLD标志位置1(|操作符),松手时,该标志位清零(&操作符)。

当检测到对应按键按下时(当前状态为按下,上一次状态为未按下)时,DOWN标志位置1(|操作符)。

当检测到对应按键松手时(当前状态为未按下,上一次状态为按下)时,UP标志位置1(|操作符)。

在状态s=0时,只需要等待按键按下,当按键按下时,设置长按阈值时间,进入s=1状态,并等待两个事件。

1.按键松手

如果先等到按键松手,设置好双击阈值时间。并进入到s=2,继续等待两个事件。

如果双击时间先到,那么判断这个事件为单击事件,SINGLE置1.完成判断后s=0.继续进入空闲状态。

②按键按下

如果按键先按下,进入s=3状态,同时判断这个事件为双击事件,DOUBLE置1,然后完成判断后等待按键松手后s=0,进入空闲状态。

2.长按时间到

如果长按时间先到,说明这个事件为长按事件,此时设置好重复阈值时间,LONG置1。再进入s=4状态。在s=4状态中,需要等待两个事件。

①按键松手

按键松手,s=0,进入空闲状态。

②重复时间到

REPEAT标志位置1,并将重复时间清零,继续进入s=4状态,直到按键松手后进入s=0空闲状态。

void Key_Tick(void)
{static uint8_t Count, i;static uint8_t CurrState[KEY_COUNT], PrevState[KEY_COUNT];static uint8_t S[KEY_COUNT];static uint16_t Time[KEY_COUNT];for (i = 0; i < KEY_COUNT; i ++){if (Time[i] > 0){Time[i] --;}}Count ++;if (Count >= 20){Count = 0;for (i = 0; i < KEY_COUNT; i ++){PrevState[i] = CurrState[i];CurrState[i] = Key_GetState(i);if (CurrState[i] == KEY_PRESSED){Key_Flag[i] |= KEY_HOLD;}else{Key_Flag[i] &= ~KEY_HOLD;}if (CurrState[i] == KEY_PRESSED && PrevState[i] == KEY_UNPRESSED){Key_Flag[i] |= KEY_DOWN;}if (CurrState[i] == KEY_UNPRESSED && PrevState[i] == KEY_PRESSED){Key_Flag[i] |= KEY_UP;}if (S[i] == 0){if (CurrState[i] == KEY_PRESSED){Time[i] = KEY_TIME_LONG;S[i] = 1;}}else if (S[i] == 1){if (CurrState[i] == KEY_UNPRESSED){Time[i] = KEY_TIME_DOUBLE;S[i] = 2;}else if (Time[i] == 0){Time[i] = KEY_TIME_REPEAT;Key_Flag[i] |= KEY_LONG;S[i] = 4;}}else if (S[i] == 2){if (CurrState[i] == KEY_PRESSED){Key_Flag[i] |= KEY_DOUBLE;S[i] = 3;}else if (Time[i] == 0){Key_Flag[i] |= KEY_SINGLE;S[i] = 0;}}else if (S[i] == 3){if (CurrState[i] == KEY_UNPRESSED){S[i] = 0;}}else if (S[i] == 4){if (CurrState[i] == KEY_UNPRESSED){S[i] = 0;}else if (Time[i] == 0){Time[i] = KEY_TIME_REPEAT;Key_Flag[i] |= KEY_REPEAT;S[i] = 4;}}}}
}

写好以上状态转移程序后只需要每隔固定的时间调用该函数即可,我们利用定时器资源设置好1ms的中断时间,在中断函数中不断调用Tick函数即可。

void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){Key_Tick();TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}

以上就是利用定时器检测按键的不同状态的程序执行逻辑。

http://www.dtcms.com/a/555348.html

相关文章:

  • 网站文章排版工具全网营销外包全网天下
  • 鞍山外国网站制作在东莞找工作上哪个网站
  • 百度云建站网站建设js图片展示网站
  • 北京时间网站建设店面门头设计网站
  • 支付宝支付业务
  • SQL多表查询优化实战技巧
  • 501-Spring AI Alibaba Graph Reflection 功能完整案例
  • 网站分类主要有哪些wordpress安装ssl证书
  • 网站设计代码沈阳网站建设工作
  • 成功备案的网站增加域名wordpress文章付费阅读
  • 辽宁高端网站建设不忘初心网站建设
  • 如何给网站增加图标seo在线论坛
  • 做网站什么最重要网站建设中技术程序
  • 固定IV在AES加密中的致命隐患
  • jsp 哪些网站网站运行团队建设
  • 做网站完整视频杭州网站建站公司
  • 国贸行业 网站建设备案域名购买完过户简单吗
  • 教做月嫂的网站有吗网站公司成本
  • app的网站域名php做网站后台有哪些框架
  • Linux学习笔记--UART子系统
  • 新闻发布网站如果做wordpress漏洞检测
  • 网站头部设计优化青岛做外贸网站建设
  • 东城专业网站建设公司创意二维码制作网站
  • 网站的用户登录一般怎么做的计算机前端开发要学哪些软件
  • 上海网站建设 虹口做个小程序需要多少钱
  • 自适应响应式网站源码城阳网站开发公司
  • 网站源码建站教程wordpress 最新 调用
  • 静态网站可以做哪些网站开发成本计算
  • 蚌埠网站建设费用郑州网站建设网站
  • 国外直播做游戏视频网站有哪些php做的网站首页是什么文件