单片机开发中多功能按键详细解析设计(单击、双击、长按程序)

目录

  • 单击、双击、长按原理解析
  • 程序源码
  • 思考总结

引言:在很多项目开发过程中我们通常会涉及到按键的使用,为了使按键的功能更多远化我们通常会区别按键的单击、双击、长按等操作过程从而实现更多的功能。现在让我们来分析一下他们的实现原理和过程。

一、原理解析(注意i:这里我们的硬件设计为当按键按下为低电平,如硬件设计不一致反过来即可,原理是相同的)

首先我们来看一下单击、双击、长按电平时序图

在这里插入图片描述
从三种时序图我们可以看出三种操作方式的区别就在于当按键按下后低电平和高电平的时间,通过判断高低电平的变化时间就可以把这三种方式区别开。

单击、和长按的区别:
在这里插入图片描述
单击和双击时序图非常的相似,最大的区别就是按键按下后低电平的持续时间,这里我们对比单击和长按的时序图,可知长按的低电平时间要比单击的要长很多所以这里我们假设单击时低电平的时间为S1,长按时低电平的时间为S2,我们只要在单击和双击之间加一个判断时间这里我们加入S3作为判断,当按键按下时低电平的时间超过了S3则判断为长按,若低电平时间小于S3则判断为单击。我们可以根据自己的需要设定S3的时间来规定什么是长按,什么是单击。

单击和双击的区别:
在这里插入图片描述
通过时序图我们可以看到双击相当于两次单击,双击时第一次按键放开到第二次按键按下有一个时间间隔这里我们用D1表示,而双击和单击的区别在于在D1时间过后单击的电平一直处于高电平状态,而双击则会再次出现一段低电平,这里我们可以加一个定时器在第一次按键放开后开始计时,计时的最大值为D2,这里我们只要判断在D2时间内是否出现了低电平。如果出现了低电平则双击,如果没有出现低电平则为单击。如果出现低电平的时间超过了D2则为两次单击而不是双击。这里我们可以更改D2的时间来改变双击的速度。

二、程序源码

在这里插入代码片
#define TOUCH_KEY_A  GPIO_Pin_1
#define TOUCH_KEYPORT  GPIOA
#define KEYA_SPEED1	100	  //长按的时间长度(单位10mS)
#define KEYA_SPEED2	10	  //双击的时间长度(单位20mS)

u8 A,B,C;


void KEY_READ(void)
{
	if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){ //检测按键是否按下
			delay_ms(20); //延时去抖动
			if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){//再次判断
				while((!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A))&&C<KEYA_SPEED1){ //循环判断长按,到时跳转
					C++;delay_ms(10); //长按判断的计时
				}
				if(C>=KEYA_SPEED1){ //长键处理
					//长按后执行的程序放到此处
						GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1));//LED控制
				while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A));
				}else{ //单击处理
					for(b=0;b<KEYA_SPEED2;b++){//检测双击
						delay_ms(20);
						if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){
							a=1;
							//双击后执行的程序放到此处
							GPIO_WriteBit(LEDPORT,LED2,(BitAction)(1));//LED控制

							while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A));
						}
					}
					if(a==0){ //判断单击
						//单击后执行的程序放到此处
						GPIO_WriteBit(LEDPORT,LED1|LED2,(BitAction)(0));//LED控制
						
					}
				}
				A=0;C=0; //参数清0
			}
		} //按键判断在此结束




  • 13
    点赞
  • 93
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
好的,以下是一个简单的实现单击双击按功能的程序,使用的是STM32的HAL库: 首先,我们需要定义一些常量和变量: ```c #define DEBOUNCE_TIME 20 // 消抖时间,单位ms #define LONG_PRESS_TIME 1000 // 按时间,单位ms #define DOUBLE_CLICK_TIME 250 // 双击间隔时间,单位ms GPIO_TypeDef* KEY_PORT = GPIOB; // 按键所在的GPIO端口 uint16_t KEY_PIN = GPIO_PIN_0; // 按键所在的GPIO引脚 typedef enum { KEY_IDLE, // 按键空闲状态 KEY_PRESS, // 按键按下状态 KEY_RELEASE, // 按键释放状态 KEY_LONG_PRESS, // 按状态 KEY_DOUBLE_CLICK_WAIT, // 等待双击状态 KEY_DOUBLE_CLICK // 双击状态 } key_state_t; key_state_t key_state = KEY_IDLE; // 当前按键状态 uint32_t key_press_time = 0; // 按键按下时间 uint32_t key_release_time = 0; // 按键释放时间 uint32_t key_last_release_time = 0; // 上一次按键释放时间 uint8_t click_count = 0; // 点击次数 ``` 然后,我们可以在主函数初始化按键所在的GPIO引脚: ```c HAL_GPIO_WritePin(KEY_PORT, KEY_PIN, GPIO_PIN_SET); // 拉高按键引脚 GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = KEY_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(KEY_PORT, &GPIO_InitStruct); ``` 在主函数的`while`循环,我们可以编写按键状态机的代码: ```c // 获取当前时间 uint32_t t = HAL_GetTick(); // 检测按键状态 switch (key_state) { case KEY_IDLE: if (HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN) == GPIO_PIN_RESET) { // 按键按下 key_press_time = t; key_state = KEY_PRESS; } break; case KEY_PRESS: if (HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN) == GPIO_PIN_SET) { // 按键释放 key_release_time = t; if (key_release_time - key_press_time > DEBOUNCE_TIME) { // 消抖 click_count++; if (click_count == 1) { // 第一次单击 key_last_release_time = key_release_time; key_state = KEY_IDLE; } else if (click_count == 2) { // 第二次单击 if (key_release_time - key_last_release_time < DOUBLE_CLICK_TIME) { // 双击 click_count = 0; key_state = KEY_DOUBLE_CLICK; } else { // 第一次单击后超时,重新开始计数 click_count = 1; key_last_release_time = key_release_time; key_state = KEY_IDLE; } } else { // 连续单击 key_last_release_time = key_release_time; key_state = KEY_IDLE; } } } else if (t - key_press_time > LONG_PRESS_TIME) { // 按 click_count = 0; key_state = KEY_LONG_PRESS; } break; case KEY_RELEASE: if (HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN) == GPIO_PIN_RESET) { // 按键重新按下 key_press_time = t; key_state = KEY_PRESS; } else { // 按键完全释放 key_state = KEY_IDLE; } break; case KEY_LONG_PRESS: if (HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN) == GPIO_PIN_SET) { // 按释放 key_state = KEY_RELEASE; } break; case KEY_DOUBLE_CLICK_WAIT: if (t - key_release_time > DOUBLE_CLICK_TIME) { // 双击超时 if (click_count == 1) { // 单击 key_state = KEY_RELEASE; } else { // 连续单击 key_last_release_time = key_release_time; key_state = KEY_IDLE; } } else if (HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN) == GPIO_PIN_RESET) { // 双击按下 key_press_time = t; key_state = KEY_PRESS; } break; case KEY_DOUBLE_CLICK: // 双击完成 key_state = KEY_IDLE; break; } // 执行按键状态的相应操作 switch (key_state) { case KEY_LONG_PRESS: // 按操作 break; case KEY_DOUBLE_CLICK: // 双击操作 break; } ``` 以上就是一个简单的实现单击双击按功能的程序,可以根据需要进行修改和优化。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值