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

news2024/9/20 9:06:34

目录

独立按键简介

按键抖动

模块接线

延时消抖

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.coloradmin.cn/o/2115241.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring框架-----ioc

基本概念 Spring 是一个轻量级的,IOC和AOP的一站式Java 开发框架,是为了简化企业级应用开发而生的 轻量级:框架核心模块体积小 IOC:Inversion of Control(控制反转)把创建对象的控制权反转给Spring框架管理 以前我们程序中需要…

Linux下构建Docker镜像

Docker在Linux构建镜像 Docker是一种轻量级的容器化技术,可以让开发者将应用程序及其所有依赖项打包到一个独立的容器中,从而实现跨平台和快速部署,在Linux系统上,我们可以使用D0cker来构建自己的镜像,并且可以通过简…

RocketMQ学习(三)

文章目录 1. 高级功能1.1 消息存储1.1.1 存储介质关系型数据库DB文件系统 1.1.2 性能对比1.1.3 消息的存储和发送1)消息存储2)消息发送 1.1.4 消息存储结构1.1.5 刷盘机制1)同步刷盘2)异步刷盘3)配置 1.2 高可用性机制…

软件设计之JavaWeb(1)

软件设计之JavaWeb(1) 此篇应在MySQL之后进行学习: 路线图推荐: 【Java学习路线-极速版】【Java架构师技术图谱】 尚硅谷全新JavaWeb教程,企业主流javaweb技术栈 资料可以去尚硅谷官网免费领取 此章节最好学完JDBC观看 学习内容: XML概述T…

excel翻译软件有哪些?如何高效提翻译?

你是否曾在面对满屏的英文Excel表格时感到头疼?项目报告、数据分析、财务报表... 当这些重要的信息被语言壁垒阻挡时,效率和理解度都会大打折扣。别担心,只需3分钟,我将带你轻松解锁excel翻译成中文的秘籍。 无论是职场新人还是…

解决浏览器自动将http网址转https

删除浏览器自动使用https的方式 在浏览器地址栏输入:chrome://net-internals/#hsts PS:如果是edge浏览器可输入:edge://net-internals/#hsts 在Delete domain security policies搜索框下,输入要删除的域名,然后点击delete 解决方法&#…

VMware中共享文件夹没了怎么办?

1.进入root su root 需要提前设置密码 sudo passwd root 2.创建一个hgfs文件夹,share就在这里面 sudo mkdir /mnt/hgfs/ 3.输入下面的命令 sudo mount -t fuse.vmhgfs-fuse .host:/ /mnt/hgfs -o allow_other 4.然后就能找到share文件夹了,注意每…

CCF推荐B类会议和期刊总结(计算机网络领域)

CCF推荐B类会议和期刊总结(计算机网络领域) 在计算机网络领域,中国计算机学会(CCF)推荐的B类会议和期刊代表了该领域的较高水平。以下是对所有B类会议和期刊的总结,包括全称、出版社、dblp文献网址以及所属…

串口通信协议(UART)

简介 uart通讯协议,是一种成本低、容易使用、通信线路简单,可实现两个设备的互相通信的协议;是一种全双工,设备点对点通信的协议。下面从硬件电路、电平标准和串口参数等方面来了解uart通信协议。 硬件电路 硬件电路非常简单&am…

如何限制与管控员工上网行为?五个管控方法让员工效率倍增!

在现代企业中,互联网是工作中不可或缺的工具,但与此同时,员工在工作时间浏览与工作无关的网站、进行网络娱乐等行为,也成为了影响企业生产力和效率的主要因素之一。如何有效限制和管控员工的上网行为,从而提升工作效率…

利士策分享,逆境破局关键:精准策略

利士策分享,逆境破局关键:精准策略 在人生的征途上,逆境如同试炼场,考验着我们的智慧与勇气。 为了在这片试炼场上稳健前行,我们需要一套具体而精准的应对策略。 以下,是结合实践经验与智慧总结的应对策略…

机器意识的可能性:从计算功能主义到生物自然主义的思考

引言 近年来,人工智能(AI)的飞速发展引发了关于机器能否具备意识的激烈讨论。这个话题不仅涉及技术层面的挑战,还触及了哲学和神经科学的基本问题。Anil Seth教授作为意识研究领域的权威,他在近期发表的论文中提出了一…

ESP32-驱动ST7789显示

前言 一、TFT_eSPI库的安装 二、TFT_eSPI 的设置 1.文件的路径 2.User_Setup.h的设置 4.连线 三.显示 总结 前言 环境: 芯片:ESP32 软件:Arduino 一、TFT_eSPI库的安装 1.安装TFT_eSPI库,安装时注意安装内容不要错了 如果提示还有…

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

数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):2757 标注数量(xml文件个数):2757 标注数量(txt文件个数):2757 标注…

论文阅读:AutoDIR Automatic All-in-One Image Restoration with Latent Diffusion

论文阅读:AutoDIR: Automatic All-in-One Image Restoration with Latent Diffusion 这是 ECCV 2024 的一篇文章,利用扩散模型实现图像恢复的任务。 Abstract 这篇文章提出了一个创新的 all-in-one 的图像恢复框架,融合了隐扩散技术&#x…

Rust : 从事量化的生态现状与前景

Rust适不适合做量化工作? 一般地认为,目前大部分场景策略开发最佳是Python;策略交易和部署是C。但还是有人会问,Rust呢? 这个问题不太靠谱! 适不适合做一件事情,本身就是一件主观的事。即使是…

通信工程学习:什么是AN接入网络

AN接入网络 AN接入网络,全称Access Network,是电信部门业务节点与用户终端设备之间的实施系统。它可以部分或全部代替传统的用户本地线路网,并可包括复用、交叉连接和传输功能。以下是关于AN接入网络的详细解释: 一、AN接入网络的…

【数据结构入门】排序算法之交换排序与归并排序

前言 在前一篇博客,我们学习了排序算法中的插入排序和选择排序,接下来我们将继续探索交换排序与归并排序,这两个排序都是重头戏,让我们接着往下看。 一、交换排序 1.1 冒泡排序 冒泡排序是一种简单的排序算法。 1.1.1 基本思想…

Recyclerview Item 高度不同 统一最大高度

参考这篇: android - How to set recycler height to highest item in recyclerView? - Stack Overflowhttps://stackoverflow.com/a/67403957/13583023