关于蓝桥杯单片机 官方例程 矩阵按键 消抖原理 ‘s 解释

关于蓝桥杯单片机官方例程矩阵按键消抖原理解释

项目场景:

芯片:IAP15F2K60S
蓝桥杯原书41页官方例程


问题描述

最开始我以为这款开发板有硬件消抖,对于下面这部分代码一直当作固定格式写,最近回看,发现其中另有“玄机”。

......
  ucKey_Val = Key_Read();
  ucKey_Down = ucKey_Val & (ucKey_Old ^ ucKey_Val);
  ucKey_Old = ucKey_Val;
......

原因分析:

这里默认大家看过并且会了 = = 之前的按键扫描例程了
众所周知,“ ^ ” 符号在C语言中是按位取 “ 异或 ” 的意思,并且,“ & ” 符号是按位取 “ 与 ”的意思。

这段代码的含义是获取扫描后的案件值然后进行消抖处理,我所截取的这一段代码是只保留了单边沿的。

详解:

首先,对于后面的异或,其作用到了ucKey_Old和ucKey_Val,其中ucKey_Old是上一次扫描按键获取的数值,包括获取到0,而ucKey_Val是这一次扫描按键获取的数值。如果二者不同,则会产生 “ 非零 ” 的结果,将代码改成如下。

......
  ucKey_Val = Key_Read();
  ucKey_Down = (ucKey_Old ^ ucKey_Val);
  ucKey_Old = ucKey_Val;
  
  if(ucKey_Down)
  {
       ......
  }
......

只要本次接收与上一次接收的按键值不同,ucKey_Down就会存储下“非零”的数值。回想一下,我们按键其实是有两个沿的,一次下降沿,一次上升沿。
更具体地针对官方例程来说,如果我按下4按键,那么他检测到的数值会出现两段变化,一次是ucKey_Val 从0到4的变化,另一次是其从4到0 的变化。
这样就会导致我的ucKey_Down在这两次变化过后都会有一次 “ 非零 ” 数值的出现,所以反应在我的输出上,就会将后面紧随的 if 代码段执行两次,这说明我们的 " ^ " 取异或只实现了部分的消抖功能,还需要保留单边沿,而非双边。

没错,我们的 “ & ” 取与符号就是实现的这个功能。我们分段讨论下0–4–0这个连续变化。

首先是 0–4,这段变化中,
我的 ucKey_Old = 0x00, 我的 ucKey_Val = 0x04;
因为这是从0到4的不同数值的变化,所以后面的取异或符号运算结束后一定是一个 “ 非零 ” 的量,并且结果一定是与 ucKey_Val 的数值相等!又加之与一个非零的 ucKey_Val 取与,那不就是 ucKey_Val 本身求与嘛!!!这就使得 ucKey_Down 最后的结果为 “ 非零 ” 的 ucKey_Val本身!!!这样解释了为什么后面会有

    switch(ucKey_Down)
    {
       ......
    }

的出现,原来我们仅凭借ucKey_Down的数值就可以判断我按下的是什么键。

-------------分隔符--------------

接下来是4–0这一段的变化,
我的 ucKey_Old = 0x40, 我的 ucKey_Val = 0x00;
之前说到如果是不同数值之间的变化必然会导致异或的结果为 “ 非零 ” 数值,所以这一段就很好明白了,我加上取与符号后,实现了ucKey_Val & “ 非零 ”,其中ucKey_Val为0,会将我的ucKey_Down,结果变成 0,这就实现了我取单边沿的效果。

总结
异或 “ ^ ” 是用来消抖的,只有扫描到不同的数值之后我才会发生变化。
取余 “ & ”是用来保留单边沿的,因为单单使用"^" 时,笨蛋机器不能判断是哪两个数值之间的变化,究竟是0–4还是4–0呢?当我加上 “ & ” 之后就可以消除一个沿。
PS:文中我们是以下降沿为例说明的,其实保留上升沿的话取个 “ ~ ” 就可以了,具体为啥,大家想想吧。

  • 11
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
按键消抖是指在按下或松开按键时,由于机械性能、电气性能等原因,会产生一些抖动信号。这些抖动信号可能会被误认为是多次按下或松开按键,导致系统出现错误。因此,需要对按键信号进行消抖处理,以确保系统正常运行。 一般的按键消抖算法是基于滤波的,常见的滤波算法有移动平均滤波和中值滤波。其中,移动平均滤波是指对一段时间内的信号取平均值,以减小信号的波动;中值滤波是指对一段时间内的信号取中值,以排除信号中的异常值。 以下是一个基于移动平均滤波的按键消抖例程: ``` #define KEY_PIN 2 // 定义按键所在引脚 #define KEY_THRESHOLD 3 // 定义按键触发的阈值 #define SAMPLE_NUM 10 // 定义采样次数 #define DELAY_TIME 5 // 定义采样间隔时间 void setup() { pinMode(KEY_PIN, INPUT_PULLUP); // 将按键所在引脚设置为输入模式,并开启上拉电阻 } void loop() { static int samples[SAMPLE_NUM] = {0}; // 定义采样数组 static int sampleIndex = 0; // 定义采样索引 int sum = 0; // 定义采样和 int average = 0; // 定义采样平均值 samples[sampleIndex] = digitalRead(KEY_PIN); // 读取按键状态,并存入采样数组 sampleIndex = (sampleIndex + 1) % SAMPLE_NUM; // 更新采样索引 for (int i = 0; i < SAMPLE_NUM; i++) { sum += samples[i]; // 计算采样和 } average = sum / SAMPLE_NUM; // 计算采样平均值 if (average < KEY_THRESHOLD) { // 判断按键是否被触发 // 执行按键触发后的操作 } delay(DELAY_TIME); // 等待采样间隔时间 } ``` 在上述例程中,首先定义了按键所在引脚、按键触发的阈值、采样次数和采样间隔时间等参数。然后在`setup()`函数中将按键所在引脚设置为输入模式,并开启上拉电阻。在`loop()`函数中,定义了一个静态的采样数组和采样索引,用于存储按键状态的采样值。读取按键状态后,将其存入采样数组中,并更新采样索引。然后计算采样和和采样平均值,判断按键是否被触发,并执行相应的操作。最后延时一定时间,等待下一次采样。这样就实现了对按键信号的消抖处理。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值