一种多按键扫描方法(短按、长按、组合键)

**前言:**本文仅为个人简单记录,文章格式不做处理(等有时间再整理吧)。文章内容仅供参考,若有更好的想法,或者有疑问欢迎在评论留言。

正片:
关于按键扫描、按键处理网上有很多文章,本文提供了一个不同IO、按键判断方法不同的多按键扫描处理方法(有缺陷)。

应用场景:
1、按键数量不多,如4、5个。
2、按键IO口分布在不同P口(如P1、P2、PA、PB等)。
3、按键识别方式不一(上拉、下拉、ADC检测)。
4、按键短按、短按释放、长按、长按释放判断。
5、组合键判断。
如上述情景下,矩阵按键扫描显然不适用,每个按键单独消抖判段,程序又会庞杂,不精炼。

整体思想:
1、为每个按键定义一个键码值。键码值以8、4、2、1形式编码,
2、使用定时器,定时扫描按键,多次扫描消抖。
3、由于是定时扫描,则在按键扫描函数里计数,即可用来做按键按下时长判断,从而判断按键长按、短按。

下面提供按键处理参考代码(懒得区分文件了)。

//枚举按键键码
typedef enum ENUM_KEY_CODE
{
  E_KEY_CODE_NONE = 0,
  E_KEY_CODE_MENU = 0x01,
  E_KEY_CODE_INC = 0x02,//加-上
  E_KEY_CODE_DEC = 0x04,//减-下
  E_KEY_CODE_OK = 0x08,
  E_KEY_CODE_POWER = 0x10,
  //。。。
} E_KEY_CODE;
//枚举按键状态
typedef enum ENUM_KEY_EVENT
{
  E_KEY_EVT_NONE = 0,
  E_KEY_EVT_SHORT_PRESS,
  E_KEY_EVT_SHORT_RELEASE,
  E_KEY_EVT_LONG_PRESS,
  E_KEY_EVT_LONG_RELEASE,
  E_KEY_EVT_TIMEOUT,	//按键超时
} E_KEY_EVENT;

//全局变量
uint8_t KEY_Code = E_KEY_CODE_NONE;	//按键键码
uint8_t KEY_Evt = E_KEY_EVT_NONE;	//按键状态


void KeyScan()
{
	// 20ms进行按键扫描
	static uint8_t KeyCode_New = E_KEY_CODE_NONE; //新的按键值
	static uint8_t KeyCode_Old = E_KEY_CODE_NONE; //旧的按键值
	static uint8_t Key_count = 0;	 //消抖计数
	static uint8_t KeyTimeCnt = 0; //按键按下时间计数

	/*
	对应按键按下或释放判断,可根据实际情况(上拉下拉,或者时ADC口),
	按键按下则 对应键码置1,
	按键释放则 对应键码清零
	*/
	//菜单键
	if (MENU_KEY_TRUE)
		KeyCode_New |= E_KEY_CODE_MENU;
	else
		KeyCode_New &= ~E_KEY_CODE_MENU;
	// OK键
	if (KEY_OK_TRUE)
		KeyCode_New |= E_KEY_CODE_PTT;
	else
		KeyCode_New &= ~E_KEY_CODE_PTT;
	//下
	if (KEY_DOWN_TRUE)
		KeyCode_New |= E_KEY_CODE_DEC;
	else
		KeyCode_New &= ~E_KEY_CODE_DEC;
	//上
	if (KEY_UP_TRUE)
		KeyCode_New |= E_KEY_CODE_INC;
	else
		KeyCode_New &= ~E_KEY_CODE_INC;
	// Power
	if (KEY_POWER_TRUE)
		KeyCode_New |= E_KEY_CODE_POWER;
	else
		KeyCode_New &= ~E_KEY_CODE_POWER;

	//通过判断当前键码与上一次保存的键码是否相同来消抖,
	//如果不同则更新键码值,清除计数变量,
	if (KeyCode_New != KeyCode_Old)
	{
		KeyCode_Old = KeyCode_New;
		Key_count = 0;
		// KeyTimeCnt = 0;
	}
	else	//键码值相同
	{
		Key_count++; //消抖计数

		if (Key_count >= 3) //按键值相同,且连加3次   消抖时间3*20mS =60mS
		{
			Key_count = 3;

			if (KeyCode_Old != E_KEY_CODE_NONE) //按键值不为空
			{
				if (KeyTimeCnt < 250)
					KeyTimeCnt++; //按键时间计数

				if (KeyTimeCnt == 1) // 立马指示按下,KEY_Evt 只置位一次
				{
					ioKeyEvt = 1;
					KEY_Code = KeyCode_Old;
					KEY_Evt = E_KEY_EVT_SHORT_PRESS; //按下
				}
				else
				{
					if (KeyTimeCnt == 33) //指示长按,KEY_Evt只置位一次
					{
						ioKeyEvt = 2;
						KEY_Evt = E_KEY_EVT_LONG_PRESS; //长按
					}
				}
			}
			else //所有按键释放
			{
				if (ioKeyEvt == 1)
				{
					KEY_Evt = E_KEY_EVT_SHORT_RELEASE; //短按释放
				}
				else 
				{
					if (ioKeyEvt == 2)
						KEY_Evt = E_KEY_EVT_LONG_RELEASE; //长按释放
				}
				ioKeyEvt = 0;
				KeyTimeCnt = 0;
			}
		}
	}
}
//按键处理函数
void KEY_FUN()
{
	switch (KEY_Code)
	{
	case E_KEY_CODE_NONE:

		break;
	case E_KEY_CODE_POWER:
		if (KEY_Evt == E_KEY_EVT_SHORT_PRESS) 		
		{
		}
		else if (KEY_Evt == E_KEY_EVT_SHORT_RELEASE) //按键短按释放
		{
		}
		else if (KEY_Evt == E_KEY_EVT_LONG_PRESS) 	//按键长按
		{
		}
		else //按键长按释放
		{
		}
		break;
	case E_KEY_CODE_MENU:
		//同上
		break;
	case E_KEY_CODE_INC:
		//同上上
		break;
	case E_KEY_CODE_DEC:
		//同上上上
		break;
	case E_KEY_CODE_PTT:
		//同上上上上
		break;

	default: //组合键,组合键功能可根据需要自行设置,可增加组合键的键码

		if (KEY_Evt == E_KEY_EVT_SHORT_PRESS) 		//按键按下
		{
		}
		else if (KEY_Evt == E_KEY_EVT_SHORT_RELEASE) //按键短按释放
		{
		}
		else if (KEY_Evt == E_KEY_EVT_LONG_PRESS) 	//按键长按
		{
		}
		else //按键长按释放
		{
		}
		break;
	}
}
uint8_t TIME_20MS_FLAG = 0;
//伪代码
void TIME_20mS_IRQ()	//定时器中断函数
{
	TIME_20MS_FLAG = 1;
}

void main()
{
	//定时器初始化
	while(1)
	{
	//定时中断中将标志置1,按键扫描一次时间 20MS
		if(TIME_20MS_FLAG == 1)
		{
			KeyScan();
			if (KEY_Evt != E_KEY_EVT_NONE)
			{
				KEY_FUN();
				KEY_Evt = E_KEY_EVT_NONE;
			}
			TIME_20MS_FLAG = 0;
		}
	}
}

//上述方法还是有些缺陷的,不过已经能满足基本的按键长短按判断,及简单组合键的判断。

//最后,希望对看到本文的你有些帮助。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值