单片机独立按键模块(含短按,长按,连发功能)

本文介绍了一种自定义的单片机按键扫描模块,支持短按、长按和连发功能,通过配置KeyInitStruct结构体来定制不同按键行为。它不支持多键同时按下,但具有高度灵活性和移植性,只需修改对应功能函数即可实现用户需求。
摘要由CSDN通过智能技术生成

       最近看了很多按键扫描的文章,发现各有长处,后来自己花了一点时间做了一个单片机独立按键扫描的模块,此模块优点颇多,支持短按,长按,连发功能,只要配置相关结构体就可以实现这些功能,唯一的缺点是不支持多按键同时按下功能(后续有时间加上)

       先简单介绍下整体思路,首先在Key_Scan.h里面定义一个按键结构体KeyInitStruct,里面包含按键的一些设置

#define Key_Num								 5            //定义按键的个数

//按键连按结构体  	
typedef  struct 
{
  uint8_t	            const Key_Continuous_flg;         //按键是否支持连按功能,0-无连按,1-连按
	uint16_t            const Key_down_TimeSet;           //连按时间设置   
  uint16_t            const Key_down_frequency;         //连按频率设置  	
}Key_DownTypeStruct;


//按键长按结构体
typedef  struct 
{
  uint8_t	            const Key_Longdown_flg;           //按键是否支持长按功能,0-无长按,1-长按
	uint16_t            const Key_L_down_TimeSet;         //长按时间设置  
	void(*Key_longdown_Dut)(void);                        //长按函数指针
}Key_LongDownStruct;


//按键设置结构体
typedef  struct  
{ 
	GPIO_TypeDef*       GPIO_X;                     //GPIO口 
	uint16_t            GPIO_PIN;                   //GPIO引脚
	uint8_t 	          Key_Value;                  //按键不按时引脚电平
	uint8_t	            Key_Down;                   //按键防抖处理后按下标志  0-没按下,1-按下
	Key_LongDownStruct	Key_LongDown;               //按键长按结构体
	Key_DownTypeStruct  Key_DownType;               //按键连按结构体  	
	void(*Key_shortdown_Dut)(void);                 //短按函数指针
}KeyInitStruct ; 

然后在Key_Scan.c定义一个Key的结构体数组

KeyInitStruct Key[Key_Num]={    
	{GPIOC,GPIO_Pin_7,1,0,{1,100,KeySet_Longdown_Dut},{1,75,10},KeySet_shortdown_Dut},  //SET按键    
	{GPIOC,GPIO_Pin_9,1,0,{0,500},{1,75,5},KeyOPEN_shortdown_Dut},	          //OPEN按键
	{GPIOC,GPIO_Pin_8,1,0,{0,500},{0,50,5},KeySTOP_shortdown_Dut},	          //STOP按键
	{GPIOC,GPIO_Pin_6,1,0,{0,500},{1,75,5},KeyCLOSE_shortdown_Dut},           //CLOSE按键
	{GPIOC,GPIO_Pin_14,1,0,{1,400,KeyLearn_Longdown_Dut},{0,50,5},KeyLearn_shortdown_Dut}, //LEARN按键
};

在Key_Scan.c放入按键功能函数,将此函数放在20ms定时器里面,该函数一般不用更改(假如用户要在按键松开实现功能,可把对应函数移动到程序松开位置)

/******************************************************************************
 Function    :void Key_Scan(void)

 ------------------------------------------------------------------------------
 Description :按键扫描函数

 -----------------------------------------------------------------------------
 Returnvalue :

 -----------------------------------------------------------------------------
 Parameters  :

 -----------------------------------------------------------------------------
 Date        :

 -----------------------------------------------------------------------------
 Remarks     :
              
 ******************************************************************************/
void Key_Scan(void)
{
	uint8_t i;
	for(i=0;i<Key_Num;i++)
	{
		if(Key[i].Key_Value ==1)                   //硬件电路是否上拉
		Key[i].Key_Down = 1-GPIO_ReadInputDataBit(Key[i].GPIO_X,Key[i].GPIO_PIN);  //硬件电路上拉,按键按下则为0
    else
		Key[i].Key_Down = GPIO_ReadInputDataBit(Key[i].GPIO_X,Key[i].GPIO_PIN);         
	}
}
	

/******************************************************************************
 Function    :void Key_Function(void)

 ------------------------------------------------------------------------------
 Description :按键功能函数,放在20ms定时器

 -----------------------------------------------------------------------------
 Returnvalue :

 -----------------------------------------------------------------------------
 Parameters  :

 -----------------------------------------------------------------------------
 Date        :

 -----------------------------------------------------------------------------
 Remarks     :
              
 ******************************************************************************/
void Key_Function(void)
{
	uint8_t i;
	static uint8_t Pre_Key_St[Key_Num] = {0};          //定义一个按键上次状态的变量
	static uint16_t Key_St_cnt[Key_Num]={0};           //按键按下计数变量

	Key_Scan();
	for(i=0;i<Key_Num;i++)
	{
		if( Key[i].Key_LongDown.Key_Longdown_flg==0 )    //无长按功能
		{
			if( Key[i].Key_DownType.Key_Continuous_flg==0 )//无连按功能
			{				
				if( Pre_Key_St[i] ^ Key[i].Key_Down )        //按键上一次与现在状态不一致,说明按键要么按下,要么松开
				{
					 Pre_Key_St[i] = Key[i].Key_Down;          //这条语句很关键,自己体会,无法言语

						if(Key[i].Key_Down)                      //按键按下
						{                              
							Key[i].Key_shortdown_Dut();
						}
						else                                     //按键松开
						{
																										 //可以在此添加按键松开的函数
						}
				}
				else
				{
				 Pre_Key_St[i] = Key[i].Key_Down;;     
				}
			}
			else                                           //连按功能
			{
				if( Pre_Key_St[i] ^ Key[i].Key_Down )        //按键上一次与现在状态不一致,说明按键要么按下,要么松开
				{
					 Pre_Key_St[i] = Key[i].Key_Down;          //这条语句很关键,自己体会,无法言语

						if(Key[i].Key_Down)                      //按键按下
						{                              

							Key[i].Key_shortdown_Dut();
						}
						else                                     //按键松开
						{
																										 //可以在此添加按键松开的函数
						}
				}
				else
				{
				 Pre_Key_St[i] = Key[i].Key_Down;    
				}				
				if(Key[i].Key_Down)                      
				{
					Key_St_cnt[i]++;                            //按键按下开始计数
				}
				else   
				{       
					Key_St_cnt[i] = 0;                          //松开计数清零
				}
				if(Key_St_cnt[i]>Key[i].Key_DownType.Key_down_TimeSet)              //按键按下时间大于连按时间设置
				{
					if((Key_St_cnt[i]-Key[i].Key_DownType.Key_down_TimeSet)%Key[i].Key_DownType.Key_down_frequency==0) //进入函数的频率
					{

						Key[i].Key_shortdown_Dut();
					}
				}
			}
	  }
		else                                           //有长按功能
		{
			if(Key[i].Key_Down)
			{
				Key_St_cnt[i]++;
			}
			if(Key_St_cnt[i]>0 && Key_St_cnt[i]<Key[i].Key_LongDown.Key_L_down_TimeSet)               //短按
			{
				if(Key[i].Key_Down == 0)                                                                //短按松开
				{

					Key[i].Key_shortdown_Dut();
					Key_St_cnt[i] = 0;
				}
			}
		  else if(Key_St_cnt[i]>=Key[i].Key_LongDown.Key_L_down_TimeSet)
			{
				if( Pre_Key_St[i] ^ Key[i].Key_Down )        					 //按键上一次与现在状态不一致,说明按键要么按下,要么松开
				{
					 Pre_Key_St[i] = Key[i].Key_Down;         				   //这条语句很关键,自己体会,无法言语

						if(Key[i].Key_Down)                      					 //按键按下
						{                               
							Key[i].Key_LongDown.Key_longdown_Dut();
						}
						else                                    					 //按键长按松开
						{
							Key_St_cnt[i] = 0;										 					 //按键长按松开计数清零
						}
				}
				else
				{
				 Pre_Key_St[i] = Key[i].Key_Down;    
				}				
			}
		}
  }
}

最后是按键对应的功能函数,用户只要在此更改自己所要实现的功能就行

void KeySet_shortdown_Dut(void)
{
//在此添加按键所对应的功能
}
void KeyOPEN_shortdown_Dut(void)
{
//在此添加按键所对应的功能
}
void KeySTOP_shortdown_Dut(void)
{
//在此添加按键所对应的功能
}
void KeyCLOSE_shortdown_Dut(void)
{
//在此添加按键所对应的功能
}
void KeyLearn_shortdown_Dut(void)
{
//在此添加按键所对应的功能
}
void KeySet_Longdown_Dut(void)
{
//在此添加按键所对应的功能
}
void KeyLearn_Longdown_Dut(void)
{
//在此添加按键所对应的功能
}

总结:用户只要配置按键结构体函数,定义按键个数Key_Num ,以及更改对应函数的功能。就可以实现所想要的功能,非常灵活,移植度高。下次有时间在做个按键与数码管二三级菜单设置模块。  

  • 4
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值