一、电子按键的工作原理

按键开关内部构造主要就是金属弹簧,当按下按键时弹簧片与电路短接,当按键弹起时弹簧片与电路为开路状态。

1.1、独立按键

一端接GPIOx一端接地。GPIOx设置为上拉输入模式,当按键按下时GPIOx读取的为低电平,当按键弹起时GPIOx读取的为高电平。

1.2、矩阵按键

  • 输入小于1/2VC(电源电压)就是低电平,反之是高电平。
  • 设列为低电平输出(R1~R4),设行为高电平输入(C1~C4)。当按键按下时为低电平,按键弹起为高电平。
  • 二、编码

     2.1、GPIO引脚配置

  • VSS:数字电路来说,VSS是接地点
  • VDD:数字电路中,VDD是芯片的工作电压
  • //  GPIO时钟
    #define GPIOx_RCC_APB2Periph RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC \
                                 RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOE|RCC_APB2Periph_GPIOF \
    														 RCC_APB2Periph_GPIOG
    
    void GPIOx_config(st_u32 GPIOx_RCC,GPIO_TypeDef *GPIOxPORT,st_u16 GPIOPin,GPIOMode_TypeDef GPIOMode)
    {
       RCC_APB2PeriphClockCmd(GPIOx_RCC, ENABLE);
      GPIO_InitTypeDef GPIO_InitStructure;
      /* Configure PD0 and PD2 in output pushpull mode */
      GPIO_InitStructure.GPIO_Pin =GPIOPin;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_InitStructure.GPIO_Mode = GPIOMode;
      GPIO_Init(GPIOxPORT, &GPIO_InitStructure);
    }

    2.2、引脚初始化

    void KeyInit(void)
    {
    	unsigned char i;
    	for(;i<7;i++)
    	{
    	  KeyNumber[i]=0x00;
    	}
    	GPIOx_Config(RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_6,GPIO_Mode_Out_PP);
      GPIOx_Config(RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_4,GPIO_Mode_Out_PP);
    	GPIOx_Config(RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_5,GPIO_Mode_Out_PP);
      GPIOx_Config(RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_8,GPIO_Mode_Out_PP);
    //	GPIOx_Config(RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_9,GPIO_Mode_IN_FLOATING);
    //	GPIOx_Config(RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_12,GPIO_Mode_IN_FLOATING);
    //	GPIOx_Config(RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_13,GPIO_Mode_IN_FLOATING);	
    //	
    //	GPIO_SetBits(GPIOB, GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_12|GPIO_Pin_13);
    	GPIO_SetBits(GPIOB, GPIO_Pin_6);
    	GPIO_SetBits(GPIOB, GPIO_Pin_4);
    	GPIO_SetBits(GPIOB, GPIO_Pin_5);
    	GPIO_SetBits(GPIOB, GPIO_Pin_8);
    	
    	
    	
    //	KEYA = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7);
    }

    2.2.1、测试

    void KeyPortBitTest(void)
    {
       if(GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_3)==0)
    	 {
    	   printf("F\r\n");
    	 }
    	 //  读取输入引脚电平
    	  ReadLed=GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_13);  // LED
    	 
    	  ReadGP9=KEY_A;
    	 
    	  ReadGP1=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_6);
    	  ReadGP2=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_4);
    	  ReadGP3=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_5);
    	  ReadGP4=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_8);
    	 
    	 
    	  //  读取输出引脚电平
    	  ReadGP5=GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_6);
    	  ReadGP6=GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_4);
    	  ReadGP7=GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_5);
    	  ReadGP8=GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_8);
    		
    		//  测试未初始化配置引脚电平 
        // 输出	
    		 ReadGP10=GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_9);
    		 ReadGP11=GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_12);
    		 
    		 // 输入
    		 ReadGP12=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9);
    		 ReadGP13=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12);
    }
    
    

     加点量

    #ifndef KEY_H
    #define KEY_H
    
    extern void KeyInit(void);
    extern void Get_Key(void);
    //extern uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);
    extern void KeyPortBitTest(void);
    
    
    #define KEY_ON  1   //  按键按下与GND短接
    #define KEY_OFF 0   //  按键弹起与GND断开
    
    
    #define KEY_RCC RCC_APB2Periph_GPIOB
    #define KEY_PORT   GPIOB
    
    
    #define KEY_A  GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_6)
    #define KEY_B  GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_4)
    #define KEY_C  GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_5)
    #define KEY_D  GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_8)
    #define KEY_E  GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9)
    #define KEY_F  GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)             
    #define Remote_Sensing_Key   GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13) 
    
    //GPIO_ReadOutputDataBit
    //#define KEY_A  GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_3)
    //#define KEY_B  GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_4)
    //#define KEY_C  GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_5)
    //#define KEY_D  GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_8)
    //#define KEY_E  GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_9)
    //#define KEY_F  GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_12)             
    //#define Remote_Sensing_Key   GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_13) 
    
    #define KeyDown_Null       0x00
    #define KeyDown_Forward    0x01
    #define KeyDown_Back       0x02
    #define KeyDown_Right      0x03
    #define KeyDown_Left       0x04
    #define KeyDown_Accelerate 0x05
    #define KeyDown_Moderate   0x06
    #define KeyDown_Stop       0x07
    
    enum key_NUMBER
    {KEYNULL,KEYA,KEYB,KEYC,KEYD,KEYE,KEYF,KEYyg,};
    
    
    
    ext unsigned char KeyScanfTime;
    ext unsigned short int KeySelect;
    ext unsigned char KeyNumber[7];
    
    ext unsigned char ReadGP1;
    ext unsigned char ReadGP2;
    ext unsigned char ReadGP3;
    ext unsigned char ReadGP4;
    
    ext unsigned char ReadGP5;
    ext unsigned char ReadGP6;
    ext unsigned char ReadGP7;
    ext unsigned char ReadGP8;
    
    ext unsigned char ReadGP9;
    ext unsigned char ReadGP10;
    ext unsigned char ReadGP11;
    ext unsigned char ReadGP12;
    ext unsigned char ReadGP13;
    #endif
    
    void Get_Key(void)
    {
    
    if(KeyScanfTime<10)
    {
      KeyScanfTime++;
    }
    else
    {
    	KeyScanfTime=0;
    	switch(Number1)  //  确保只有一个按键按下,使用枚举类型
    	{
    		case KEYNULL:
    			Number1=KEYA;
    			break;
    		
    		case KEYA:
    			//	 KeyNumber[0]=KEY_A&0xFF;             //  1111 1110  按下
            KeyNumber[0]=KEY_A&0x01;	              //  0000 0001  
    	    	Number1=KEYB;
    			break;
    		
    		case KEYB:
    			 //KeyNumber
         		//KeyNumber[1]=(KEY_B<<0x01)&0xFF;     // 1111 1101
    		    KeyNumber[1]=KEY_B<<0x01;               // 0000 0010
    		     Number1=KEYC;
    			break;
    		
    		case KEYC:
    			//KeyNumber[2]=(KEY_C<<0x02)&0xFF;     //  1111 1011
    		  KeyNumber[2]=KEY_C<<0x02;
    	     Number1=KEYD;;
    			break;
    		case KEYD:
    		//	KeyNumber[3]=(KEY_D<<0x03)&0xFF;    //  1111 0111
    		 KeyNumber[3]=KEY_D<<0x03;
    	   Number1=KEYE;
    			break;
    		case KEYE:
    		 //KeyNumber[4]=(KEY_E<<0x04)&0xFF;    // 1110 1111
    		  KeyNumber[4]=KEY_E<<0x04;
    	     Number1=KEYF;
    			break;
    		case KEYF:
    			//KeyNumber[5]=(KEY_F<<0x05)&0xFF;    // 1101 1111
    		   KeyNumber[5]=KEY_F<<0x05;
    	     Number1=KEYyg;
    			break;
    		
    		case KEYyg:
    		//	KeyNumber[6]=(Remote_Sensing_Key<<0x06)&0xFF;   // 1011 1111
    		KeyNumber[6]=Remote_Sensing_Key<<0x06;
    		Number1=KEYNULL;
    			break;
    		
    		default:
    			break;
    	}
    	
    
    }
    }

    按键弹起

     按键按下

    三、按键算法模板

    3.1、使用枚举定义按键列表

    typedef enum   //  按键列表
    {
        KEY_S1,  
        KEY_S2,   
    	KEY_S3,  
    	KEY_S4,   
    	KEY_S5,   
    	KEY_S6,   
    	KEYNUM
    }KEY_TYPEDEF;
    
    typedef enum                      //   检测按键流程   通过switch遍历数据
    {
         KEY_STEP_WAIT,               //   等待按键
    	 KEY_STEP_CLICK,              //   按键按下
    	 KEY_STEP_LOGN_PRESS,         //   长按
    	 KEY_STEP_CONTINOUS_PRESS,    //   持续按下
    }KEY_STEP_TYPEDEF;
    
    extern unsigned char ScanfStep[KEYNUM]; //  按键

    3.2、初始化按键IO

    static void KeyConfig(void)
    {
    
    	  GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
    	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    	 
      	// key1  PB3
    	  GPIOConfig( RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_3,GPIO_Speed_50MHz,GPIO_Mode_IPU);
       
      	// key2  PB5
    	  GPIOConfig( RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_5,GPIO_Speed_50MHz,GPIO_Mode_IPU);
    	 
      	// key3  PB6
    	  GPIOConfig( RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_6,GPIO_Speed_50MHz,GPIO_Mode_IPU);
    	 
    	  // key4  PB7
    	  GPIOConfig( RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_7,GPIO_Speed_50MHz,GPIO_Mode_IPU);
    	  
    	  // key5  PB10
    	  GPIOConfig( RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_10,GPIO_Speed_50MHz,GPIO_Mode_IPU);
    	 
      	// key6  PB11
    	  GPIOConfig( RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_11,GPIO_Speed_50MHz,GPIO_Mode_IPU);
    }

    3.3、IO口封装函数

    //  时钟  端口  引脚  输入输出速度   输出输入模式
    void GPIOConfig(uint32_t RCC_APB2Periph,GPIO_TypeDef* GPIOx,uint16_t GPIOPIN,GPIOSpeed_TypeDef GPIOSPEED,GPIOMode_TypeDef GPIOMODE)
    {
    	 GPIO_InitTypeDef GPIO_InitStructure;
    	
    	 RCC_APB2PeriphClockCmd(RCC_APB2Periph, ENABLE);
    	 GPIO_InitStructure.GPIO_Pin = GPIOPIN;
    	 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	 GPIO_InitStructure.GPIO_Mode = GPIOMODE; ; 
    	 GPIO_Init(GPIOx, &GPIO_InitStructure);
    }

    3.4、获取按键的状态函数

     //  根据原理图知按键按下为低电平,按键未按下为高电平  取反后按键按下为IO口高电平  按键为按下IO为低电平
    static unsigned char GetKey1State(void)
    {
    	 
       return (!GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_3)); 
    }
    
    static unsigned char GetKey2State(void)
    {
       return (!GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_5));
    }
    
    static unsigned char GetKey3State(void)
    {
       return (!GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_6));
    }
    
    static unsigned char GetKey4State(void)
    {
       return (!GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7));
    }
    
    static unsigned char GetKey5State(void)
    {
       return (!GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10));
    }
    
    static unsigned char GetKey6State(void)
    {
       return (!GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11));
    }

    3.5、定义数组保存按键状态

    unsigned char (*GetKeysState[KEYNUM])()={GetKey1State,GetKey2State,GetKey3State,GetKey4State,GetKey5State,GetKey6State};

    3.6、按键值与按键回掉函数的定义

    typedef enum                       //  按键值定义
    {
    	KEY_IDLE_VAL,	//0
    	
    	KEY1_CLICK,	                 	//1   KEY1 单击确认
    	KEY1_CLICK_RELEASE,		        //2   KEY1 单击释放
    	KEY1_LONG_PRESS,		        	//3   KEY1 长按确认
    	KEY1_LONG_PRESS_CONTINUOUS, 	//4   KEY1 长按持续
    	KEY1_LONG_PRESS_RELEASE,		  //5   KEY1 长按释放
    	
    	KEY2_CLICK,								    //6
    	KEY2_CLICK_RELEASE,
    	KEY2_LONG_PRESS,
    	KEY2_LONG_PRESS_CONTINUOUS,
    	KEY2_LONG_PRESS_RELEASE,
    	
    	KEY3_CLICK,							      //11
    	KEY3_CLICK_RELEASE,
    	KEY3_LONG_PRESS,
    	KEY3_LONG_PRESS_CONTINUOUS,
    	KEY3_LONG_PRESS_RELEASE,
    	
    	KEY4_CLICK,						//16
    	KEY4_CLICK_RELEASE,
    	KEY4_LONG_PRESS,
    	KEY4_LONG_PRESS_CONTINUOUS,
    	KEY4_LONG_PRESS_RELEASE,
    	
    	KEY5_CLICK,						//21
    	KEY5_CLICK_RELEASE,
    	KEY5_LONG_PRESS,
    	KEY5_LONG_PRESS_CONTINUOUS,
    	KEY5_LONG_PRESS_RELEASE,
    	
    	KEY6_CLICK,						//26
    	KEY6_CLICK_RELEASE,
    	KEY6_LONG_PRESS,
    	KEY6_LONG_PRESS_CONTINUOUS,
    	KEY6_LONG_PRESS_RELEASE,
    	
    	
    	
    }KEY_VALUE_TYPEDEF;
    
    //  指针函数   返回值类型  指针函数名(参数一,参数二)
    typedef void (*KeyEvent_CallBack_t)(KEY_VALUE_TYPEDEF keys);
    KeyEvent_CallBack_t KeyScanCBS; 
    
    void hal_keyScanCBSRegister(KeyEvent_CallBack_t pCBS)
    {
       if(KeyScanCBS==0)
    	 {
    	   KeyScanCBS = pCBS;
    	 }
    }

    3.7、按键扫描流程

    unsigned char KeyStep[KEYNUM];    //  按键检查流程
    unsigned char KeyScanTimer[KEYNUM];       //  消抖延时
    unsigned char KeyPressLongTimer[KEYNUM]; //  长按延时
    unsigned char KeyContPressTimer[KEYNUM]; //  连续长按延时
    
    void hal_KeyProc(void)
    {
       unsigned char i,KeyState[KEYNUM];
    	 for(i=0;i<KEYNUM;i++)                 //  遍历按键
    	 {
    	    keys=0;
    		  KeyState[i]=GetKeysState[i]();     //  获取按键状态 1~6
    		 switch(KeyStep[i])
    		 {
    			 case KEY_STEP_WAIT:               //  等待按键
    				   if(KeyState[i])               //  获取到按键状态
    					 {
    							KeyStep[i]=KEY_STEP_CLICK;
    					 }
    				break;
    				 
    			 case KEY_STEP_CLICK:              //  按键单击按下
    				 if(KeyState[i])
    				 {
    						 if(!(--KeyScanTimer[i]))
    						 {
    							 KeyScanTimer[i]=KEY_SCANTIME;
    							 KeyStep[i]=KEY_STEP_LOGN_PRESS;
    							 keys=(i*5)+1;
    						 }
    				 }
    				 else
    				 {
    				    KeyScanTimer[i]=KEY_SCANTIME;
    					  KeyStep[i]=KEY_STEP_WAIT;
    				 }
    				 break;
    				 
    			 case KEY_STEP_LOGN_PRESS:           //  长按按键
    				 if(KeyState[i])
    				 {
    				    if(!(--KeyPressLongTimer[i])) // 计数结束 等于0
    						{
    						   KeyPressLongTimer[i]=KEY_PRESS_CONINUE_TIME;
    							 KeyStep[i]=KEY_STEP_CONTINOUS_PRESS;
    						   keys=(i*5)+3;                 //  长按确认
    						}
    				 }
    			   else
    				 {
    				     KeyPressLongTimer[i]=KEY_PRESS_CONINUE_TIME;
    					   KeyStep[i]=KEY_STEP_WAIT;
    					   keys=(i*5)+2;                   //  单击释放
    				 }
    				 break;
    				 
    			 case KEY_STEP_CONTINOUS_PRESS:                  //  持续长按
    				 if(KeyState[i])
    				 {
    				    if(!(--KeyContPressTimer[i]))             //   先自减 再判断值是否等于0
    						{
    						   KeyContPressTimer[i]=KEY_PRESS_CONINUE_TIME; //  连续长按延时
    							 keys=(i*5)+4;               
    						}
    				 }
    				 else
    				 {
    				      KeyStep[i]=KEY_STEP_WAIT;
    					    KeyContPressTimer[i]=KEY_PRESS_CONINUE_TIME;   //  连续长按延时
    					    keys=(i*5)+5;
    				 }
    				 break;
    				 
    				 
    			  default:
    				 break;
    				 
    		 }
    		 //  扫描按键
    		  if(keys)// 按键ID 0~5
    			{
    			  if(KeyScanCBS)
    				{
    				   KeyScanCBS((KEY_VALUE_TYPEDEF)keys);
    				}
    			}
    	 }
    
    }

    事件回调时间

    static void KeyEventHandle(KEY_VALUE_TYPEDEF keys); 
    static  void KeyEventHandle(KEY_VALUE_TYPEDEF keys) //  事件回调函数
    {
       if((keys==KEY1_CLICK)                     //  按键单机
    		 ||(keys==KEY2_CLICK)
    	   ||(keys==KEY3_CLICK)
    		 ||(keys==KEY4_CLICK)
    	   ||(keys==KEY5_CLICK)
    		 ||(keys==KEY6_CLICK))
    	  {
    	  
    
    	  }
    		else if((keys==KEY1_CLICK_RELEASE)  //  按键单机释放
    		 ||(keys==KEY2_CLICK_RELEASE)
    	   ||(keys==KEY3_CLICK_RELEASE)
    		 ||(keys==KEY4_CLICK_RELEASE)
    	   ||(keys==KEY5_CLICK_RELEASE)
    		 ||(keys==KEY6_CLICK_RELEASE))
    		{
    		 
    
    		}
    		else if((keys==KEY1_LONG_PRESS)     //  按键长按
    		 ||(keys==KEY2_LONG_PRESS)
    	   ||(keys==KEY3_LONG_PRESS)
    		 ||(keys==KEY4_LONG_PRESS)
    	   ||(keys==KEY5_LONG_PRESS)
    		 ||(keys==KEY6_LONG_PRESS))
    		{
    		
    
    		}
          	else if(keys==KEY1_LONG_PRESS_CONTINUOUS
    		||keys==KEY2_LONG_PRESS_CONTINUOUS
    		||keys==KEY3_LONG_PRESS_CONTINUOUS
    		||keys==KEY4_LONG_PRESS_CONTINUOUS
    		||keys==KEY5_LONG_PRESS_CONTINUOUS
    		||keys==KEY6_LONG_PRESS_CONTINUOUS)  // 长按持续
    		{
    		
    		
    		}
    		else if(keys==KEY1_LONG_PRESS_RELEASE
    		||keys==KEY2_LONG_PRESS_RELEASE
    		||keys==KEY3_LONG_PRESS_RELEASE
    		||keys==KEY4_LONG_PRESS_RELEASE
    		||keys==KEY5_LONG_PRESS_RELEASE
    		||keys==KEY6_LONG_PRESS_RELEASE)  // 长按释放
    		{
    		
    		
    		}
    }

    作者:旭日初扬

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32电子按键设计指南

    发表回复