消抖完整代码见http://t.csdnimg.cn/cWWFq
目录
一、
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_up
、Key_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
为 1Key_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为下一次做准备
}
}
}