懒人独立按键-单击,双击和长按功能拿来就用(易移植,可增删按键)

三种功能的独立按键

效果

设计一款能实现单击,双击和长按的独立按键程序,并且保证程序有着良好的移植性,支持多个按键。独立按键的扫描程序还应该是非阻塞的,也就是说不能用到内部延时。按键扫描程序通过调用回调函数进行逻辑处理,这样可以使耦合度极大的降低。最后,经过简单的移植就能在不同平台上轻松实现相同功能。呜呜呜,妈妈再也不用担心我要写一天的按键逻辑了

程序逻辑

  1. 提取所有按键的当前信息,并且生成两个信号。一个是触发信号trg,当按键按下是变为1,等下一次扫描时不管按键状态,直接清零,相当于一个脉冲信号。另一个是持续信号cont,随着按键按下会一直置1,等按键松开才会置0。这两个变量的每一位对应一个实体按键,由于我使用的是无符号短整形,所以最大可以有16个按键。实现原理参考:http://t.csdn.cn/3rftG(我看的那个博客已经被删了,找了一个差不多的)
  2. 首先遍历触发信号trg,判断是否有按键按下。如果没有直接结束这次按键扫描;如果按下了,就先记录此时的按键位置,然后开启一个计数器。
  3. 如果开启了计数器,每次扫描就加一次值。如果在一定时间内,检测到同一按键的触发信号再次触发,判断为双击。如果超出这个时间而且没有持续信号,判断为单击。如果持续信号超过更长的一个时间就判断为长按。要注意2和3是互斥的,同时运行的应该只有一个。按键事件对应的标志规则为:按键数×3+n。单击n=0,双击n=1,长按n=2。例如:按键0发生了长按时间,0*3+2=2,那传给按键回调函数的就是2。

那么现在两个问题: 1.如果双击第二次不松,判断为什么?2.如果在长按为到时间,但是又超过了双击判断的时间,判断为什么?这两个问题是现实存在的,但是并不能称为BUG。毕竟每个人的理解不一样,处理的方式也不就不一样。第1个我还是判断为双击,并且不会再判断为长按,第2个就判断为单击。到此,程序在理论上就完成了(其实是先花一下午写程序,再来分析的)

程序源码

头文件:

/**
 * @name	  three_keys
 * @brief	  三种功能的独立按键
 * @feature	  支持单击,双击和长按。
 * @note	  最多支持16个独立按键
 * @author	  yyj
 * @date	  2022/3/27
 */

#ifndef __THREE_KETS_H
#define __THREE_KETS_H
#include "stm32f10x.h"

#define	KEY_NUM			3		//按键个数
#define	DOUBLE_TIME		300		//判断双击最长的间隔 单位:ms
#define	CONTINUE_TIME	2000	//判断长按的最短持续时间 单位:ms

char Key_Init(short fre);
void Key_Scan(void);
void Key_CallBack(int key);

#endif

c文件:

#include "three_keys.h"

unsigned short trg = 0;			//代表触发,每一位是一个单脉冲信号
unsigned short cont = 0;		//代表是否连续按下,按键没松开,对应的位就不清零
unsigned short key_time=0;		//按键计时变量
unsigned short double_time=0;	//实际判断双击要延迟的周期
unsigned short continue_time=0;	//实际判断长按要延迟的周期


/**
 * @name	  Key_Init
 * @brief	  初始化按键的io口
 * @param	  fre:按键扫描频率
 * @return	  0:初始化正常
 *			  1:传入的参数错误
 * @note	  扫描频率不用完全精确,视具体使用情况而定,主要是为了
 *			  确定双击和长按的时间间隔。误差大一点,只要使用没问题就行
 */
char Key_Init(short fre)
{
	float num;
	GPIO_InitTypeDef GPIO_InitStruct;
	
	/****移植需要修改的地方****/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE, ENABLE);
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4;//PA0
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;//上拉输入
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOE,&GPIO_InitStruct);//初始化PA0
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPD;//下拉输入
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;//PA0
	GPIO_Init(GPIOA,&GPIO_InitStruct);//初始化PA0
	/**************************/
	
	
	if(fre<1 || fre >1000)
		return 1;
	
	num=DOUBLE_TIME*fre/1000.0;
	if(num>(int)num+0.5)	//四舍五入
		double_time = (int)num+1;
	else
		double_time = (int)num;
	
	num=CONTINUE_TIME*fre/1000.0;
	if(num>(int)num+0.5)	//四舍五入
		continue_time = (int)num+1;
	else
		continue_time = (int)num;
	
	return 0;
}

/**
 * @name	  Key_Scan
 * @brief	  扫描按键,判断按键事件并调用按键回调函数
 * @note	  不同平台移植只需要更改前面部分代码,要保证按键没按下时
 *			  读取的值为1,按下为0。
 *			  这是非阻塞式按键扫描函数,不要阻塞使用
 */
void Key_Scan()
{
	unsigned short key_val=0xffff, read_data,i;
	static unsigned char key=10;//暂时保存是哪个按键按下的
	
	/****移植需要修改的地方****/
	key_val =(key_val<<1) + (!GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0));//取反是因为这个按键接的3.3v,为了和另外两个接地的按键数据保持一致
	key_val = (key_val<<1) + GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3);
	key_val = (key_val<<1) + GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4);
	/*************************/
	
	//以下是三行代码按键//
	read_data = key_val^0xffff;//异或操作,获取按键位置
	trg = read_data & (read_data ^ cont);//保存按键信息,且每次按下只保留一次,即使下次按键不松也清零
	cont = read_data;//按键不松,这一直有值,且对应了按键位置
	
	
	if(key_time==0)	//前面没发生过按键按下
	{
		for (i=0;i<KEY_NUM;i++) //循环遍历每个按键的信息
		{
			if(trg&(0x01<<i)) //对应位置的按键按下了
			{
				key_time=1;//让计时变量开始计时
				key=i;
				break;	//仅处理一个按键
			}
		}
	}
	else //前面发生过按键按下,现在判断是什么事件
	{
		key_time++; //计数加一
		if(key_time<=double_time) //没超过双击判断时间
		{
			if(trg&(0x0001<<key)) //检测到相同按键再次按下
			{
				Key_CallBack(key*3+1);//判断为双击,并调用回调函数,传入相关按键事件
				key_time=0;	//结束计数
				key=0;//清除暂时的按键信息
			}
		}
		else if(key_time>double_time && ((cont&(0x0001<<key))==0)) //超过双击判断时间,并且没有连续按
		{
			Key_CallBack(key*3);//判断为单击,并调用回调函数,传入相关按键事件
			key_time=0;	//结束计数
			key=0;//清除暂时的按键信息
		}
		else //其他情况可能是长按
		{
			if((cont&(0x0001<<key))==0) //如果松掉了表示不是长按
			{
				Key_CallBack(key*3);//还是判断为单击,并调用回调函数,传入相关按键事件
				key_time=0;	//结束计数
				key=0;//清除暂时的按键信息
			}
			else if(key_time>continue_time) //超过一定时间表示为长按
			{
				Key_CallBack(key*3+2);//判断为长按,并调用回调函数,传入相关按键事件
				key_time=0;	//结束计数
				key=0;//清除暂时的按键信息
			}
		}
	}
}

/**
 * @name	  Key_CallBack
 * @breif	  发生按键事件时调用的函数
 * @param	  key:按键事件标志,规则为:按键*3+n
 *			  单击n=0,双击n=1,长按n=2。按键从0开始
 *			  为第一个 
 */
void Key_CallBack(int key)
{
	printf("%d\r\n",key);
}

移植事项:

  1. 首先根据自己的需求修改头文件的内容,由于过于简单,在此不详述
  2. 本程序是在stm32f103zet6单片机上实现的,移植时需要参考自己单片机的驱动与按键引脚修改Key_Init()和Key_Scan()前面与硬件有关的部分。
  3. Key_Init()的参数fre是按键扫描频率,也就是一秒扫描按键的次数,不能大于1000或者小于1。这个参数不用太精确,只是用来告诉按键程序现实的时间与程序执行次数的关系。这是为了判断双击和长按所必要的。如果觉得实现效果不理想可以适当加或者减fre的值。
  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
### 回答1: 按键精灵懒人插件是一款针对Chrome浏览器的实用插件,可以帮助用户进行自动化操作,提高生产力。 该插件功能强大,可以支持自动填写表格、自动点击、自动登录、自动抢红包、自动下单、自动刷票等多种操作,让用户摆脱重复枯燥而又耗时的操作,同时还能够提高生产效率和工作效率。 由于其功能强大,使用范围广泛,因此在很多企业和机构中也得到了广泛的应用。特别是在一些数据处理、报表填写等需要大量重复性工作的场景中,可以大大提高工作效率和准确性。 同时,在使用时,我们还需要注意它的使用场景,确保其合法合规,并严格遵守相关政策和法律法规。除此之外,还需要时刻保持谨慎和警惕,确保自己的机密信息和个人隐私不被泄露出去。 总而言之,作为一款懒人插件,按键精灵给用户带来了极大的便利和实用性,同时也需要合法合规地使用,以免造成不必要的损失和问题。 ### 回答2: 按键精灵懒人插件是一款能够自动化操作浏览器和电脑的工具。它可以在浏览器中自动完成各种重复性、繁琐的操作,如点击、输入、切换标签页等,提高了用户的生产力和工作效率。 该插件支持Chrome浏览器,并且可以安装在Windows和Mac OS平台的电脑上。 安装后,用户可以创建自己的脚本,用于执行一系列操作。脚本可以通过简单的录制和编写来创建,而不需要复杂的编程知识。 除了创建脚本外,按键精灵还支持分组管理和快捷键设置。用户可以将脚本按照类别分组,并为它们设置不同的快捷键,方便快速启动。 此外,按键精灵还提供了一些高级功能,如循环执行、延时执行和条件执行等。通过这些功能,用户可以更加灵活地控制脚本的执行结果。 值得一提的是,按键精灵还提供了一个在线脚本分享平台。用户可以在这个平台上搜索并下载其他用户共享的脚本,大大节省了脚本编写的时间。 综上所述,按键精灵懒人插件是一款功能强大的浏览器插件,它可以帮助用户将繁琐重复的操作自动化,提高工作效率。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值