多功能检測按键-3 按键扫描 单按 长按 多个按键 响应方式

/***************************************
// 函数功能:按键扫描
//这是我第三次对按键扫描的优化,为什么我要这么做呢,非常easy,
//我仅仅想将简单的事情不断完好好
//第一次发表:http://blog.csdn.net/chuangwu2009/article/details/9466715
//第一次发表:http://blog.csdn.net/chuangwu2009/article/details/18032195					
*****************************************/

/* 按键响应方式我个人总结了下,一共同拥有11种(包含无按键)
按键响应方式:1:单按键按下响应 2:单按键按下释放响应 3:多按键按下响应

当中按下对应有:1.1:按下立即响应
				1.2:长按后立即响应
				1.3:长按后一直响应(如:长按累加更改时间设置)

释放响应有:      2.1:短按释放响应
				2.2长按释放响应

多按键按下响应有: 1.1 1.2 1.3 2.1 2.2四种组合按键响应方式				 
*/
#define  SETB0   (unsigned char)0x01
#define  SETB1   (unsigned char)0x02
#define  SETB2   (unsigned char)0x04
#define  SETB3   (unsigned char)0x05
#define  SETB4   (unsigned char)0x10
#define  SETB5   (unsigned char)0x20
#define  SETB6   (unsigned char)0x40
#define  SETB7   (unsigned char)0x80



typedef union uFLG1{
	uint8 Flg ;
    struct FLAG1{
        uint8	Flg1   : 1;
        uint8	Flg2   : 1;
        uint8	Flg3   : 1;
        uint8	Flg4   : 1;
        uint8	Flg5   : 1;
        uint8	Flg6   : 1;
        uint8	Flg7   : 1;
        uint8	Flg8   : 1;
    }tFlg1;	
}uFlg1;


uFlg1  F_KEY={0};
//位定义
#define		f_onekey			F_KEY.tFlg1.Flg1//有单按键按下标志
#define		F_longonekey	F_KEY.tFlg1.Flg2//长按标志
#define		f_mulkey			F_KEY.tFlg1.Flg3//多按键标志
#define		F_longmulkey 	F_KEY.tFlg1.Flg4//有多按键按下标志
#define 	F_first    		F_KEY.tFlg1.Flg5//上电识别是否上电按下 开机初始化时 运行 F_first = 1;


//按键响应模式
#define  NONE_KEY			  (unsigned char)0//无效按键值-无按键

#define  ONE_DOWN 			(unsigned char)1
//单个按键按下响应--(短按)按下立即响应
#define	 ONE_LONG_DOWN  (unsigned char)2
//单个按键长按下响应--长按时间到后的(立即)响应
#define  MUL_DOWN			  (unsigned char)3
//多个按键按下响应
#define  MUL_LONG_DOWN  (unsigned char)4
//多个按键长按下响应
#define  ONE_UP    			(unsigned char)5
//单个按键释放响应
#define  MUL_UP    			(unsigned char)6
//多个按键释放响应
#define  ONE_LONG_UP		(unsigned char)7
//单个按键长按后释放响应
#define  MUL_LONG_UP		(unsigned char)8
//多个按键长按后释放响应
#define  ONE_AL_DOWN		(unsigned char)9
//单按键长按之后的响应-长按后一直响应(如:长按累加更改时间设置)
#define  MUL_AL_DOWN		(unsigned char)10
//多按键长按之后的响应

#define  LongAndShortUpIsSame		//长按和短按释放按键功能一样
//定义此宏意思是 在短按按键释放后 或者长按按键后
// 功能运行是一样的 即运行ONE_UP或者MUL_UP的动作

static	 uint8	key_value1=0;//单前按键值
//按键值寄存器-当前按下的值-按下立即响应一次动作

static	uint8	key_value2=0;
//按键值寄存器-前一次的值
//--长按响应一次动作或者按键短按释放响应一次动作
static	uint8	key_value3=0;
//按键值寄存器长按的值---长按后一直响应

static	uint8 mode_key=0;//按键响应模式


void Key_scan(void)	 // 
{


  static	uint8	key_delay=0;//按键去抖动寄存器
  static	uint8	key_value0=0;//前一次按键值寄存器

  static	uint8	key1_mult=0;//多按键寄存器2
  
  static	uint8	key_longtime=0;//长按时间累积寄存器1
  static	uint8	key_longtime1=0;//长按时间累积寄存器2
  
  uint8	key_mult=0;//多按键寄存器1

  
  if(!P_K0)//	
  {
		key_value1|=SETB0;
		key_mult++;
  } 
  
  if(!P_K1)//
  {
		key_value1|=SETB1;	
		key_mult++;
  }
  /*
  if(!P_K2)//
  {
		key_value1|=SETB2;	
		key_mult++;
  } 
  if(!P_K3)//
  {
		key_value1|=SETB3;	
		key_mult++;
  } 
  if(!P_K4)//
  {
		key_value1|=SETB4;	
		key_mult++;
  } 
  */
  
  
  if(key_value1 == key_value0)//如不响等,保存当前按键值,清抖动寄存器,以此次识别
  {
    if(key_delay > 10)//抖动处理
    {
      if(key_value1 > 0)//无按键按下确认,等于0时表示无按键
      {
        if(1 == key_mult)
        {//单按键按下处理程序
            if(!f_onekey)
            { //单按键短按按下处理程序
                    f_onekey=1;
                    key1_mult=0;
                    mode_key=ONE_DOWN;
            }
            else
            {//单按键长按
							if(!F_longonekey)
							{//还没达到长按时间
								if(++key_longtime>=100)//
								{
									 key_longtime=0;
									 if(++key_longtime1>=15)
									 {//长按时间到
													 key_longtime1=0;	
													 mode_key=ONE_LONG_DOWN;
													 F_longonekey=1;
													//长按按键处理程序
									 }
								}
								else								
								{//还没达到长按时间
									mode_key=NONE_KEY;
								}
							}
							else
							{//单按键长按之后的响应
								
								mode_key = ONE_AL_DOWN;

							}
            }
        }
        else
        {	   //多按键按下处理程序
          key1_mult=1;
          if(!f_mulkey)
          {//多按键短按下对应
							f_mulkey=1;	
							f_onekey=1;	
		//	f_onekey=1;	防止多个按键释放时候,不同一时候释放,响应单个按键功能
							mode_key=MUL_DOWN;
          }
          else
          {//多按键长按
						if(!F_longmulkey)
						{
							if(++key_longtime>=100)//2毫秒1次  100*2*15=3秒
							{
								 key_longtime=0;
								 if(++key_longtime1>=15)
								 {
									 key_longtime1=0;//3秒时间到	
									 
									 F_longmulkey=1;
									//多按键长按按键处理程序
									 mode_key=MUL_LONG_DOWN;
									}
																							
							 }
							 else
							{
											mode_key=NONE_KEY;
							}
						}
						else
						{
										//多按键长按之后的响应
										mode_key = MUL_AL_DOWN;
						}
				  }	
			  }   
       }
        else//按键释放或者无按键
       {
              
        f_mulkey=0;
        F_first=0;

        key_longtime=0;
        key_longtime1=0;
        if(F_longonekey)
        {//长按单按键释放
            F_longonekey=0;
            #ifdef (LongAndShortUpIsSame)
						 mode_key=ONE_UP;
						#else
						 mode_key=ONE_LONG_UP;
						#endif
           
                
        }
        else if(F_longmulkey)
        {//长按多按键释放
            F_longmulkey=0;	
    
						#ifdef (LongAndShortUpIsSame)
						 mode_key=MUL_UP;
						#else
						 mode_key=MUL_LONG_UP;
						#endif
				
        }
        else if(f_onekey)
        {//短按按键释放
                        
            if(0==key1_mult)
            {
                mode_key=ONE_UP;
            }
            else
            {
                mode_key=MUL_UP;	
            }
        }
        else
        {
             mode_key=NONE_KEY;
        }
        f_onekey=0;  
       }
    }
    else  
    {//滤波处理
            key_delay++;
    }
  }
  else
  {
          key_value0=key_value1;
          key_delay=0;
  }
		
}

//*************************************
// 函数名称:key_deal
// 函数功能:按键响应后的处理
// 入口參数:无
// 出口參数:无
//***************************************/


void key_deal(void)
{
  static uint8	key_longtime=0;//长按时间累积寄存器1


  switch(mode_key)
  {
    case ONE_DOWN://单个按键按下响应
    {
			switch(key_value1)
			{
			 case SETB1://P_K1短按下响应
			 {		
						key_value2 =	key_value1;		
						
						if(F_first)
						{//按住按键上电响应-要运行的动作-经常使用于自检
							
							
							break;
						}
							break;
				}
				 case SETB0:///P_K0短按下响应
				 {		
							key_value2 =	key_value1;	
							if(F_first)
							{
									
									break;
							}
							
							break;
				 }
				default:  
				break;
			}
			
    key_value1 = 0;	
    
    break;
    }
    case ONE_LONG_DOWN://单个按键长按下响应--对应一次
    {
				key_value3=key_value2;
				
				  switch(key_value2)
					{
									 
						case SETB1:
					 {		
										
									break;
					 }
					 case SETB0:
					 {		
							
									break;
					 }
									
									default:  
									break;
					}
					
					key_value2=0;
                    
    break;
    }
    case MUL_DOWN://多个按键按下响应
    {
					switch(key_value1)
					{		 
							case (SETB1|SETB0):
						 {		
														key_value2 = key_value1;
										break;
						 }	
						default:  
						break;
					}     
          key_value1=0;
      
    break;
    }
    case MUL_LONG_DOWN://多个按键长按下响应
    {
    
				if(key_value2==(SETB0|SETB1))
				{
					key_value3=key_value2;
				}
				
				key_value2=0;
    break;
    }
    case ONE_UP://单个按键释放响应
    {
				switch(key_value2)
				{
								 case SETB0:
								 {//TURBO释放
												
												break;
								 }
								 case SETB1:
								 {							 	
												
												
												break;
								 }
								default:  
								break;
				}
					key_value2=0;//按键释放后清零
    break;
    }
    case MUL_UP://多个按键释放响应
    {        
					key_value2=0;//按键释放后清零
					key_value3=0;

    break;
    }		
    case ONE_LONG_UP://单个按键长按后释放响应
    {	
                    
				switch(key_value3)
				{
						 case SETB0:
						 {//TURBO释放
										
														
										break;
						 }
						 case SETB1:
						 {							 	
										
										
										break;
						 }
						default:  
						break;
				}
				key_value2=0;//
				key_value3=0;
            
    break;
    }
    case MUL_LONG_UP://多个按键长按后释放响应
    {
                            
                     
				key_value2=0;//按键释放后清零
				key_value3=0;
    break;
    }
    case ONE_AL_DOWN://单按键长按之后的响应
    {
                            
			if(++key_longtime>=100)//2毫秒1次  40*15*5=3秒
			{
				key_longtime=0;
							
				 switch(key_value3)
				{
					 case SETB0:
					 {							 	
									
									break;
					 }
					 case SETB1:
					 {							 	
									
									
									break;
					 }
					default:  
					break;
				}	
			}	
    break;
    }
    case MUL_AL_DOWN://多按键长按之后的响应
    {	
          if(++key_longtime>=100)//2毫秒1次  40*15*5=3秒
				{
								 key_longtime=0;
				}	
    break;
    }
    default:
    break;
  }
}


好的,以下是一个使用状态机方式运行的,能够识别5个独立按键并且能够识别双按键同时按下、按、短按、按键双击、按键按的程序。这个程序使用了STM32G030为硬件平台,使用了HAL库进行编程。 ```c #include "stm32g0xx_hal.h" #include <stdbool.h> // 定义按键名称 #define KEY_UP 0x01 #define KEY_DOWN 0x02 #define KEY_LEFT 0x04 #define KEY_RIGHT 0x08 #define KEY_CENTER 0x10 // 定义按键状态 typedef enum { KEY_RELEASED = 0, // 按键释放状态 KEY_PRESSED = 1 // 按键按下状态 } KeyState; // 定义按键信息结构体 typedef struct { GPIO_TypeDef* port; // GPIO端口 uint16_t pin; // GPIO引脚 bool inverted; // 是否反相 KeyState state; // 按键状态 uint32_t time; // 按键按下的时间 uint8_t name; // 按键名称 } KeyInfo; // 初始化按键 void KeyInit(KeyInfo* key, GPIO_TypeDef* port, uint16_t pin, bool inverted, uint8_t name) { key->port = port; key->pin = pin; key->inverted = inverted; key->state = KEY_RELEASED; key->time = 0; key->name = name; GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = pin; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(port, &GPIO_InitStruct); } // 获取按键状态 KeyState GetKeyState(KeyInfo* key) { bool isPressed = HAL_GPIO_ReadPin(key->port, key->pin) ^ key->inverted; if (isPressed && (key->state == KEY_RELEASED)) { key->state = KEY_PRESSED; key->time = HAL_GetTick(); } else if (!isPressed && (key->state == KEY_PRESSED)) { key->state = KEY_RELEASED; } return key->state; } // 判断是否为按事件 bool IsLongPress(KeyInfo* key) { if (GetKeyState(key) == KEY_PRESSED) { uint32_t timeDiff = HAL_GetTick() - key->time; if (timeDiff >= 1000) { return true; } } return false; } // 判断是否为短按事件 bool IsShortPress(KeyInfo* key) { if (GetKeyState(key) == KEY_RELEASED) { uint32_t timeDiff = HAL_GetTick() - key->time; if (timeDiff < 1000) { return true; } } return false; } // 判断是否为双击事件 bool IsDoubleClick(KeyInfo* key) { if (GetKeyState(key) == KEY_RELEASED) { uint32_t timeDiff = HAL_GetTick() - key->time; if ((timeDiff > 100) && (timeDiff < 300)) { return true; } } return false; } // 判断是否为双键同时按下事件 bool IsKeyCombination(KeyInfo* key1, KeyInfo* key2) { if ((GetKeyState(key1) == KEY_PRESSED) && (GetKeyState(key2) == KEY_PRESSED)) { return true; } return false; } int main(void) { // 初始化GPIO HAL_Init(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); // 初始化按键 KeyInfo key_up, key_down, key_left, key_right, key_center; KeyInit(&key_up, GPIOA, GPIO_PIN_0, false, KEY_UP); KeyInit(&key_down, GPIOA, GPIO_PIN_1, false, KEY_DOWN); KeyInit(&key_left, GPIOA, GPIO_PIN_2, false, KEY_LEFT); KeyInit(&key_right, GPIOA, GPIO_PIN_3, false, KEY_RIGHT); KeyInit(&key_center, GPIOB, GPIO_PIN_4, false, KEY_CENTER); // 进入状态机 KeyState state = KEY_RELEASED; uint8_t curKey = 0, prevKey = 0; uint32_t curTime = 0, prevTime = 0; while (1) { switch (state) { case KEY_RELEASED: if (IsKeyCombination(&key_up, &key_down)) { state = KEY_UP_DOWN; } else if (IsKeyCombination(&key_left, &key_right)) { state = KEY_LEFT_RIGHT; } else if (IsLongPress(&key_center)) { state = KEY_CENTER_LONG; } else if (IsShortPress(&key_center)) { state = KEY_CENTER_SHORT; } else if (IsDoubleClick(&key_center)) { state = KEY_CENTER_DOUBLE; } else if (GetKeyState(&key_up) == KEY_PRESSED) { state = KEY_UP; } else if (GetKeyState(&key_down) == KEY_PRESSED) { state = KEY_DOWN; } else if (GetKeyState(&key_left) == KEY_PRESSED) { state = KEY_LEFT; } else if (GetKeyState(&key_right) == KEY_PRESSED) { state = KEY_RIGHT; } break; case KEY_UP: curKey = KEY_UP; curTime = HAL_GetTick(); if (IsShortPress(&key_up)) { printf("Key Up Short Pressed\n"); state = KEY_RELEASED; } else if (IsLongPress(&key_up)) { printf("Key Up Long Pressed\n"); state = KEY_RELEASED; } else if (IsDoubleClick(&key_up)) { printf("Key Up Double Clicked\n"); state = KEY_RELEASED; } break; case KEY_DOWN: curKey = KEY_DOWN; curTime = HAL_GetTick(); if (IsShortPress(&key_down)) { printf("Key Down Short Pressed\n"); state = KEY_RELEASED; } else if (IsLongPress(&key_down)) { printf("Key Down Long Pressed\n"); state = KEY_RELEASED; } else if (IsDoubleClick(&key_down)) { printf("Key Down Double Clicked\n"); state = KEY_RELEASED; } break; case KEY_LEFT: curKey = KEY_LEFT; curTime = HAL_GetTick(); if (IsShortPress(&key_left)) { printf("Key Left Short Pressed\n"); state = KEY_RELEASED; } else if (IsLongPress(&key_left)) { printf("Key Left Long Pressed\n"); state = KEY_RELEASED; } else if (IsDoubleClick(&key_left)) { printf("Key Left Double Clicked\n"); state = KEY_RELEASED; } break; case KEY_RIGHT: curKey = KEY_RIGHT; curTime = HAL_GetTick(); if (IsShortPress(&key_right)) { printf("Key Right Short Pressed\n"); state = KEY_RELEASED; } else if (IsLongPress(&key_right)) { printf("Key Right Long Pressed\n"); state = KEY_RELEASED; } else if (IsDoubleClick(&key_right)) { printf("Key Right Double Clicked\n"); state = KEY_RELEASED; } break; case KEY_CENTER_SHORT: printf("Key Center Short Pressed\n"); state = KEY_RELEASED; break; case KEY_CENTER_LONG: printf("Key Center Long Pressed\n"); state = KEY_RELEASED; break; case KEY_CENTER_DOUBLE: printf("Key Center Double Clicked\n"); state = KEY_RELEASED; break; case KEY_UP_DOWN: printf("Key Up and Down Pressed\n"); state = KEY_RELEASED; break; case KEY_LEFT_RIGHT: printf("Key Left and Right Pressed\n"); state = KEY_RELEASED; break; } // 判断按键双击 if ((curKey != 0) && (curKey == prevKey)) { uint32_t timeDiff = curTime - prevTime; if ((timeDiff > 100) && (timeDiff < 300)) { printf("Key %d Double Clicked\n", curKey); curKey = 0; } } // 记录上一个按下的按键和时间 if (curKey != 0) { prevKey = curKey; prevTime = curTime; } } } ``` 在这个程序中,我们首先定义了按键名称以及按键状态枚举类型。然后我们定义了包含按键信息的结构体`KeyInfo`,包含GPIO端口、引脚、状态、按下时间以及按键名称等信息。接着我们定义了一些函数,包括初始化按键、获取按键状态、判断是否为按事件、短按事件、双击事件以及双键同时按下事件等。最后我们通过状态机的方式来处理按键事件,包括按键短按、按、双击事件以及双键同时按下事件等。同时我们还记录了上一个按下的按键以及时间,以便判断按键双击事件。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值