一套强悍的实现按键按下_弹起_长按_功能代码

最近做一些项目,对按键操作要求比较复杂,要实现按键按下、弹起、组合、长按等不同状态的响应,之前写过一篇按键实现的文章,现在把代码重新优化了一下,分享给大家,代码实现了下面功能:

  • 按下:(按键被按下去,但没有松开)
  • 弹起 : (松开按键)
  • 重复:(按键被一直按住,键值重复出现,像电脑键盘一样,按住一个字符不放它就能一直输入)
  • 长按:(按住一定时间不松开)
  • 按下时长:(记录按键从按下到弹起的时间)**
    采用时间片轮询方式扫描按键动作,带消抖功能,运行稳定,可移植性好,不同平台,只要简单修改硬件相关的部分。

这个按键实现方法可以满足绝大部分应用的需求,后面有示例讲解

先上代码一堵为快,程序不难,主要部分都有注释,代码有点长,但是可移植性好,可读性强,有不懂的可以留言交流

H文件

/************************************************************
Copyright (C), 2013-2020
@FileName: key.h
@Author  : 祥子  QQ:570525287
@Version : 3.0
@Date    : 2020-10-23
@Description: 按键扫描
@Function List:
@History    : 
<author> <time> <version > <desc>
Lennon 2020/10/23 2.0	增加对按键按下弹起的支持
移植注意事项:
1、注意键值的定义是u8 还是u16类型
2、用define定义的16位数值,要加上 ul,否则可能会出错
***********************************************************/

#ifndef __KEY_H
#define	__KEY_H

#include "stm8s.h"

typedef u16	key_u16;

//按键类型
typedef struct _Key{
	key_u16		Value;		//键值
	u16			PressTime;	//按下时长
	u16			Flag;		//标记
}Key_TypeDef;	

/*---------用bit位表示键值 (最多支持16个键),支持组合键----------*/
//也可以用数字码表示,但不支持组合键
#define KEY_OFF			0x0000
#define KEY_UP			0x0001
#define KEY_DN			0x0002
#define KEY_LEFT		0x0004
#define KEY_RIGHT		0x0008
#define KEY_OK			0x0010
#define KEY_STOP		0x0020
#define KEY_RUN			0x0040

//----------按键动作标记---------
#define FLAG_PRESS		0x8000	//按下
#define FLAG_RELEASE	0x4000	//释放
#define FLAG_REPEAT		0x2000	//重复
#define FLAG_LONG		0x1000	//长按

/*----------按键重复-----------*/
//下面的数据根据Key_Scan函数的扫描周期计算,这里是20ms扫描一次
#define KEY_CNT_SHAKE		2ul		//消抖次数
#define KEY_CNT_REP_SPEED	5ul		//重复键出现的速度  5*20ms = 0.1秒,即按下1秒出现10次
#define KEY_CNT_REP_END		50ul	//按下一定时间后,如果没有松开按键,则出现重复键 50*20ms = 1秒
#define KEY_CNT_LONG_PRESS	99ul	//按下2秒不放为长按	 时间:100*20ms = 2s  这里改成99是防止长按和重复标志同时出现

/*-------------------------------*/
extern Key_TypeDef gKey;	//可以通过这个变量直接得到按键扫描结果,也可以通过Key_GetValue和Key_GetFlag得到

/*-------------函数--------------*/
void Key_Init(void);	
void Key_Scan(void);	//扫描按键

key_u16 Key_GetValue(void);	//读取扫描到的按键
u16 Key_GetFlag(void);	//读取按键动作标记
u16 Key_GetPressTime(void); //读取按下的时长

void KeyTestDemo();

#endif /* __KEY_H */

C文件

/************************************************************
Copyright (C), 2013-2020
@FileName: gKey.c
@Author  : 祥子  QQ:570525287
@Version : 3.0
@Date    : 2020-10-23
@Description: 按键扫描
@Function List:
@History    : 
<author> <time> <version > <desc>
Lennon 2020/10/23 2.0	增加对按键按下弹起的支持,增加按下时间记录
***********************************************************/

#include "key.h"

Key_TypeDef gKey;

//================================与硬件相关的内容,移植时需要修改============================================

//*-----------硬件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); //上拉无中断	
	GPIO_Init(KEY5_PORT,KEY5_PIN,GPIO_MODE_IN_PU_NO_IT); //上拉无中断	
	GPIO_Init(KEY6_PORT,KEY6_PIN,GPIO_MODE_IN_PU_NO_IT); //上拉无中断	
	GPIO_Init(KEY7_PORT,KEY7_PIN,GPIO_MODE_IN_PU_NO_IT); //上拉无中断	
}

//读取按键值
key_u16 Get_IO_Value(void)
{
	key_u16	key_value;

	key_value = 0x00;
	if (!Key1_ReadSta())key_value |= KEY_UP;
	if (!Key2_ReadSta())key_value |= KEY_DN;
	if (!Key3_ReadSta())key_value |= KEY_LEFT;
	if (!Key4_ReadSta())key_value |= KEY_RIGHT;
	if (!Key5_ReadSta())key_value |= KEY_OK;
	if (!Key6_ReadSta())key_value |= KEY_STOP;
	if (!Key7_ReadSta())key_value |= KEY_RUN;
	return key_value;
}

//================================ END ============================================

/*----------------------------------------------------------------------------------
@Function   :Key_Scan
@Description:按键扫描
@Input      :无
@Retrun     :无
@Others     :
----------------------------------------------------------------------------------*/
void Key_Scan(void)
{
	key_u16 key_curr;
	static key_u16 key_value;	
	static key_u16 key_last = 0x00;	//最后一次被按下的键
	static u8 cntDown = 0;		//按下计数
	
	gKey.Value = KEY_OFF;
	gKey.Flag = 0x00;

	key_curr = Get_IO_Value();//读取按键值

	if (key_curr) //有键按下
	{
		if (key_last == key_curr) //当前与最一后次为同一个按键
		{
			cntDown++;
			gKey.PressTime++; //记数按下的时间

			if(cntDown == KEY_CNT_SHAKE) //消抖
			{
				gKey.Value = key_curr;
				gKey.Flag = FLAG_PRESS; //标记按键按下
				key_value = key_curr; //记下按键
				gKey.PressTime = KEY_CNT_SHAKE; //需要重置这个记数
			}
			else if (cntDown == KEY_CNT_REP_END) //达到出现重复键的时间
			{
				cntDown = KEY_CNT_REP_END - KEY_CNT_REP_SPEED; //回退计数
				gKey.Value = key_curr;
				gKey.Flag = FLAG_REPEAT; //标记重复
			}
			
			if (gKey.PressTime == KEY_CNT_LONG_PRESS) //达到长按时间
			{
				gKey.Value = key_curr;
				gKey.Flag = FLAG_LONG; //标记长按
			}		
		}
		key_last = key_curr;	
	}
	else
	{
		if (cntDown > KEY_CNT_SHAKE) //按下达到消抖时间
		{
			gKey.Value = key_value;
			gKey.Flag = FLAG_RELEASE; //标记释放
		}
		cntDown = 0;
		key_last = KEY_OFF;
	}
}

key_u16 Key_GetValue(void)
{
	return gKey.Value;
}

u16 Key_GetFlag(void)
{
	return gKey.Flag;
}

u16 Key_GetPressTime(void)
{
	return gKey.PressTime;
}
//-------------END OF FILE--------------------

应用示例:

void KeyTestDemo();

int main(void)
{
	/*设置内部高速时钟16M为主时钟*/ 
	CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);

	Uart2_Init(115200);
	SystemClock_Init();

	LED_Init();
	Key_Init();
	enableInterrupts();
	while(1)
	{
		if (SysGetSignal_2ms(DELAY_20ms,1))
		{
			KeyTestDemo();
		}
	}
}

//每隔20ms调用一次KeyTestDemo
void KeyTestDemo()
{
	Key_Scan();//按键检测

	if (gKey.Value)
	{
		printf("[%x][%d]  ",gKey.Value,gKey.PressTime); //打印键值和按下时间
	}

	if (gKey.Flag == FLAG_PRESS) //按下
	{
		switch(gKey.Value)
		{
		case KEY_UP:
			LED_STA_ON(); //亮灯
			break;

		case KEY_DN:
			break;

		case (KEY_DN | KEY_UP): //组合键
			LED_STA_ON();//亮灯
			break;
		}
	}
	else if (gKey.Flag == FLAG_RELEASE) //释放
	{
		switch(gKey.Value)
		{
		case KEY_UP:
			LED_STA_OFF();
			break;
			
		case KEY_DN:
			break;
		}
	}
	else if (gKey.Flag == FLAG_REPEAT) //重复
	{
		switch(gKey.Value)
		{
		case KEY_UP:
			LED_STAToggle();
			break;

		case KEY_DN:
			break;
		}
	}
	else if (gKey.Flag == FLAG_LONG) //长按
	{
		switch(gKey.Value)
		{
		case KEY_UP:
			break;

		case KEY_DN:
			break;
		}
	}
}

这个示例演示了KEY_UP按下的时候LED亮,松开后LED灭,可以用在带灯的触摸键上
KEY_UP按住不放后,LED灯闪烁
KEY_UP和KEY_DN同时按下,指示灯亮起

也可以实现同一个按键按住1秒 、3秒、5秒实现不同功能
代码:

	if (gKey.Flag == FLAG_REPEAT) //重复
	{
		switch(gKey.Value)
		{
		case KEY_UP:
			if (gKey.PressTime == 50ul) //20ms扫描一次,50*20=1000,即1秒
			{
				//处理事情A
			}
			else if (gKey.PressTime == 150ul) //150*20=3000,即3秒
			{
				//处理事情B
			}
			else if (gKey.PressTime == 250ul) //150*20=3000,即3秒
			{
				//处理事情C
			}
			break;
		}
	}
  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值