STM32按键单击双击和长按实验

本文介绍了一种使用定时器实现的按键事件检测方法,通过定时器周期性检测按键状态,实现对单击、双击和长按操作的区分。通过实例代码展示了如何在STM32中实现按键的计数和标志管理,并在主函数中处理不同按键事件的响应。
摘要由CSDN通过智能技术生成

学习目标:

1. 按键单击
2. 按键双击
3. 按键长按

实验思路:

使用定时器来完成对按键长按,单击,双击的检测。

实验代码:

//定时器采用的定时器7周期周期为30ms
#define Press_Time 60								//长按时间				1.8s
#define KEY_Response_Time 10				//按键响应时间		300ms
enum{
	Click = 0x01,//单机标志				  
	Double_Click = 0x02,//双击标志
	Press = 0x04 //长按2s标志
};

typedef struct {
	uint8_t volatile KEY_Count;//按下时间计数
	uint8_t KEY_Flag;//按键标志 				  
}User_Key_t;

User_Key_t KEY0={0,0};
User_Key_t KEY1={0,0};
User_Key_t KEY2={0,0};
User_Key_t WKUP={0,0};
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	static uint8_t tim300_count;
	static uint8_t count_flag;
	if(htim == &htim7)   //确定是定时器6产生的中断
	{
		if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)==RESET){
			KEY0.KEY_Count++;
		}
		else if(KEY0.KEY_Count){
			if(KEY0.KEY_Count>Press_Time){KEY0.KEY_Flag=Press;KEY0.KEY_Count=0;count_flag=0;tim300_count=0;return;}
			if(count_flag==0){KEY0.KEY_Count=0;count_flag=0X01;}
		}
		if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==RESET){
			KEY1.KEY_Count++;
		}else if(KEY1.KEY_Count){
			if(KEY1.KEY_Count>Press_Time){KEY1.KEY_Flag=Press;KEY1.KEY_Count=0;count_flag=0;tim300_count=0;return;}
			if(count_flag==0){KEY1.KEY_Count=0;count_flag=0X02;}
		} 
		if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==RESET){
			KEY2.KEY_Count++;
		}else if(KEY2.KEY_Count){
			if(KEY2.KEY_Count>Press_Time){KEY2.KEY_Flag=Press;KEY2.KEY_Count=0;count_flag=0;tim300_count=0;return;}
			if(count_flag==0){KEY2.KEY_Count=0;count_flag=0X04;}
		}
		if(HAL_GPIO_ReadPin(WKUP_GPIO_Port,WKUP_Pin)==SET){
			WKUP.KEY_Count++;
		}else if(WKUP.KEY_Count){
			if(WKUP.KEY_Count>Press_Time){WKUP.KEY_Flag=Press;WKUP.KEY_Count=0;count_flag=0;tim300_count=0;return;}
			if(count_flag==0){WKUP.KEY_Count=0;count_flag=0X08;}
		}
		if(count_flag){
			tim300_count++;
		}	
		if(tim300_count>=KEY_Response_Time){
			if(KEY0.KEY_Count>2){KEY0.KEY_Flag=Double_Click;KEY0.KEY_Count=0;tim300_count=0;count_flag=0;return;}//双击
			else if(KEY1.KEY_Count>2){KEY1.KEY_Flag=Double_Click;KEY1.KEY_Count=0;tim300_count=0;count_flag=0;return;}//双击			
			else if(KEY2.KEY_Count>2){KEY2.KEY_Flag=Double_Click;KEY2.KEY_Count=0;tim300_count=0;count_flag=0;return;}//双击
			else if(WKUP.KEY_Count>2){WKUP.KEY_Flag=Double_Click;WKUP.KEY_Count=0;tim300_count=0;count_flag=0;return;}//双击
			if(count_flag==0x01) {KEY0.KEY_Flag=Click;tim300_count=0;count_flag=0;return;}//单击
			else if(count_flag==0x02) {KEY1.KEY_Flag=Click;tim300_count=0;count_flag=0;return;}//单击	
			else if(count_flag==0x04){KEY2.KEY_Flag=Click;tim300_count=0;count_flag=0;return;}//单击	
			else if(count_flag==0x08){WKUP.KEY_Flag=Click;tim300_count=0;count_flag=0;return;}//单击							
			
		}
	}

}

主函数里调用

		switch(KEY0.KEY_Flag)
		{
			case Click:
				printf("%d\r\n",KEY0.KEY_Flag);
				LED.LED_On(LED1_GPIO_Port,LED1_Pin);
				LED.LED_Off(LED0_GPIO_Port,LED0_Pin);
				KEY0.KEY_Flag=0x00;
				break;
			case Double_Click:
				printf("%d\r\n",KEY0.KEY_Flag);				
				LED.LED_Off(LED1_GPIO_Port,LED1_Pin);
				LED.LED_On(LED0_GPIO_Port,LED0_Pin);
				KEY0.KEY_Flag=0x00;			
			break;
			case Press:
				printf("%d\r\n",KEY0.KEY_Flag);
				LED.LED_Off(LED1_GPIO_Port,LED1_Pin);
				LED.LED_Off(LED0_GPIO_Port,LED0_Pin);	
				KEY0.KEY_Flag=0x00;
			break;
		}
在51单片机中,独立按键的驱动可以采用轮询方式或者中断方式。下面分别介绍三种按键的驱动方式。 ### 单击按键 #### 轮询方式 在轮询方式中,通过不断查询按键的状态来判断是否按下。具体实现如下: ```c // 定义按键IO口和状态 sbit key = P1^0; bit key_state = 0; void main() { while(1) { // 检测按键是否按下 if(key == 0) { // 消抖处理 delay_ms(10); if(key == 0) { key_state = 1; } } else { key_state = 0; } // 判断按键状态,执行相应的操作 if(key_state == 1) { // 执行单击操作 // ... } } } ``` #### 中断方式 在中断方式中,通过外部中断触发来判断按键是否按下。具体实现如下: ```c // 定义按键IO口和状态 sbit key = P1^0; bit key_state = 0; // 中断服务函数 void key_interrupt() interrupt 0 { // 消抖处理 delay_ms(10); if(key == 0) { key_state = 1; } } void main() { // 配置外部中断0 IT0 = 1; EX0 = 1; EA = 1; while(1) { // 判断按键状态,执行相应的操作 if(key_state == 1) { // 执行单击操作 // ... } } } ``` ### 双击按键 双击按键的实现可以通过轮询方式或者定时器方式实现。 #### 轮询方式 在轮询方式中,需要记录上一次按键的时间,并通过计算两次按键时间的间隔来判断是否为双击。具体实现如下: ```c // 定义按键IO口和状态 sbit key = P1^0; bit key_state = 0; unsigned int last_key_time = 0; void main() { while(1) { // 检测按键是否按下 if(key == 0) { // 消抖处理 delay_ms(10); if(key == 0) { key_state = 1; } } else { key_state = 0; } // 判断按键状态,执行相应的操作 if(key_state == 1) { if((unsigned int)(get_sys_time() - last_key_time) < 500) { // 执行双击操作 // ... last_key_time = 0; } else { last_key_time = get_sys_time(); } } } } ``` #### 定时器方式 在定时器方式中,需要配置定时器,并记录上一次按键的时间。每次检测到按键按下时,通过计算两次按键时间的间隔来判断是否为双击。具体实现如下: ```c // 定义按键IO口和状态 sbit key = P1^0; bit key_state = 0; unsigned int last_key_time = 0; // 定时器0中断服务函数 void timer0_interrupt() interrupt 1 { // 定时1ms TH0 = (65536 - 1000) / 256; TL0 = (65536 - 1000) % 256; } void main() { // 配置定时器0 TMOD = 0x01; TH0 = (65536 - 1000) / 256; TL0 = (65536 - 1000) % 256; ET0 = 1; TR0 = 1; EA = 1; while(1) { // 检测按键是否按下 if(key == 0) { // 消抖处理 delay_ms(10); if(key == 0) { key_state = 1; } } else { key_state = 0; } // 判断按键状态,执行相应的操作 if(key_state == 1) { if((unsigned int)(get_sys_time() - last_key_time) < 500) { // 执行双击操作 // ... last_key_time = 0; } else { last_key_time = get_sys_time(); } } } } ``` ### 按键 按键的实现可以通过轮询方式、中断方式或者定时器方式实现。 #### 轮询方式 在轮询方式中,需要记录按键按下的时间,并通过判断按键持续时间的短来判断是否为按。具体实现如下: ```c // 定义按键IO口和状态 sbit key = P1^0; bit key_state = 0; unsigned int key_down_time = 0; void main() { while(1) { // 检测按键是否按下 if(key == 0) { // 消抖处理 delay_ms(10); if(key == 0) { key_state = 1; key_down_time = get_sys_time(); } } else { if(key_state == 1) { if((unsigned int)(get_sys_time() - key_down_time) > 1000) { // 执行按操作 // ... key_state = 0; } else { // 执行单击操作 // ... key_state = 0; } } } } } ``` #### 中断方式 在中断方式中,需要配置外部中断,并记录按键按下的时间。每次触发中断时,通过判断按键持续时间的短来判断是否为按。具体实现如下: ```c // 定义按键IO口和状态 sbit key = P1^0; bit key_state = 0; unsigned int key_down_time = 0; // 中断服务函数 void key_interrupt() interrupt 0 { // 消抖处理 delay_ms(10); if(key == 0) { key_down_time = get_sys_time(); } else { if((unsigned int)(get_sys_time() - key_down_time) > 1000) { // 执行按操作 // ... } else { // 执行单击操作 // ... } } } void main() { // 配置外部中断0 IT0 = 1; EX0 = 1; EA = 1; while(1) { } } ``` #### 定时器方式 在定时器方式中,需要配置定时器,并记录按键按下的时间。每次检测到按键按下时,通过判断按键持续时间的短来判断是否为按。具体实现如下: ```c // 定义按键IO口和状态 sbit key = P1^0; bit key_state = 0; unsigned int key_down_time = 0; // 定时器0中断服务函数 void timer0_interrupt() interrupt 1 { // 定时1ms TH0 = (65536 - 1000) / 256; TL0 = (65536 - 1000) % 256; // 检测按键是否按下 if(key == 0) { // 消抖处理 delay_ms(10); if(key == 0) { key_state = 1; key_down_time = get_sys_time(); } } else { if(key_state == 1) { if((unsigned int)(get_sys_time() - key_down_time) > 1000) { // 执行按操作 // ... key_state = 0; } else { // 执行单击操作 // ... key_state = 0; } } } } void main() { // 配置定时器0 TMOD = 0x01; TH0 = (65536 - 1000) / 256; TL0 = (65536 - 1000) % 256; ET0 = 1; TR0 = 1; EA = 1; while(1) { } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值