按键实现单击、双击、长按 方便移植版本

参考网友 @暖暖的纠结 的按键方法,做了些优化,做了一个方便移植的按键版本。

代码如下:

头文件

#ifndef KEY_DETECT_H
#define KEY_DETECT_H

typedef struct{
	
	       unsigned char pressdown_flag;      //按键按下标志,在中断中置1,key_detect函数中置0
	       unsigned char short_flag;          //短按标志,key_detect中置1,需要手动置0!
	       unsigned char long_flag;           //长按标志,key_detect中置1,需要手动置0!
	       unsigned char double_flag;         //双击标志, key_detect中置1,需要手动置0!
	       unsigned char (*read_input)(void); //检测按键输入函数,根椐实际情况封装
       }key_s;

void key_detect (key_s * k);

#endif  // KEY_DETECT_H

C文件

#include "key_detect.h"

void key_detect (key_s * k)
{
	static int  down_counter = 0;            //按键持续按下计数器
	static int  up_counter = 0;             //有效单击抬起按键后的计数器
	static unsigned char  up_flag = 0;      //有效单击抬起按键后,生成按键抬起标志 
	
  if(k->pressdown_flag == 1)               //发生按键按下	
   {
      if(k->read_input() == 0)               //按键持续按下,注意实际函数的逻辑!!		
        {         
          if(down_counter <= 2000)          //长按阈值,跟据实际情况更改!!
           {
             down_counter++; 
           }
					 
           else                             //按键按下到2000,就判断长按时间成立,生成长按标志 
           { 
             k->long_flag = 1;             //长按键标志置位
             k->pressdown_flag = 0;        //清按键按下标志 
			 down_counter = 0;             //清除按下计数
			return;                       //跳出函数
            } 
         }
				
       else                                 //按键抬起
         { 
          if(down_counter > 20)             //消抖阈值:按下时间大于20,则为有效。可更改!!
            {
              up_flag = 1;                  //有效单击抬起按键后,生成按键抬起标志 
							
             //距离上次单击时间在1~500计数周期之间,再次单击,则认为发生双击事件
              if(up_counter > 1 && up_counter < 500)   //双击间隔阈值,可更改!!
                { 
									
                  k->double_flag = 1;        //双击键标志置位
				 up_flag = 0;               //清除按键抬起标志
				 up_counter = 0;            //清除按键抬起计数
									
                } 
             } 

			 k->pressdown_flag = 0;
         	 down_counter = 0; 					 
          }
   }
	 
  if(up_flag)               //有效单击抬起后,启动计数
       up_counter++;
	
  if(up_counter > 500)      //计数到500  ,没有连击,则视为单击;配合上面双击间隔阈值,一起更改!!
    { 
	  k->short_flag = 1;    //单击键标志置位
      up_counter = 0;
      up_flag = 0;
    }
}


应用main文件,在stm32单片机上测试

.
.
.
//结构体实例化
unsigned char read_k1_pin(void)
{
  if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0)== GPIO_PIN_RESET) return 1;
  else return 0;
}
key_s K1={0,
          0,
          0,
	      0,
	       read_k1_pin,
          };
.
.
.
//按键中断函数  
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if(GPIO_Pin== GPIO_PIN_0)
  {
    K1.pressdown_flag = 1;
 
  }

} 
.
.
.
.
//定时器中断,定时周期是1ms,可根据实际情况更改
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{

  if (htim->Instance == TIM3) {
    
		
		key_detect ( &K1 );
		
  }

}

.
.
.
//while中处理按键事件
  while (1)
  {
		
		if(K1.short_flag==1)
		{
		  K1.short_flag=0; //清除标记
			HAL_GPIO_TogglePin(LED_BLUE_GPIO_Port,LED_BLUE_Pin);
		}
			
		if(K1.double_flag==1)
		{
			K1.double_flag=0;
			HAL_GPIO_TogglePin(LED_GREEN_GPIO_Port,LED_GREEN_Pin);
		}
		
		if(K1.long_flag==1)
		{
		  K1.long_flag=0;
			HAL_GPIO_TogglePin(LED_RED_GPIO_Port,LED_RED_Pin);
		}
		
		
		
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }       

你可能感兴趣的:(嵌入式,单片机,stm32,嵌入式)