独立按键中双击,短按,长按一次实现,支持多按键(注意学习思路)

目录

原理简介

主要分析图(按下时低电平,松开是高电平)

代码

总结


原理简介

用0.5秒进行判断,0.5s内按一下算短按,0.5s内按两下算双击,0.5s以上按一下算长按。是不是一看觉得很简单,先让我们认真分析一下。

首先:你按下0.5s以内,然后松开,这个时候时间还在0.5s以内,那你改对他放入那一类呢?答案是这个时候还不能判断,你无法判断它剩下的时间能否再按一次(总时间未超过0.5s),所以你对一次按键的判断肯定得超过0.5s,所以这个代码的弊端一就出来了。

到这里,有人肯定会说,这还不是照样简单,0.5s按一下我就算短按,0.5s按两下我就算双击,可是还是会有一个问题,我第一次按下抬起在0.5s以内,但我第二次按下了,我还没松开,0.5s已经过了,那这种算啥呢?所以我们这个时候得把第一次算短按中去,剩下的这个第二次按下需要重新判断。

最后聪明的小伙伴会问,那这个时候应没坑了吧?那我也说多余的坑我也不知道。当然这里面的长按是不需要啥顾虑的,你按下的时间超过了0.5s,那肯定是长按了,还有就是双击的判读,0.5s内你就是按了两次,同时最后松开了,那就是双击。这个时候比较牛逼的人就要说了,博主你是不是不行啊,像我手速快,我0.5s按三下,但然我还没回答,更加牛逼的人肯定要说了,0.5s按三下算什么,我手速快的飞起,0.5s四下,0.5s四下算什么,0.5s一万下····

不得不说他们确实牛逼,博主手速没你们这么快。所以我们得引入循环这个概览,10ms定时器加中断,既可以消抖动,还能循环往复,那三次怎么算你,首先前两次肯定算双击,后面一次 保留进行0.5s的重新观察,那0.5s按四次就是两次双击,一万次的博主表示,太快了不好。

主要分析图(按下时低电平,松开是高电平)

代码

我这里用了hal库,板子stm32g431Rbt6,我这里用四个按键,随意弄了一个结构体,但其实不管是啥都一样的,无非就是对一个按键进行不断读取,然后判断状态。

uint16_t count;
struct key
{
	bool single_flag;    //单次按下
	bool double_flag;    //双击
	bool long_flag;       //长按
	uint16_t double_time;    //按下后松开的时间
	bool double_timeEN;        //已经有一次按下松开的标志位
	uint16_t age;                //按下的时间
	uint16_t age_old;            //需要保留的第一次按下时间
	bool press;                   //松开的标准位
}keys[5];                            //懒得减一

uint8_t key_read(void){    //判断按键的返回值
	uint8_t key_val=0;
	if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==0)
		key_val=1;
	if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)==0)
		key_val=2;
	if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)==0)
		key_val=3;
	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==0)
		key_val=4;
	return key_val;
}

void key_scan(void)
{
	uint8_t key_sta=key_read();
	if(key_sta!=0)        //有按键按下
	{
		keys[key_sta].age++;    //按下时间记录
		if(keys[key_sta].age>1){    //第一次不管,进行消抖
			keys[key_sta].press=1;    //按下标志位
			if(keys[key_sta].double_timeEN==1&&(keys[key_sta].age+keys[key_sta].age_old+keys[key_sta].double_time)>=50){    //0.5s去除前一次,保留后一次
				keys[key_sta].double_timeEN=0;
				keys[key_sta].double_time=0;
				keys[key_sta].single_flag=1;
				keys[key_sta].age_old=0;
			}
		}
	}else        //松开
	{
		for(int i=0;i<5;i++)
		{
			if(keys[i].age>50)    //直接长按
			{
				keys[i].long_flag=1;
			}
			if(keys[i].double_timeEN==1&&keys[i].press==1&&(keys[i].age+keys[i].age_old+keys[i].double_time)<50)    //符合双击
			{
				keys[i].double_flag=1;
				keys[i].press=0;
				keys[i].double_timeEN=0;
				keys[i].age_old=0;
			}
			
			if(keys[i].press==1&&keys[i].long_flag!=1)    //第一次按下松开
			{
				keys[i].double_timeEN=1;        //记录松开时间,打开标志位
				keys[i].age_old=keys[i].age;    //保存第一次按下时间
			}
			if(keys[i].double_timeEN==1)
			{
				keys[i].double_time++;        //记录松开时间
			}
			
			if(keys[i].double_timeEN==1&&(keys[i].double_time+keys[i].age)>50)    //符合单击
			{
				keys[i].single_flag=1;
				keys[i].double_timeEN=0;
				keys[i].double_time=0;
			}
			keys[i].age=0;    //松开进行数据清理
			keys[i].press=0;
		}
	}
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){    //10ms定时器中断
	if(htim->Instance==TIM6){
		key_scan();
	}
}

void key_prc1(){        //对具体进行处理,同时记得清零
	if(keys[1].single_flag){
		count++;
		keys[1].single_flag=0;        //单击清零
	}
	if(keys[1].long_flag){
		count+=2;
		keys[1].long_flag=0;
	}
	if(keys[1].double_flag){
		count+=3;
		keys[1].double_flag=0;
	}
}

//定时器初始化我就不写了,还有一下其他基础配置之类的也不写了
int main(void)
{
  while (1)
  {
		key_prc1();
  }

}

总结

聪明的人是不是发现第二个弊端了,if(keys[key_sta].age>1)竟然第一次进来20ms,博主时间这么长的吗?其实也可以弄成10ms,只不过又得加变量了,需要设置key_old=为之前出入的值,然后key_up,在key_down,有点麻烦了,算了,是我太懒了。

欧克,如有不足,希望指正

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我可以为您提供一些关于 stm32 按键双击实现方法。 对于单片机的按键处理,常见的方法是轮询法和断法。轮询法是在主循环不断读取按键状态,判断按键是否按下或者释放,并进行相应的处理;断法则是通过外部断或者定时器断来响应按键事件。 对于按、按和双击事件的处理,一种常见的方法是使用状态机。状态机包括多种状态和转移条件,用于描述按键事件的不同状态和状态之间的转移过程。通过按键状态的变化,可以判断出按键事件的类型,并进行相应的处理。 具体实现方法可以参考以下步骤: 1. 读取按键状态,并判断按键是否按下或者释放。 2. 根据按键状态的变化,更新状态机的状态,并记录按键的按下时间和释放时间。 3. 根据状态机的状态,判断按键事件的类型,并进行相应的处理。例如,当状态机处于“按下”状态时,如果按键持续按下的时间超过一定阈值,则判断为按事件;如果按键持续按下的时间不足阈值,则判断为按事件;如果在一定时间内连续按下两次按键,则判断为双击事件。 4. 根据按键事件的类型,执行相应的操作。例如,按事件可以用于开启或关闭某个功能;按事件可以用于切换不同的模式;双击事件可以用于执行快速操作。 总之,实现按键双击需要结合具体的硬件平台和软件环境进行综合考虑,根据实际需求选择合适的方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值