单片机按键扫描实现短按_长按_重复_组合键功能详解

在单片机项目中,按键操作通常是产品与用户交互必不可少功能,按键又有短按、长按、重复、组合键等操作,本文介绍了一种按键扫描的实现方法,能够实现短按、长按、重复以为组合键的功能。

短按:即按下按键马上松开,如图1,按下的 时间tl小于允许的最大时间t_max,则认为是短按
长按:即按下按键3秒后再松开,如图2,按下的 时间tl大于时间t_lmax
重复:即一直按住按键不放,重复该按键,如图3,按下的时间达到t_rep,产生一次按键,达到时间r1又产生一次按键,以此重复。
我们可以通过计算按下的时间长短来确定是短按还是长按,在松开按键时,产生相应的键值。
在这里插入图片描述
扫描操作流程图
在这里插入图片描述

头文件

#ifndef __KEY_H
#define	__KEY_H

#include "stm8l15x.h"
#include "stm8l15x_gpio.h"
#include "stm8l15x_exti.h"

/*--------按键标记 ------*/
#define KEY_NO	0x00	//无按键
#define KEY_S1	0x01	//按键1
#define KEY_S2	0x02	//按键2
#define KEY_S3	0x04
#define KEY_S4	0x08
#define KEY_REP_FLAG	0x40	//重复按下
#define KEY_LONG_FLAG	0x80	//长按3秒标记

//键值
typedef enum _key{
    
    
	KEY_OFF				= KEY_NO,	//没有任何键按下
	KEY_RIGHT			= KEY_S2,  //向右键
	KEY_DN				= KEY_S3,	//向下键
	KEY_UP				= KEY_S1,	//向上键
	KEY_LEFT			= KEY_S4,  //向左键
	KEY_L_UP			=(KEY_LONG_FLAG|KEY_UP), //左键+上键
	KEY_L_DN			=(KEY_LONG_FLAG|KEY_DN),
	KEY_L_RIGHT			=(KEY_LONG_FLAG|KEY_RIGHT),
	KEY_L_LEFT			=(KEY_LONG_FLAG|KEY_LEFT),
	KEY_R_UP			=(KEY_REP_FLAG|KEY_UP),
	KEY_R_DN			=(KEY_REP_FLAG|KEY_DN),
	KEY_R_RIGHT			=(KEY_REP_FLAG|KEY_RIGHT),
	KEY_R_LEFT			=(KEY_REP_FLAG|KEY_LEFT),
	KEY_L_UP_DN			=(KEY_LONG_FLAG|KEY_UP|KEY_DN), //+ - 长按
	KEY_L_LEFT_RIGHT	=(KEY_LONG_FLAG|KEY_RIGHT|KEY_LEFT), //OK ESC 长按
	KEY_L_LEFT_DN		=(KEY_LONG_FLAG|KEY_DN|KEY_LEFT), //DN ESC 长按
}KeyValue_ENUM;

/*----------按键计时-----------*/
#define KEY_DOWN_CNT	50  //按下时长
#define KEY_SPEED		40	//重复按下重载时间
#define KEY_REP_3S		2	//重复速度

//*-----------IO-----------*/
#define KEY1_PORT	GPIOA
#define KEY1_PIN	GPIO_Pin_2

#define KEY2_PORT	GPIOA
#define KEY2_PIN	GPIO_Pin_3

#define KEY3_PORT	GPIOC
#define KEY3_PIN	GPIO_Pin_0

#define KEY4_PORT	GPIOC
#define KEY4_PIN	GPIO_Pin_1

/*-----------读取按键-----------*/
#define Key1_ReadSta()		((BitStatus)(KEY1_PORT->IDR&KEY1_PIN))
#define Key2_ReadSta()		((BitStatus)(KEY2_PORT->IDR&KEY2_PIN))
#define Key3_ReadSta()		((BitStatus)(KEY3_PORT->IDR&KEY3_PIN))
#define Key4_ReadSta()		((BitStatus)(KEY4_PORT->IDR&KEY4_PIN))

/*--------函数-------*/
void KEY_Init(void);
KeyValue_ENUM KeyScan(void);

#endif /* __KEY_H */

代码

/************************************************************
Copyright (C), 2013-2020
@FileName: KEY.C
@Author  : 糊读虫 QQ:570525287
@Version : 2.0
@Date    : 2019-8-12
@Description: 按键扫描
@Function List:
@History    : 
<author> <time> <version > <desc>

***********************************************************/
#include "key.h"

//IO初始化
void KEY_Init(void)
{
    
    
	GPIO_Init(KEY1_PORT,KEY1_PIN,GPIO_Mode_In_PU_No_IT); //上拉无中断
	GPIO_Init(KEY2_PORT,KEY2_PIN,GPIO_Mode_In_PU_No_IT); //上拉无中断
	GPIO_Init(KEY3_PORT,KEY3_PIN,GPIO_Mode_In_PU_No_IT); //上拉无中断
	GPIO_Init(KEY4_PORT,KEY4_PIN,GPIO_Mode_In_PU_No_IT); //上拉无中断
}

//按键扫描
KeyValue_ENUM KeyScan(void)
{
    
    
	uint8_t io_value;
	static uint8_t key_Press;  // 这个是要返回的键值
	static uint8_t key_Old = 0x00;
	static uint8_t DownCnt = 0; //按下计数
	static struct _sta{
    
    
		uint8_t up:		1;  //弹起
		uint8_t dn:		1;	//按下
		uint8_t rep:	1;
	}Status;  //按键状态标记
	uint8_t ret;  
	
	io_value = 0x00;
	ret = KEY_OFF;

	//获取当前按下的按键
	if (!Key1_ReadSta())io_value |= KEY_S1;
	if (!Key2_ReadSta())io_value |= KEY_S2;
	if (!Key3_ReadSta())io_value |= KEY_S3;
	if (!Key4_ReadSta())io_value |= KEY_S4;

	if (io_value) //如果按键被按下
	{
    
    
		if (key_Old == io_value) //判断与上次扫描到的是否为同一个按键
		{
    
    
			DownCnt++; //计数

			if(DownCnt < KEY_DOWN_CNT)//当前计数小于长按的时间
			{
    
    
				if (Status.rep == 0) // 不是重复
				{
    
    
					Status.dn = 1;
					key_Press = io_value; //记下按键
				}				
			}

			if (DownCnt >= KEY_DOWN_CNT) //当前计数大于长按的时间
			{
    
    
				Status.rep = 1;  //标记重复

				if(CntLongPress++ == KEY_REP_3S)//按下超过3秒
				{
    
    
					ret = io_value | KEY_LONG_FLAG; //加长按标记
				}
				else
				{
    
    
					ret = io_value | KEY_REP_FLAG; //重复
				}
				
				DownCnt = KEY_SPEED;	//重复起始值				
			}
		}
		key_Old = io_value;	 //记录键值
	}
	else  //按键松开
	{
    
    
		if (Status.dn && !Status.rep) //返回松开前的键值
		{
    
    
			ret = key_Press;
		}

		//清除标记
		Status.dn = 0;
		Status.rep = 0;
		DownCnt = 0;
		key_Old = KEY_NO;
	}

	return (KeyValue_ENUM)ret;  //返回键值
}
//--------------END OF FILE---------------

在主函数中每隔20ms扫描一次

#include "key.h"

void main()
{
    
    
	//....
	KEY_Init();
	while(1)
	{
    
    
		if(delay_20ms == 1)
		{
    
    
			KeyValue_ENUM key;
			delay_20ms = 0;
			key = KeyScan();
			
			switch(key)
			{
    
    
			case KEY_RIGHT:
				//处理按键
				break;
			case KEY_UP:
				//处理按键
				break;
			//.....
			}	
		}
	}
}

猜你喜欢

转载自blog.csdn.net/Lennon8_8/article/details/104848734
今日推荐