3、按键扫描检测处理

news2024/9/29 9:34:41

说明:本文处理按键的短按、长按检测执行,非矩阵按键
硬件可以类似如下连接即可,无需放置上下拉电阻;
在这里插入图片描述
按键动作分长按、短按(弹起时执行)两种
按下不放执行长按,但松开按键时不予执行短按函数
多个按键可以同时操作
按键引脚会自动配置软件上下拉

1、驱动文件

Key_Driver.h

#ifndef _Key_Driver_H_
#define _Key_Driver_H_
#include "Common_Driver.h"

extern void Key_Init(void);
extern void Key_Scan(void);

#endif

Key_Driver.c

/**********************************************************************
*file:按键扫描处理文件
*author:残梦
*date:2023.2.15
*Version:V1.0
*note:  按键动作分长按、短按(弹起时执行)两种
        按下不放执行长按,但松开按键时不予执行短按函数
        多个按键可以同时操作
        按键引脚会自动配置软件上下拉
使用方法:
    1、修改宏按键个数值:KEY_NUM
    2、修改宏按键引脚对应时钟:KEYx_GPIO_CLK_ENABLE()
    3、修改按键编码枚举:KeyCode_EnumDef
    4、修改按键端口及引脚号变量数组:Key_gpioPort[]、Key_gpioPin[];端口和引脚需相对应,第一个为按键0
    5、修改按键引脚初始电平状态值:KeyInitStatus[]
    6、主函数调用按键初始化函数Key_Init()
    7、1ms调用1次扫描函数Key_Scan()
    8、修改按键长按、短按执行函数:Key_LongPressAction()、Key_Action()
**********************************************************************/
#include "Key_Driver.h"

#define LongPressThr 2000//长按LongPressThr毫秒标为长按按键动作
#define LongPressInc 500//长按LongPressInc毫秒执行一次长按动作函数

#define KEY_NUM 4//按键个数
#define KEY0_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOE_CLK_ENABLE()
#define KEY1_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOE_CLK_ENABLE()
#define KEY2_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOE_CLK_ENABLE()
#define KEY3_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOA_CLK_ENABLE()

typedef enum
{
    eKeyCode0 = 0,
    eKeyCode1 = 1,
    eKeyCode2 = 2,
    eKeyCode3 = 3
}KeyCode_EnumDef;//按键编码

static GPIO_TypeDef*    Key_gpioPort[KEY_NUM]   = {GPIOE,GPIOE,GPIOE,GPIOA};//按键引脚对应端口key0-key
static uint16_t         Key_gpioPin[KEY_NUM]    = {GPIO_PIN_4,GPIO_PIN_3,GPIO_PIN_2,GPIO_PIN_0};//按键引脚对应引脚号key0-key
static uint8_t          KeyInitStatus[KEY_NUM]  = {1,1,1,0};//按键引脚初始电平状态:0--低电平,1--高电平
static uint8_t KeyReset = 1;//无需用户更改

static void Key_LongPressAction(KeyCode_EnumDef key,uint32_t DownTime);
static void Key_Action(KeyCode_EnumDef key);

/****************************************************
@function:按键初始化(无需用户修改)
@param:void
@return:void
@date:2023.2.15
@note:
****************************************************/
void Key_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    KEY0_GPIO_CLK_ENABLE();
    KEY1_GPIO_CLK_ENABLE();
    KEY2_GPIO_CLK_ENABLE();
    KEY3_GPIO_CLK_ENABLE();

    //外设引脚初始化
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    for(uint8_t i = 0;i < KEY_NUM;i++)
    {
        GPIO_InitStruct.Pull = KeyInitStatus[i]?GPIO_PULLUP:GPIO_PULLDOWN;
        GPIO_InitStruct.Pin = Key_gpioPin[i];
        HAL_GPIO_Init(Key_gpioPort[i], &GPIO_InitStruct);
    }
    KeyReset = 1;
}

/******************************
@function:按键扫描函数(无需用户修改)
@param:void
@return:void
@date:2023.2.15
@remark:需在定时中断中调用,推荐调用间隔1ms
        长按和短按分为两种
        按下不放执行长按,但是弹起时不予执行短按函数
******************************/
void Key_Scan(void)
{
    static uint8_t keybuf[KEY_NUM] = {0xFF};//按键扫描缓冲区赋值
    static uint8_t KeySta[KEY_NUM] = {1};//全部矩阵按键的当前状态
    static uint8_t backup[KEY_NUM] = {1};//按键值备份,保存前一次的值
    static uint32_t KeyDownTime[KEY_NUM] = {0};//长按累计时间
    static uint32_t KeyDownThr[KEY_NUM] = {LongPressThr};
    static uint8_t KeyDownFlag[KEY_NUM] = {0};
    uint8_t i = 0,pinSta = 0;

    if(KeyReset)
    {
        KeyReset = 0;
        for(i = 0;i < KEY_NUM;i++)
        {
            keybuf[i] = 0xFF;
            KeySta[i] = 1;
            backup[i] = 1;
            KeyDownTime[i] = 0;
            KeyDownThr[i] = LongPressThr;
            KeyDownFlag[i] = 0;
        }
    }

    //将按键值移入缓冲区
    for(i=0;i < KEY_NUM;i++)
    {
        pinSta = (uint8_t)HAL_GPIO_ReadPin(Key_gpioPort[i],Key_gpioPin[i]);
        if(!KeyInitStatus[i]){pinSta = pinSta?0:1;}
        keybuf[i] = (keybuf[i] << 1) | pinSta;
    }

    //消抖后更新按键状态
    for (i=0; i < KEY_NUM; i++)
    {
        if((keybuf[i] & 0x0F) == 0x00){KeySta[i] = 0;}//连续4次扫描值为0,即4ms内都是按下状态时,可认为按键已稳定的按下
        else if((keybuf[i] & 0x0F) == 0x0F){KeySta[i] = 1;}//连续4次扫描值为1,即4*4ms内都是弹起状态时,可认为按键已稳定的弹起
    }

    for(i=0; i < KEY_NUM; i++)//循环检测按键
    {
        if(KeySta[i] == 0)//长按按键
        {
            KeyDownTime[i] += 1;
            if(KeyDownTime[i] >= KeyDownThr[i])
            {
                KeyDownFlag[i] = 1;		//长按标志
                KeyDownThr[i] += LongPressInc; //时间阈值增加LongPressInc毫秒,以准备下次执行
                Key_LongPressAction((KeyCode_EnumDef)i,KeyDownTime[i]); //调用长按执行函数
            }
        }

		//按键弹起 
		if(backup[i] != KeySta[i])//检测按键动作
        {
            if(backup[i] != 1)//按键按下时执行动作
            {
                if(KeyDownFlag[i] == 0)//长按弹起避免重复执行	
                {
                    Key_Action((KeyCode_EnumDef )i); //调用按键动作函数
                }
            }
            backup[i] = KeySta[i];     //刷新前一次的备份值
        }
					 
        if(KeySta[i]) 
        {
            KeyDownFlag[i] = 0;	//长按标志
            KeyDownTime[i] = 0;	//累加时间清零
            KeyDownThr[i] = LongPressThr;//恢复阈值
        }					
    }
}

/******************************
@function:长按按键动作函数
@param:key-按键键码
@return:void
@date:2023.2.15
@remark:void
******************************/
static void Key_LongPressAction(KeyCode_EnumDef key,uint32_t DownTime)
{
    printf("Key%d:%d\r\n",key,DownTime);
    switch(key)
    {
        case eKeyCode0:
        {
            
        }break;
        case eKeyCode1:
        {

        }break;
        case eKeyCode2:
        {

        }break;
        case eKeyCode3:
        {

        }break;
    }
}

/******************************
@function:短按按键动作函数
@param:key-按键键码
@return:void
@date:2023.2.16
@remark:void
******************************/
static void Key_Action(KeyCode_EnumDef key)
{
    printf("Action Key%d\r\n",key);
    switch(key)
    {
        case eKeyCode0:
        {

        }break;
        case eKeyCode1:
        {

        }break;
        case eKeyCode2:
        {

        }break;
        case eKeyCode3:
        {

        }break;
    }

}

2、使用方法

1、修改宏按键个数值:KEY_NUM
2、修改宏按键引脚对应时钟:KEYx_GPIO_CLK_ENABLE()
3、修改按键编码枚举:KeyCode_EnumDef
4、修改按键端口及引脚号变量数组:Key_gpioPort[]、Key_gpioPin[];端口和引脚需相对应,第一个为按键0
5、修改按键引脚初始电平状态值:KeyInitStatus[]
6、主函数调用按键初始化函数Key_Init()
7、1ms调用1次扫描函数Key_Scan()
8、修改按键长按、短按执行函数:Key_LongPressAction()、Key_Action()

测试效果:
在这里插入图片描述
注意:扫描时间1ms如觉得太长可以更久调用1次,程序中会检测4次引脚的状态值,如都为1或0则算为稳定状态
按键文件下载:
链接:https://pan.baidu.com/s/1sO_kFuJb6wtLiMlFbneFsA
提取码:sun5

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/349161.html

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

相关文章

内网渗透(三十四)之横向移动篇-IPC配合计划任务横向移动

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…

Altium Designer输出生产文件Gerber、IPC、NC Drill、坐标文件--AD

AD软件版本&#xff1a;22.2.1 gerber文件输出共有两部分&#xff1a; 1、Gerber Files:铜皮 和 外形分别导出 2、Nc Drill Files 分3次导出 一、Gerber Files 导出2次 设定原点 ** Edit->Origin->Set** 一般板边左下角为原点&#xff0c;可以根据自己板子形状确定 导…

使用MAT工具分析OOM问题

1、添加jvm参数 保存堆内存快照 -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath存放路径 2、下载MAT工具 下载地址&#xff1a; https://www.eclipse.org/mat/downloads.php 3、启动如果遇到 Version 1.8.0 102 of the JVM is not suitable for this product, Version: 11…

2023LaCTFWriteup

文章目录2023LaCTFEBEa hackers notes2023LaCTF 前言&#xff1a; 累了&#xff0c;没有话&#xff0c;下次一定 EBE I was trying to send a flag to my friend over UDP, one character at a time, but it got corrupted! I think someone else was messing around with me…

【C++】类与对象(三)

前言 本章我们接替前一章继续深入理解类的默认成员函数&#xff0c;赋值重载&#xff0c;取地址重载&#xff0c;及const取地址操作符重载 但是在讲剩下的三个默认成员函数之前&#xff0c;我们要先来了解运算符重载&#xff0c;因为赋值重载&#xff0c;取地址重载&#xff0c…

Compose-Navigation简单案例上手

Navigation 快速上手 下面案例简要展示使用 Compose 版本的 Navigation 库来实现两个页面之间的跳转 这是完整的结构&#xff08;忽略掉红线划过的那个包&#xff09; 安装适用于 kotlin 的 navigation 依赖 dependencies {implementation("androidx.navigation:navigati…

兼职任务平台收集(一)分享给有需要的朋友们

互联网时代&#xff0c;给人们带来了很大的便利。信息交流、生活缴费、足不出户购物、便捷出行、线上医疗、线上教育等等很多。可以说&#xff0c;网络的时代会一直存在着。很多人也在互联网上赚到了第一桶金&#xff0c;这跟他们的努力和付出是息息相关的。所谓一份耕耘&#…

使用kubeadm部署k8s1.24.0版本,遇到的坑总结

使用kubeadm部署k8s1.24.0版本&#xff0c;遇到的坑总结环境安装遇到的问题环境 操作系统&#xff1a;centos7 内核&#xff1a;5.4.231-1.el7.elrepo.x86_64 kubeadm&#xff1a;1.24.0 kubelet&#xff1a;1.24.0 kubectl&#xff1a;1.24.0 cri&#xff1a;docker cni&…

nginx rewrite 规则

生效位置 rewrite 可以写在 server 、 location、 if 配置段内 格式 rewrite regex replacement [flag] regex是匹配的正则表达式。 只会处理$request_uri部分&#xff0c;不会匹配$host replacement 是跳转的URI。 可以以http:// 或者https:// 开头&#xff0c; 也…

自适应多因素认证:构建不可破解的企业安全防线|身份云研究院

打开本文意味着你理解信息安全的重要性&#xff0c;并且希望获取行业最佳实践来保护你所在组织的信息安全。本文将带你了解多因素认证&#xff08;MFA&#xff1a;Multi-Factor-Authentication&#xff09;对于企业信息安全的重要性以及实施方法。 多因素认证&#xff08;MFA&…

尚医通(十五)医院排班管理

目录一、医院排班管理需求1、页面效果2、接口分析二、科室列表&#xff08;接口&#xff09;1、添加service接口和实现2、添加DepartmentController方法三、科室列表&#xff08;前端&#xff09;1、添加隐藏路由2、封装api方法3、添加/views/hosp/schedule.vue组件四、排班日期…

torch相比于numpy都干了什么——torch入门

Torch已深度学习框架被熟知&#xff0c;但它首先是作为Numpy的存在。我们首先比较一下Torch和Numpy有什么不同&#xff0c;为什么可以实现深度学习。 从数据结构看起。Numpy的强大之处就在于array的数据结构&#xff0c;它是多维数组&#xff0c;要求所有元素是相同类型的&…

人与人之间赚钱的差距在哪里呢?体现在这几个因素中

同样生而为人&#xff0c;同样接受九年制义务教育的熏陶&#xff0c;但最终赚钱能力却千差万别&#xff0c;因此也就形成了我们所谓的圈层&#xff0c;阶层&#xff0c;穷人和富人。 一个人的赚钱能力跟什么有关&#xff1f;资源技能、学历、认知&#xff0c;这些都会决定一个人…

搞清楚动态库和静态库的区别

什么是库文件 一般来说&#xff0c;一个程序&#xff0c;通常都会包含目标文件和若干个库文件。经过汇编得到的目标文件再经过和库文件的链接&#xff0c;就能构成可执行文件。库文件像是一个代码仓库或代码组件的集合&#xff0c;为目标文件提供可直接使用的变量、函数、类等…

visual studio 改变界面语言

在使用visual studio 2019 时&#xff0c;开始是英文界面&#xff0c;后面变成了中文界面。但是看视频教学时有的是英文界面&#xff0c;我就想回到英文界面&#xff0c;所以有切换界面语言的需要。其实操作很简单&#xff1a;工具-> 选项 打开界面在界面里选择环境&#xf…

axios中的resolvePromise为什么影响promise状态

axios的取消请求意思很简单&#xff0c;就是在发送请求后不久停止发送请求 本文探讨的是v0.22.0之前的CancelToken API&#xff0c;因为在阅读源码交流的时候发现很多朋友不理解为什么CancelToken中的resolvePromise会影响到实例对象身上的promise状态 即下图所示代码&#xf…

为乡村振兴塑形铸魂,VR全景助力美丽乡村建设

民族要复兴&#xff0c;乡村必振兴&#xff0c;文旅部一直推动乡村文化产业和乡村旅游融合发展&#xff0c;大力推进乡村文化数字化&#xff0c;为乡村文化振兴注入新动能。为了更好的给乡村振兴塑形铸魂&#xff0c;VR智慧乡村助力数字化乡村建设发展&#xff0c;利用VR全景展…

CMD SET % 字符串截取

:: 使用 CSDN Cobol 格式化风格。:: *. 百分号 (%) :: (1) % 作为变量引导符&#xff0c;有几种用途&#xff1a;在命令行窗口中&#xff0c;用 1 个 % 引号变量; 在批处理文件中&#xff0c;用 2 个 % 引号变量。[1] 两个 % 夹着一个变量名称&#xff0c;表示取&#xff08;扩…

HashMap如何避免内存泄露问题

HashMap对于Java开发人员来说&#xff0c;应该是一种非常非常熟悉的数据结构了&#xff0c;应用场景相当广泛。 本文重点不在于介绍如何使用HashMap&#xff0c;而是关注在使用HashMap过程中&#xff0c;可能会导致内存泄露的情况&#xff0c;下面将以示例的形式展开具体介绍。…

【Java】properties 和 yml 的区别

文章目录properties和yml的区别① 定义和定位不同② 语法不同③ yml更好的配置多种数据类型④ yml可以跨语言⑤ 总结properties和yml的区别 这几天刚好看到Spring Boot当中有两种配置文件的方式&#xff0c;但是这两种配置方式有什么区别呢&#xff1f; properties和yml都是S…