蓝桥杯专题:STM32G431RBT6按键处理与算法精讲(三行代码实现消抖、短按长按与单击双击功能)下篇解析(二)

消抖完整代码见http://t.csdnimg.cn/cWWFq

目录

一、

1.三行代码是什么?

 2.三行代码算法的原理是什么?

1.符号

2.以按下按键二举例

3.为什么能消抖

二、

1.长按与短按(含详细解释)

2.单击与双击(含详细解释)

一、

1.三行代码是什么?

    Key_down=key_rval&(key_rval^key_old);
	Key_up=~key_rval&(key_rval^key_old);
	key_old=key_rval;

 2.三行代码算法的原理是什么?

1.符号

 `~` 表示按位取反(bitwise NOT)操作,将每一位上的 0 变为 1,1 变为 0。

`&` 表示按位与(bitwise AND)操作,只有当两个对应位上都为 1 时,结果才为 1,否则为 0。

^ 运算符表示按位异或(bitwise exclusive OR)操作。:当两个对应位上只有一个为 1 时,结果为 1,否则为 0。

2.以按下按键二举例

初始状态:

  • key_rval 为其他按键值(非按键二的值)。
  • Key_upKey_down 为 0。
  • key_old 为其他按键值。
  • 按下按键二时:

  • key_rval 更新为按键二的值(设为 0x02)。
  • key_rval ^ key_old 的结果不为 0。当两个不同的数进行按位异或运算时,对于每一位来说,如果两个数在这一位上的值不同(一个为 0,另一个为 1 或反之),那么异或的结果在这一位上就是 1,key_rval 会从之前的值变为对应按键的新值,而 key_old 仍然是之前的按键值

    因为两者不同,所以它们按位异或的结果就会有至少一位是 1,导致结果不为 0。这种按键值都是0000 0010  0000 0000这种形式如果与0000 0001 0000 0000异或结果为0000 0011 0000 0000,非0

    所以 Key_down 为 1

  • Key_up 为 0。
  • key_old 更新为 0x02。
  • 在这个过程中,通过检测 Key_down 的值为 1,我们可以知道按键二被按下了。

    3.为什么能消抖

    这是通过逻辑运算来实现消抖的。

    当有按键动作时,key_rval 会发生变化,key_rval ^ key_old 可以检测出变化的位,即判断是否有按键事件。

    然后,Key_down 是通过 key_rval 与变化位进行与运算得到的,这样只有真正的按键按下事件才会被检测到,因为抖动产生的短暂变化在与运算中会被过滤掉。

    同样,Key_up 是通过对 key_rval 取反后与变化位进行与运算得到的,能够准确检测按键的释放。

    最后,通过将 key_rval 更新为 key_old,为下一次检测做准备。这样不断循环,就能够有效地消除按键抖动的影响。

    它的基本原理是通过对信号进行连续采样,并根据一定的逻辑判断来确定信号的真实状态。在消抖滤波中,通常会设置一个时间窗口,只有在这个窗口内信号保持稳定的状态,才被认为是有效的信号变化,从而避免了因短时间内的抖动或干扰而产生的误判。

    二、

    1.长按与短按(含详细解释)

    消抖算法如上

    #include "headfile.h"
    
    void led_show(uint8_t led,uint8_t mode)
    {
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
    	if(mode)
    HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8<<(led-1),GPIO_PIN_RESET);
        else
    HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8<<(led -1),GPIO_PIN_SET);
    	
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
    
    } 
    uint8_t Key_Scan(void)
    {
    
    	uint8_t Key_val=0;
    	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)
    	{
    	Key_val=2;
    	}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)
    	{
    	Key_val=4;
    	}
    	return Key_val;
    }
    extern uint8_t key_rval;
    uint8_t Key_up,Key_down,key_old;
    uint32_t keytime;//加入了计时的功能,在SysTick_Handler 是系统节拍中断处理函数。keyyime是在stm32g4**——it.c中定义
    //在嵌入式系统中,系统节拍定时器(SysTick)会周期性地产生中断,SysTick_Handler 就是用来处理这些中断的函数。
    //它通常用于进行一些定时相关的操作或任务调度等。
    void Key_Proc(void)	
    {
    	key_rval=Key_Scan();
    	Key_down=key_rval&(key_rval^key_old);
    	Key_up=~key_rval&(key_rval^key_old);
    	key_old=key_rval;//原理看上文
    	
    	if(Key_down==1)//如果检测到按键按下
    	{
    		keytime=0;//给系统计时器清0,开始计时
    	}
    	if(keytime<800)//如果计时时间小于800ms
    	{
    		if(Key_up==1)//检测到松手,则系统判断为单击
    		{
    			led_show(1,1);
    		}
    	}
    	else//如果计时大于800ms
    	{
    		if(key_rval==1)//如果此时按键仍处于按下状态,则进行长按相关的操作。
    		{
    		   led_show(2,1);
    			led_show(1,0);//长按亮LED2,熄灭LED1
    		}
    	}
    }
    
    

    2.单击与双击(含详细解释)

    #include "headfile.h"
    
    void led_show(uint8_t led,uint8_t mode)
    {
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
    	if(mode)
    HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8<<(led-1),GPIO_PIN_RESET);
        else
    HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8<<(led -1),GPIO_PIN_SET);
    	
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
    
    } 
    uint8_t Key_Scan(void)
    {
    
    	uint8_t Key_val=0;
    	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)
    	{
    	Key_val=2;
    	}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)
    	{
    	Key_val=4;
    	}
    	return Key_val;
    }
    extern uint8_t key_rval;
    uint8_t Key_up,Key_down,key_old;
    uint32_t keytime;
    uint8_t key_temp,key_flag;//temp保存第一次的按键值,flag用来判断是否可以计时
    void Key_Proc(void)	
    {
    	key_rval=Key_Scan();
    	Key_down=key_rval&(key_rval^key_old);
    	Key_up=~key_rval&(key_rval^key_old);
    	key_old=key_rval;
        if(Key_up)//如果检测到松手
    	{   
    		key_temp=Key_up;//将当前释放值保存到 key_temp,并根据标志变量 key_flag 的状态进行相应操作。
    		if(key_flag==0)//变量定义时,没有显式地给 key_flag 赋值,编译器会自动将其初始化为默认值 0。
    		{
    			keytime=0;
    			key_flag=1;
    		}
    		else
    		key_flag=0;//目的是将key_flag双击结束后的下一次可以开始计时
    	}
    	if(key_flag==1)//key_flag=1
    	{
    		if(keytime<300)//如果300ms内
    		{
    		if(Key_down==1&&key_temp==1)//检测到按键按下最开始松手一致
    		{
    		led_show(2,1);//则执行
    			led_show(1,0);
    		}	
    //		if(Key_down==2&&key_temp==2)//多按键
    //		{
    //		led_show(3,1);//
    //			led_show(4,0);
    //		}	
    		}
    		else
    			{
    				if(key_temp==1)//如果只检测到开始保存键值,则视为单击
    			{
    			led_show(1,1);//则执行
    				led_show(2,0);
    			}
    			key_flag=0;//并将key_flag置0为下一次做准备
    //			if(key_temp==2)//
    //			led_show(4,1);//则执行
    //				led_show(3,0);
    //			}
    //			key_flag=0;//并将key_flag置0为下一次做准备
    		}
    	}
    }
    
    

    作者:金科W

    物联沃分享整理
    物联沃-IOTWORD物联网 » 蓝桥杯专题:STM32G431RBT6按键处理与算法精讲(三行代码实现消抖、短按长按与单击双击功能)下篇解析(二)

    发表回复