STM32L476VET6 RTC时钟日历(HAL库+CubeMX开发)(四)按键消抖方法和按键EXTI双边沿检测

外部按键KEY中断

我板子上的按键是按键按下读取低电平抬起读取高电平

#ifndef _KEY_H
#define _KEY_H
#include "sys.h"

#define KEY_1			KEY_RET
#define KEY_2			KEY_VOID
#define KEY_3			KEY_SET
#define KEY_4			KEY_DOWN
#define KEY_5			KEY_RIGHT
#define KEY_6			KEY_INC


typedef enum
{
	KEY_RET,
	KEY_VOID,
	KEY_SET,
	KEY_RIGHT,
	KEY_DOWN,
	KEY_INC,
	NONE,//没有任何键被按下
}KEY_VALUE;


typedef struct
{
	int key_dpress_flag;
	int key_dpress_count;
	int key_duration_flag;
	float key_duration;
}KEY_STATUS;


typedef struct
{
	u8 key_suf;
	u8 key_flag;
	KEY_VALUE key_value_read;
}KEY_DETECT_T;

extern KEY_STATUS ks;
extern KEY_DETECT_T kd;
void GetKeyValue(void);
void KeyDeal(void);

#endif
#include "key.h"
#include "lcd_st7920.h"
#include "usart.h"
#include "low_pwr.h"

KEY_DETECT_T kd={0,0,NONE};
KEY_STATUS ks={1,0,1,0};

void GetKeyValue(void)
{
	kd.key_suf=(GPIOE->IDR)&0xfc;
	if(kd.key_suf!=0xfc)
	{
		if(kd.key_suf==0x7c)
		{
			kd.key_value_read=KEY_1;//KEY_RET
		}
		
		else if(kd.key_suf==0xbc)
		{
			kd.key_value_read=KEY_2;//KEY_VOID免装按键(WKUP键)
		}
		
		else if(kd.key_suf==0xdc)
		{
			kd.key_value_read=KEY_3;//KEY_SET
		}
		
		else if(kd.key_suf==0xec)
		{
			kd.key_value_read=KEY_4;//KEY_DOWN
		}
		
		else if(kd.key_suf==0xf4)
		{
			kd.key_value_read=KEY_5;//KEY_RIGHT
		}
		
		else if(kd.key_suf==0xf8)
		{
			kd.key_value_read=KEY_6;//KEY_INC
		}
	}
}


void KeyDeal(void)
{
	if(kd.key_value_read==KEY_RET)
	{
		printf("KEY_RET PRESS\r\n");
		kd.key_value_read=NONE;
	}
	if(kd.key_value_read==KEY_SET)
	{
		printf("KEY_SET PRESS\r\n");
		kd.key_value_read=NONE;
	}
	if(kd.key_value_read==KEY_VOID)//WKUP低功耗唤醒键
	{
		printf("KEY_VOID PRESS\r\n");
		kd.key_value_read=NONE;
		low_pwr_struct.low_pwr_count=LOW_PWR_COUNTDOWN;//每次按下都更新countdown值
		if(low_pwr_struct.low_pwr_flag)
		{
			low_pwr_struct.low_pwr_flag=0;
			low_pwr_struct.low_pwr_count=LOW_PWR_COUNTDOWN;
			LOW_PWR_Exit();
		}
	}
	if(kd.key_value_read==KEY_DOWN)
	{
		printf("KEY_DOWN PRESS\r\n");
		kd.key_value_read=NONE;
	}
	if(kd.key_value_read==KEY_RIGHT)
	{
		printf("KEY_RIGHT PRESS\r\n");
		kd.key_value_read=NONE;
	}
	if(kd.key_value_read==KEY_INC)
	{
		printf("KEY_INC PRESS\r\n");
		kd.key_value_read=NONE;
	}
}


按键消抖和长短按检测

很多按键消抖的博客里说的在外部中断函数中做hal_delay,我认为这种方法不合理,在中断中做delay永远是不合理的因为中断肯定是要快进快出的,在里面做delay很容易出问题。所以我只在外部中断IRQ里置标志位,然后在定时器TIM2里面做加法定时,整个流程完全不用delay。

在初始化KEY_STATUS时ks.key_dpress_flag和ks.key_duration_flag被初始化为1,按键按下下降沿触发中断ks.key_dpress_flag置0

ks.key_dpress_flag置0后进入TIM2中断,ks.key_dpress_count++开始计数当ks.key_dpress_count++>=6也就是5*6=30ms之后才会进入读取电平确认是否已按下,换句话说就是按下之后的30ms是消抖时间。确定按键被按下之后将ks.key_dpress_flag置1同时将ks.key_dpress_count置0直到上升沿再次触发,接着将ks.key_duration_flag置0开始按下时间计时ks.key_duration++。

案件抬起消抖也一样,再次进入定时器中断函数30ms之后读取电平确定是否抬起,然后ks.key_dpress_flag置1,ks.key_dpress_count清零,ks.key_duration_flag置1停止计时,直到下次下降沿触发。然后读取ks.key_duration值并转化为单位秒

/* USER CODE BEGIN 1 */
/**********************************************
功能说明: 定时器中断回调函数
入口参数: 
返回参数: 
***********************************************/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	//5ms
	if(htim == (&htim2))
	{	
		//消抖+长短按检测
		if(!ks.key_dpress_flag)
		{
			ks.key_dpress_count++;
			if(ks.key_dpress_count>=6)//消抖6*5ms=30ms
			{
				if((HAL_GPIO_ReadPin(GPIOE,KEY_1_EXTI7_Pin)&\
					HAL_GPIO_ReadPin(GPIOE,KEY_2_EXTI6_Pin)&\
					HAL_GPIO_ReadPin(GPIOE,KEY_3_EXTI5_Pin)&\
					HAL_GPIO_ReadPin(GPIOE,KEY_4_EXTI4_Pin)&\
					HAL_GPIO_ReadPin(GPIOE,KEY_5_EXTI3_Pin)&\
					HAL_GPIO_ReadPin(GPIOE,KEY_6_EXTI2_Pin))==GPIO_PIN_RESET)//按键按下消抖
				{
					ks.key_dpress_flag=1;//按键按下标志位置1直到上升沿再次触发
					ks.key_dpress_count=0;
					ks.key_duration_flag=0;
					GetKeyValue();
					KeyDeal();
				}
				if((HAL_GPIO_ReadPin(GPIOE,KEY_1_EXTI7_Pin)&\
					HAL_GPIO_ReadPin(GPIOE,KEY_2_EXTI6_Pin)&\
					HAL_GPIO_ReadPin(GPIOE,KEY_3_EXTI5_Pin)&\
					HAL_GPIO_ReadPin(GPIOE,KEY_4_EXTI4_Pin)&\
					HAL_GPIO_ReadPin(GPIOE,KEY_5_EXTI3_Pin)&\
					HAL_GPIO_ReadPin(GPIOE,KEY_6_EXTI2_Pin))==GPIO_PIN_SET)//按键抬起消抖
				{
					ks.key_dpress_flag=1;//按键按下标志位置1直到下降沿再次触发
					ks.key_dpress_count=0;
					ks.key_duration_flag=1;
					ks.key_duration*=0.005;
					printf("key_duration:%05.2fs\r\n",ks.key_duration);
					printf("\r\n");
				}
			}
		}
		//按键按下计时
		if(!ks.key_duration_flag)
		{
			ks.key_duration++;
		}
		
		//低功耗计时
		if(!low_pwr_struct.low_pwr_flag)
		{
			low_pwr_struct.low_pwr_count--;
			if(low_pwr_struct.low_pwr_count<0)//1min
			{
				low_pwr_struct.low_pwr_flag=1;
				low_pwr_struct.low_pwr_count=0;
				LOW_PWR_Enter();
			}
		}


		//LCD忙检测超时
		if(lcd.lcd_busy_overtime>0)
		{
			lcd.lcd_busy_overtime--;
		}
		
	}
	
	//100us	
	if(htim == (&htim3))
	{
  
	}
}

整个消抖流程不会对程序造成任何阻滞

执行效果

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值