独立按键单击检测(延时消抖+定时器扫描)

2024-09-08 09:12

本文主要是介绍独立按键单击检测(延时消抖+定时器扫描),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

独立按键简介

按键抖动

模块接线

延时消抖

Key.h

Key.c

定时器扫描按键代码

Key.h

Key.c

main.c

思考 

MultiButton按键驱动


独立按键简介

​ 轻触按键相当于一种电子开关,按下时开关接通,松开时开关断开,实现原理是通过轻触按键内部的金属弹片受力弹动来实现接通与断开。  ​

按键抖动

由于按键内部使用的是机械式弹簧片来进行通断的,所以在按下和松手的瞬间会伴随有一连串的抖动

当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上就稳定的接通,在断开时也不会一下子彻底断开,而是在闭合和断开的瞬间伴随了一连串的抖动。由于单片机检测 IO口速度非常快,超过弹片抖动的频率,所以在检测按键状态时。从而对按键的判断产生一些误操作(比如按一下会产生按多下的效果),因此必须要消除抖动才能正常使用按键。要消除按键抖动的影响。抖动时间的长短由按键的机械特性决定的,一般为5ms 到10ms。

硬件消抖
本节不涉及

软件消抖
延时消抖
定时器消抖

模块接线

按键STM32F103C8T6
KEY1PB11
KEY2PB1

另一端接入GND

延时消抖

按键松手后判定

Key.h

#ifndef       _KEY_H_
#define		  _KEY_H_void  Key_Init(void);
uint8_t Key_GetNum(void);#endif

Key.c

#include "stm32f10x.h"                  // Device header
#include "Key.h"
#include "Delay.h"#define KEY1   GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11)
#define KEY2   GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1)void  Key_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//输出速度,输入无用GPIO_Init(GPIOB, &GPIO_InitStructure);
}uint8_t Key_GetNum(void)
{uint8_t KeyNum = 0;if(KEY1 == RESET){Delay_ms(20);while(KEY1 == RESET)Delay_ms(20);KeyNum = 1;}if(KEY2 == RESET){Delay_ms(20);while(KEY2 == RESET)Delay_ms(20);KeyNum = 2;}return KeyNum;
}

单按松手前按键触发+连按按键扫描函数来源于正点原子:                                                                                                                                             

//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//0,没有任何按键按下
//1,KEY0按下
//2,KEY1按下
//3,KEY3按下 WK_UP
//注意此函数有响应优先级,KEY0>KEY1>KEY_UP!!
u8 KEY_Scan(u8 mode)
{	 static u8 key_up=1;//按键按松开标志if(mode)key_up=1;  //支持连按		  if(key_up&&(KEY0==0||KEY1==0||WK_UP==1)){delay_ms(10);//去抖动 key_up=0;if(KEY0==0)return KEY0_PRES;else if(KEY1==0)return KEY1_PRES;else if(WK_UP==1)return WKUP_PRES;}else if(KEY0==1&&KEY1==1&&WK_UP==0)key_up=1; 	    return 0;// 无按键按下
}

连按: key_up每次进入函数都为1,相当于把有关key_up的判断与赋值删除,只要有按键按下,消抖后再次判断是否按下,不等松手就返回对应按键值。

//拆分代码
u8 KEY_Scan(void)
{	 if(KEY0==0||KEY1==0||WK_UP==1){delay_ms(10);//去抖动 key_up=0;if(KEY0==0)return KEY0_PRES;else if(KEY1==0)return KEY1_PRES;else if(WK_UP==1)return WKUP_PRES;}; 	    return 0;// 无按键按下
}

只单击不连按: key_up为上一次检测按键值,上一次没有按,这一次按了才会返回对应按键值,     

u8 KEY_Scan(void)
{	 static u8 key_up=1;//按键按松开标志		  if(key_up&&(KEY0==0||KEY1==0||WK_UP==1)){delay_ms(10);//去抖动 key_up=0;if(KEY0==0)return KEY0_PRES;else if(KEY1==0)return KEY1_PRES;else if(WK_UP==1)return WKUP_PRES;}else if(KEY0==1&&KEY1==1&&WK_UP==0)key_up=1; 	    return 0;// 无按键按下
}

作为这种很简单的演示程序,我们可 以这样来写,但是实际做项目开发的时候,程序量往往很大,各种状态值也很多,while(1) 这个主循环要不停的扫描各种状态值是否有发生变化,及时的进行任务调度,如果程序中间 加了这种 delay 延时操作后,很可能某一事件发生了,但是我们程序还在进行 delay 延时操作 中,当这个事件发生完了,程序还在 delay 操作中,当我们 delay 完事再去检查的时候,已经晚了,已经检测不到那个事件了。为了避免这种情况的发生,我们要尽量缩短 while(1)循环 一次所用的时间,而需要进行长时间延时的操作,必须想其它的办法来处理。 那么消抖操作所需要的延时该怎么处理呢?

定时器扫描按键代码

为了检测按键按下和松开,必须记录按键当前状态和上一次状态。如果当前状态为高电平,上一次状态为低电平,则检测到按键按下后松手;如果当前状态为低电平,上一次为高电平,则检测到按键按下。由于短按按下一次只需返回一次按键值,因此只需检测按键松开动作,即上一次状态低电平,当前状态高电平。

定时1ms(根据项目和按键抖动情况更改,不要频繁进入中断)进入中断,中断服务函数里扫描按键上一次状态低电平,当前状态高电平就认为按下了。主函数获取按键值后就清除按键按下标志位,等待下一次中断扫描更新按键按下标志位。

单击不支持连按

Key.h

#ifndef       _KEY_H_
#define		  _KEY_H_void  Key_Init(void);uint8_t Key_GetNum(void);
void Key_Scan(void);#endif

Key.c

#include "stm32f10x.h"                  // Device header
#include "Key.h"uint8_t Key_KeyNum;#define KEY1   GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11)
#define KEY2   GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1)
void  Key_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//输出速度,输入无用GPIO_Init(GPIOB, &GPIO_InitStructure);
}uint8_t Key_GetNum(void)
{uint8_t temp;temp = Key_KeyNum;Key_KeyNum = 0;return temp;
}static uint8_t Key_Get(void)
{uint8_t Key_Check = 0;if(KEY1 == RESET){ Key_Check = 1;}if(KEY2 == RESET){ Key_Check = 2;}return  Key_Check;
}void Key_Scan(void)
{static uint8_t LastNum, KeyNum;LastNum = KeyNum;KeyNum = Key_Get();if(LastNum == 1 && KeyNum == 0){Key_KeyNum = 1;}if(LastNum == 2 && KeyNum == 0){Key_KeyNum = 2;}
}

main.c

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "Key.h"
#include "Timer.h"int8_t KeyNum, Num;
int main(void)
{OLED_Init();Key_Init();Timer_Init();OLED_ShowString(1, 1, "Num:");while (1){KeyNum = Key_GetNum();if(KeyNum == 1){Num++;}if(KeyNum == 2){Num--;}OLED_ShowSignedNum(1, 5, Num,3);}
}void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){Key_Scan();TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}

思考 

由于定时器每1ms进入一次中断,对于具有IIC和One-Wire通信等具有严格时序要求的项目来说,容易使通信被干扰。 在通信前关闭中断,在通信完成后打开中断。根据此种方法,可以在通过中断提高MCU工作效率的同时,使中断不干扰具有严格时序的通信(如I2C,One-Wire,SPI等)

MultiButton按键驱动

事件说明
PRESS_DOWN按键按下,每次按下都触发
PRESS_UP按键弹起,每次松开都触发
PRESS_REPEAT重复按下触发,变量repeat计数连击次数
SINGLE_CLICK单击按键事件
DOUBLE_CLICK双击按键事件
LONG_PRESS_START达到长按时间阈值时触发一次
LONG_PRESS_HOLD长按期间一直触发

开源链接

0x1abin/MultiButton: Button driver for embedded system (github.com)

使用教程(包含一个回调函数检测多个按键方法):

MultiButton | 一个小巧简单易用的事件驱动型按键驱动模块 - 知乎 (zhihu.com)

//头文件中可根据需要修改参数
//According to your need to modify the constants.
#define TICKS_INTERVAL    5	//ms
#define DEBOUNCE_TICKS    3	//MAX 7 (0 ~ 7)
#define SHORT_TICKS       (300 /TICKS_INTERVAL)
#define LONG_TICKS        (1000 /TICKS_INTERVAL)

这篇关于独立按键单击检测(延时消抖+定时器扫描)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1147700

相关文章

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

烟火目标检测数据集 7800张 烟火检测 带标注 voc yolo

一个包含7800张带标注图像的数据集,专门用于烟火目标检测,是一个非常有价值的资源,尤其对于那些致力于公共安全、事件管理和烟花表演监控等领域的人士而言。下面是对此数据集的一个详细介绍: 数据集名称:烟火目标检测数据集 数据集规模: 图片数量:7800张类别:主要包含烟火类目标,可能还包括其他相关类别,如烟火发射装置、背景等。格式:图像文件通常为JPEG或PNG格式;标注文件可能为X

poj 2594 二分图最大独立集

题意: 求一张图的最大独立集,这题不同的地方在于,间接相邻的点也可以有一条边,所以用floyd来把间接相邻的边也连起来。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <sta

poj 3692 二分图最大独立集

题意: 幼儿园里,有G个女生和B个男生。 他们中间有女生和女生认识,男生男生认识,也有男生和女生认识的。 现在要选出一些人,使得这里面的人都认识,问最多能选多少人。 解析: 反过来建边,将不认识的男生和女生相连,然后求一个二分图的最大独立集就行了。 下图很直观: 点击打开链接 原图: 现图: 、 代码: #pragma comment(

最大流=最小割=最小点权覆盖集=sum-最大点权独立集

二分图最小点覆盖和最大独立集都可以转化为最大匹配求解。 在这个基础上,把每个点赋予一个非负的权值,这两个问题就转化为:二分图最小点权覆盖和二分图最大点权独立集。   二分图最小点权覆盖     从x或者y集合中选取一些点,使这些点覆盖所有的边,并且选出来的点的权值尽可能小。 建模:     原二分图中的边(u,v)替换为容量为INF的有向边(u,v),设立源点s和汇点t

基于 YOLOv5 的积水检测系统:打造高效智能的智慧城市应用

在城市发展中,积水问题日益严重,特别是在大雨过后,积水往往会影响交通甚至威胁人们的安全。通过现代计算机视觉技术,我们能够智能化地检测和识别积水区域,减少潜在危险。本文将介绍如何使用 YOLOv5 和 PyQt5 搭建一个积水检测系统,结合深度学习和直观的图形界面,为用户提供高效的解决方案。 源码地址: PyQt5+YoloV5 实现积水检测系统 预览: 项目背景

JavaFX应用更新检测功能(在线自动更新方案)

JavaFX开发的桌面应用属于C端,一般来说需要版本检测和自动更新功能,这里记录一下一种版本检测和自动更新的方法。 1. 整体方案 JavaFX.应用版本检测、自动更新主要涉及一下步骤: 读取本地应用版本拉取远程版本并比较两个版本如果需要升级,那么拉取更新历史弹出升级控制窗口用户选择升级时,拉取升级包解压,重启应用用户选择忽略时,本地版本标志为忽略版本用户选择取消时,隐藏升级控制窗口 2.

C++操作符重载实例(独立函数)

C++操作符重载实例,我们把坐标值CVector的加法进行重载,计算c3=c1+c2时,也就是计算x3=x1+x2,y3=y1+y2,今天我们以独立函数的方式重载操作符+(加号),以下是C++代码: c1802.cpp源代码: D:\YcjWork\CppTour>vim c1802.cpp #include <iostream>using namespace std;/*** 以独立函数

线性因子模型 - 独立分量分析(ICA)篇

序言 线性因子模型是数据分析与机器学习中的一类重要模型,它们通过引入潜变量( latent variables \text{latent variables} latent variables)来更好地表征数据。其中,独立分量分析( ICA \text{ICA} ICA)作为线性因子模型的一种,以其独特的视角和广泛的应用领域而备受关注。 ICA \text{ICA} ICA旨在将观察到的复杂信号

[数据集][目标检测]血细胞检测数据集VOC+YOLO格式2757张4类别

数据集格式:Pascal VOC格式+YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):2757 标注数量(xml文件个数):2757 标注数量(txt文件个数):2757 标注类别数:4 标注类别名称:["Platelets","RBC","WBC","sickle cell"] 每个类别标注的框数: