STM32的GPIO_蓝桥杯按键实验_实现松手检测/长按短按/单击双击

按键实验简介

按键实验原理

  • 按键的硬件结构

    蓝桥杯开发板上的按键默认为抬起状态,此时经上拉电阻接VDD,连接引脚状态为高电平;按下后线路导通接地,转变为低电平

  • 检测按键的方法

    使用查询法,不断读入按键相连GPIO输入状态,从而判断按键的状态

按键的GPIO引脚配置

  • 按键的GPIO引脚连接

    开发板有4个独立按键B1-B4,分别连接PB0,PB1,PB2,PA0

  • 按键的GPIO初始化配置

    将PB0,PB1,PB2,PA0配置为GPIO_Input

按键的使用

按键使用的HAL库函数

  • 读取引脚电平状态HAL_GPIO_ReadPin
    GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
    /*
    GPIOx:端口号,如GPIOA,GPIOB,GPIOC……
    GPIO_Pin:引脚号,如GPIO_PIN_0,GPIO_PIN_1,GPIO_PIN_2……
    返回值:GPIO_PinState,即引脚状态GPIO_PIN_SET或GPIO_PIN_RESET
    */
    

代码实现

  • 按键查询函数

    也就是通过不断查询按键引脚电平状态,来判断按键是否被按下了

    uint8_t Key_Scan(void)
    {
    	uint8_t key_val=0;
    	if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==GPIO_PIN_RESET)
    	{
    		HAL_Delay(20);//加入延时去抖的写法,也可不写
    		if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==GPIO_PIN_RESET)
    		{
    			key_val=1;
    		}
    	}
    	if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)==GPIO_PIN_RESET)
    	{
    		HAL_Delay(20);
    		if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)==GPIO_PIN_RESET)
    		{
    			key_val=2;
    		}
    	}
    	if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)==GPIO_PIN_RESET)
    	{
    		HAL_Delay(20);
    		if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)==GPIO_PIN_RESET)
    		{
    			key_val=3;
    		}
    	}
    	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET)
    	{
    		HAL_Delay(20);
    		if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET)
    		{
    			key_val=4;
    		}
    	}
    	return key_val;//返回key_val来判断按下的是哪一个键
    }
    //在main函数中使用时,注意C语言参数传递不传变量本身的特性:
    key_val=Key_Scan();
    
  • 定时查询法

    例如将按键扫描改为定时扫描法,减少对资源的占用

    //使用flag标记按键的扫描
    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    //0.001秒定时器中断,也可放在Systick中
    {
    	flag=1;
      	//不直接把按键检测函数放在中断中,避免停留在中断内太久妨碍其他程序执行
    }
    void key_scan()
    {
    	if(flag==1)
    	{
    		//按键的检测
    	}
    	flag=0;
    }
    

功能拓展

带有松手检测的完整按键处理函数

  • 按键松手检测
    //为按下按键增加抬手检测功能
    uint8_t key_val;//读取按键值
    uint8_t key_down;//按键下降沿检测,只在按键按下瞬间为按键值,其他时刻全为0
    uint8_t key_up;//按键上升沿检测,只在按键抬起瞬间为按键值,其他时刻全为0
    uint8_t key_old;//保存上一次检测按键值
    
    void Key_Proc(void)
    {
    	key_val = Key_Scan();//查询哪一个键被按下
    	key_down = key_val & (key_val ^ key_old);
    	//新旧键值异或,两者不同时得1,由此检测哪些键发生了变化
    	//与当前键值作按位与运算,只有按键被按下的情况下才保留变化位
    	//这样就把那些被按下的按键的键值保存到key_up
    	key_up = ~key_val & (key_val ^ key_old);
    	//同上,但加入取反符号,检测哪些按键被释放,把释放的按键保存到key_up
    	key_old = key_val;//保存当前的按下的键值用以下一次判断
    	if(key_down==1)
    	{
    	 	//此处写按键1按下后执行的操作
    	}
    }
    
  • 按键处理函数用例
    void Key_Proc(void)
    {
    	key_val=Key_Scan();
    	key_down = key_val & (key_val^key_old);
    	key_up = ~key_val & (key_val^key_old);
    	key_old=key_val;
    	if(key_down==3)//如果key3被按下
    	{
    		enled=0x01;
    	}
    	if(key_up==3)//如果key3松开
    	{
    		enled=0x00;
    	}
    }
    

区分更多按键操作

  • 长按与短按

    在按键处理函数中增加一个计时变量(使用SysTick实现),按下后开始计时,超过某个时间没有抬起就是长按

    void Key_Proc(void)
    {
    	key_val=Key_Scan();
    	key_down = key_val & (key_val^key_old);
    	key_up = ~key_val & (key_val^key_old);
    	key_old=key_val;
      
    	if(key_down)//按下后开始使用SysTick计时
    	{
    		uskey=0;
    	}
    	if(uskey<1000)//1s内抬起为短按,务必注意uskey的数据类型取值是否足够
    	{
    		if(key_up==1) 
    		{
    			//按键1短按操作
    		}
    		if(key_up==2)
    		{
    			//按键2短按操作
            }
    		if(key_up==3)
    		{
    			//按键3短按操作
    		}
    		if(key_up==4)
    		{
    			//按键4短按操作
    		}
    	}
    	else//1s后未抬起为长按
    	{
    		if(key_val==1) 
    		{
    			//按键1长按操作
    		}
    		if(key_val==2)
    		{
    			//按键2长按操作
    		}
    		if(key_val==3)
    		{
    			//按键3长按操作
    		}
    		if(key_val==4)
    		{
    			//按键4长按操作
    		}
    	}
    }
    
  • 单击与双击

    在区分长按短按基础上,再增加检测规定时间内是否有再次按下同一按键

    但是当按键抬起后变量key_up会清零,无从判断两次点击按键值是否相同,所以要增添一个临时变量保存按键值

    计时(用以检测是否有第二次按下的行为)并非一直运行,而是在上电后第一次按下,双击结束后的下一次按下以及超时后(当前操作为单击)的下一次按下才需要计时,计时的启停通过设置标志位实现

    void Key_Proc(void)
    {
    	key_val=Key_Scan();
    	key_down = key_val & (key_val^key_old);
    	key_up = ~key_val & (key_val^key_old);
    	key_old=key_val;
      
    	if(key_up)//如果有按键处在抬起阶段
    	{
    		key_temp=key_up;//记下这个键
    		if(key_flag==0)//key_flag为0表示没有按键抬起,可以计时
    		{
    			uskey=0;
    			key_flag=1;//将标志位改为1以表示有按键抬起并开始计时
    		}
    		else//key_up且key_flag=1,代表双击结束后的第二次上升沿
    			key_flag=0;
    	}
    	if(key_flag==1)//如果在计时状态(已经按下了一次)
    	{
    		if(uskey<300)//300ms内有按键按下为双击 
    		{
    			if(key_down==1&&key_temp==1) 
    			{
    				//按键1双击操作
    			}
    			if(key_down==2&&key_temp==2) 
    			{
    				//按键2双击操作
    	        }
    			if(key_down==3&&key_temp==3) 
    			{
    				//按键3双击操作
    			}
    			if(key_down==4&&key_temp==4) 
    			{
    				//按键4双击操作
    			}
    		}
    		else//300ms内无按键为单击
    		{
    			if(key_temp==1) 
    			{
    				//按键1单击操作
    			}
    			if(key_temp==2)
    			{
    				//按键2单击操作
    			}
    			if(key_temp==3)
    			{
    				//按键3单击操作
    			}
    			if(key_temp==4)
    			{
    				//按键4单击操作
    			}
    			key_flag=0;//清零标志位
    		}
    }
    

posted on 2025-05-08 19:51  无术师  阅读(465)  评论(0)    收藏  举报