MCU短按长按的功能

news2024/11/23 12:37:22

.外设:一个io口、一个定时器。
为了降低上手的门槛,本文仅使用一个IO口作演示。

实现思路
使用定时器,定时20ms来读取简化的按键状态机。
这里简化了状态机,大家只需明白三个概念。

状态:数量为有限个,记录按键状态。可根据条件切换。对应的代码中,用switch case来匹配不同的状态
条件:就是一些简单的判断。对应的代码,用if来判断按键按下或释放,判断时间计数的长短
事件:在特定的状态和条件下,产生的事件。代码只有三个事件:Null(空闲)SingleClick(短按)LongPress(长按)
思路图解:

在这里插入图片描述

3. 长按、单击 定义
长按事件:按下时间大于1s,释放后响应。(不支持连按,若需要连按,进行简单修改即可)
单击事件:按下时间小于1s, 释放后响应。

4.代码分析
4.1 外设准备

4.2 类型、变量定义
4.2.1按键事件
枚举型名称:KEY_EventList_TypeDef
本文实现短按长按功能,故只有三种事件:无事件、短按事件、长按事件
对应代码,即:空闲、单击、长按

typedef enum _KEY_EventList_TypeDef 
{
    KEY_Event_Null            = 0x00, // 空闲
    KEY_Event_SingleClick  = 0x01, // 单击
    KEY_Event_LongPress    = 0x02 // 长按
}KEY_EventList_TypeDef;

4.2.2 按键电平、动作
因为有的电路按下时,引脚为低电平;有的按下时,引脚却为高电平。这里将电平、动作分开,更方便移植。
枚举型名称:KEY_PinLevel_TypeDef
即:高、低电平。

// 按键引脚的电平
typedef enum

    KKEY_PinLevel_Low = 0,
    KEY_PinLevel_High
}KEY_PinLevel_TypeDef;

枚举型名称:KEY_Action_TypeDef
按键只有按下和没按下俩个动作:
即:按下、释放

// 按键动作,
typedef enum

    KEY_Action_Press = 0,
    KEY_Action_Release
}KEY_Action_TypeDef;

4.2.3 按键状态
枚举型名称:KEY_StatusList_TypeDef
思路图解的分析,分为如下几个状态。
即:空闲、消抖、确认按下、确认长按

// 按键状态
typedef enum _KEY_StatusList_TypeDef 
{
    KEY_Status_Idle     = 0, // 空闲
    KEY_Status_Debounce ,    // 消抖
    KEY_Status_ConfirmPress    ,    // 确认按下    
    KEY_Status_ConfirmPressLong,    // 确认长按
}KEY_StatusList_TypeDef;

4.2.4 按键配置结构体
按键配置信息的结构体名称:KEY_Configure_TypeDef
打包好按键的基本属性。

typedef struct _KEY_Configure_TypeDef 
{
    uint16_t                        KEY_Count;                 // 按键长按时长计数
    KEY_Action_TypeDef             KEY_Action;                // 按键动作,按下或释放
    KEY_StatusList_TypeDef         KEY_Status;                // 按键状态
    KEY_EventList_TypeDef          KEY_Event;                 // 按键事件
    KEY_PinLevel_TypeDef           (*KEY_ReadPin_Fcn)(void);  // 读引脚电平函数
}KEY_Configure_TypeDef;


成员解释:

KEY_Count:计数,记一个数为20ms。
KEY_Action:按键动作,按下或者释放。
KEY_Status:记录按键的状态值。
KEY_Event:记录按键触发的事件。
KEY_ReadPin_Fcn:读取按键电平值的函数指针。方便移植。
4.3 变量、函数、宏定义
4.3.1宏定义
KEY_LONG_PRESS_TIME :
短按、长按的时长分界线。大于–>长按,小于–>短按。
KEY_PRESSED_LEVEL:
按键被按下的实际电平,我的电路里,按键按下引脚接地。所以为低电平。
/**************************************************************************************************** 
*                             长按、单击 定义
* 长按事件:按下时间大于 KEY_LONG_PRESS_TIME,释放后响应。(不支持连按,需要连按响应可自己配置)
* 单击事件:按下时间小于 KEY_LONG_PRESS_TIME 释放后响应。
****************************************************************************************************/
// 长按时长的宏定义
#define KEY_LONG_PRESS_TIME    50  // 20ms*50 = 1s
#define KEY_PRESSED_LEVEL      0   // 按键被按下时的电平

4.3.2 变量定义
KeyCfg:全局变量,打包了按键的各个属性。

/**************************************************************************************************** 
*                            按键配置信息的全局结构体变量
****************************************************************************************************/
KEY_Configure_TypeDef KeyCfg={
        
        0,                        // 按键长按时长计数
        KEY_Action_Release,        // 按键动作,按下或释放 
        KEY_Status_Idle,        // 按键状态
        KEY_Event_Null,         // 按键事件
        KEY_ReadPin             // 读引脚电平函数
};

4.3.3函数定义
局部函数:

//读取引脚的电平
static KEY_Action_TypeDef KEY_ReadPin(void) 
{
  return (KEY_Action_TypeDef)GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0);
}

// 获取按键动作,按下或释放,保存到结构体
static void KEY_GetAction(void) 
{
    if(KeyCfg.KEY_ReadPin_Fcn() == KEY_PRESSED_LEVEL)
    {
        KeyCfg.KEY_Action = KEY_Action_Press;
    }
    else
    {
        KeyCfg.KEY_Action =  KEY_Action_Release;
    }
 
}


KEY_ReadPin:读取PA_0的电平状态。
KEY_GetAction:将读取到的电平状态转换为实际的按下或释放的动作。
状态处理函数
KEY_ReadStateMachine:编写,思路完全按照前文手绘的思路图像
首先读取按键的动作,再在switch case 里面匹配引脚的状态,case下用if判断按键动作或按下的时长,对状态、事件进行赋值。

void KEY_ReadStateMachine(void)
{

    
    KEY_GetAction();
    
    switch(KeyCfg.KEY_Status)
    {
        //状态:没有按键按下
        case KEY_Status_Idle:
            if(KeyCfg.KEY_Action == KEY_Action_Press)
            {
                KeyCfg.KEY_Status = KEY_Status_Debounce;
                KeyCfg.KEY_Event = KEY_Event_Null;
            }
            else
            {
                KeyCfg.KEY_Status = KEY_Status_Idle;
                KeyCfg.KEY_Event = KEY_Event_Null;
            }
            break;
            
        //状态:消抖
        case KEY_Status_Debounce:
            if(KeyCfg.KEY_Action == KEY_Action_Press)
            {
                KeyCfg.KEY_Status = KEY_Status_ConfirmPress;
                KeyCfg.KEY_Event = KEY_Event_Null;
            }
            else
            {
                KeyCfg.KEY_Status = KEY_Status_Idle;
                KeyCfg.KEY_Event = KEY_Event_Null;
            }
            break;    
            
        //状态:确认按下
        case KEY_Status_ConfirmPress:
            if( (KeyCfg.KEY_Action == KEY_Action_Press) && ( KeyCfg.KEY_Count >= KEY_LONG_PRESS_TIME))
            {
                printf("KEY_Status_ConfirmPressLong\r\n");
                KeyCfg.KEY_Count = 0;
                KeyCfg.KEY_Status = KEY_Status_ConfirmPressLong;
                KeyCfg.KEY_Event = KEY_Event_Null;
                
            }
            else if( (KeyCfg.KEY_Action == KEY_Action_Press) && (KeyCfg.KEY_Count < KEY_LONG_PRESS_TIME))
            {
                printf("继续按下 %d\r\n",KeyCfg.KEY_Count);
                KeyCfg.KEY_Count++;
                KeyCfg.KEY_Status = KEY_Status_ConfirmPress;
                KeyCfg.KEY_Event = KEY_Event_Null;
            }
            else
            {
                printf("突然gg,按短了 %d\r\n",KeyCfg.KEY_Count);
                KeyCfg.KEY_Count = 0;
                KeyCfg.KEY_Status = KEY_Status_Idle;
                KeyCfg.KEY_Event = KEY_Event_SingleClick;

            }
            break;    

            
        //状态:确认长按
        case KEY_Status_ConfirmPressLong:
            printf("KEY_Status_ConfirmPressLong\r\n");
            if(KeyCfg.KEY_Action == KEY_Action_Press) 
            {   // 一直等待其放开
                printf("一直按着 KEY_Status_ConfirmPressLong\r\n");
                KeyCfg.KEY_Status = KEY_Status_ConfirmPressLong;
                KeyCfg.KEY_Event = KEY_Event_Null;
                KeyCfg.KEY_Count = 0;
            }
            else
            {
                KeyCfg.KEY_Status = KEY_Status_Idle;
                KeyCfg.KEY_Event = KEY_Event_LongPress;
                KeyCfg.KEY_Count = 0;
            }
            break;    
        default:
            break;
    }

}


5.1定时器中断:
直接调用KEY_ReadStateMachine()函数即可,将读取到的事件保存到KeyCfg.KEY_Event变量。

extern KEY_Configure_TypeDef KeyCfg;
//定时器3中断服务程序
void TIM3_IRQHandler(void)   //TIM3中断
{

    if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否
    {
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx更新中断标志 
            KEY_ReadStateMachine();  //调用状态机
            
            if(KeyCfg.KEY_Event == KEY_Event_SingleClick)
            {
                printf("单击\r\n");//事件处理
            }
            if(KeyCfg.KEY_Event == KEY_Event_LongPress)
            {
                printf("长按\r\n");//事件处理
            }
        }
}

5.2 main函数
这里main函数十分简洁。初始化即可。
实际应用时,事件处理的代码不建议放在定时器里面。

int main(void)
{    
    uart_init(115200); // 用于查看输出
    TIM3_Int_Init(200-1,7200-1); //调用定时器使得20ms产生一个中断
    //按键初始化函数
    KEY_Init();
    
    while(1);

}     

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

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

相关文章

css初入门:网页布局之网格布局-grid

文章目录 网格布局-grid1、设置网格布局2、网格布局介绍3、网格容器grid-template-columns属性grid-template-rows 属性justify-content 属性justify-items属性align-content 属性 4、网格项目grid-column-start、grid-column-start、grid-column属性grid-row-start、grid-row-…

ChatGPT来了,必须紧跟时代潮流!

大家好&#xff0c;我是瓜叔。 正如标题所示&#xff0c;最近几周令人难以置信的兴奋。几乎每天都有surprise的新AI产品发布&#xff0c;特别是ChatGPT-4和AI绘画。这是一种科幻即将成为现实的感觉。例如&#xff0c;看看下面的网络图像&#xff0c;它是由AI生成的。当我看到它…

我的HackerOne漏洞赏金平台漏洞挖掘流程

简介 高强度在hackerone平台挖了一周的漏洞&#xff0c;在这里分享一下经验 选定目标 hackerone有两种src&#xff0c;一种是给钱的&#xff0c;一种是免费的&#xff0c;我一般都是选的有钱的去挖 假如我选择了这个作为今天的挖洞目标 首先需要往下滑&#xff0c;看他不收…

银行系统【GUI/Swing+MySQL】(Java课设)

系统类型 Swing窗口类型Mysql数据库存储数据 使用范围 适合作为Java课设&#xff01;&#xff01;&#xff01; 部署环境 jdk1.8Mysql8.0Idea或eclipsejdbc 运行效果 ​​​​​​​ 本系统源码地址&#xff1a;​​​​​​​https://download.csdn.net/download/qq_50…

科大讯飞的2022:夯实“根据地”业务,以技术创新点燃大模型产业落地的“星星之火”...

‍数据智能产业创新服务媒体 ——聚焦数智 改变商业 4月20日晚&#xff0c;科大讯飞正式公布2022年度及2023年度第一季度业绩报告。财报数据显示&#xff0c;公司2022年度实现营业收入188.20亿元&#xff0c;同比增长2.77%&#xff0c;实现毛利76.84亿元&#xff0c;较上年增长…

局域网 - CSMA/CD

文章目录 1 概述1.1 局域网的拓扑结构 2 CSMA/CD2.1 三种监听算法2.2 冲突检测原理2.3 二进制指数后退算法 3 扩展3.1 网工软考真题 1 概述 1.1 局域网的拓扑结构 2 CSMA/CD 对 总线型、星型 和 树型 拓扑访问控制协议是 CSMA/CD&#xff08;Carrier Sense Multiple Access/ …

Android硬件通信之 WIFI通信

一&#xff0c;简介 1.1 随着网络的普及和通信技术的发展&#xff0c;网络的传输速度也越来越快&#xff0c;wifi技术也还成为手机设备最基本的配置。我们可以通过wifi实现手机与手机之前的信息传输&#xff0c;当然也可以与任意一台有wifi模块的其它设备传输。 1.2 wifi与蓝…

mysql 命令集

数据库操作 查看库命令 show databases; 使用某个库&#xff08;可以不用分号 &#xff1b;&#xff09; use 库名 查看库下所有的表 show tables; 显示一个表下的所有字段和类型 desc user(表名); 查看一个表的建表语句 show create table user\G 创建数据库db1 cre…

gitee教程精简版

$ git config --global user.name "Your Name" $ git config --global user.email "emailexample.com" 设置名字和邮箱 初始化 git init git add test.txt 将文件预先添加到git仓库 git commit -m "刚刚我创建了一个文本"提交给git仓库&#x…

Android硬件通信之 蓝牙Mesh通信

一&#xff0c;简介 蓝牙4.0以下称为传统蓝牙&#xff0c;4.0以上是低功耗蓝牙&#xff0c;5.0开始主打物联网 5.0协议蓝牙最重要的技术就是Mesh组网&#xff0c;实现1对多&#xff0c;多对多的无线通信。即从点对点传输发展为网络拓扑结构&#xff0c;主要领域如灯光控制等&…

物联仓储系统ZigBee组网原理

在嵌入式项目物联仓储系统中&#xff0c;使用cortexM0模拟仓库&#xff0c;cortex-A9模拟服务器&#xff0c;两块开发板之间使用ZigBee技术实现数据接收和发送&#xff0c;本文就介绍一下ZigBee组网的原理和相关步骤。 1.组网概述 组建一个完整的zigbee网状网络包括两个步骤&am…

VSCode连接远程服务器调试代码详细流程

文章目录 1.远程连接服务器2. 打开项目文件目录3. 配置调试环境 在研究人工智能项目时&#xff0c;很多时候本地机器性能不够&#xff0c;只能把代码拉倒服务器上&#xff0c;然后利用服务器资源来运行代码。遇到问题时需要调试&#xff0c;本文详细介绍利用VScode来调试远程服…

简述docker镜像制作:阿里云私服使用说明

阿里云私服使用说明 使用阿里云容器镜像服务 在使用docker时&#xff0c;为了方便管理docker镜像和版本迭代&#xff0c;咱们推荐有一个镜像服务器。这里我们比较推荐使用服务商提供的容器镜像服务&#xff0c;一来不用自己搭建私服&#xff0c;节省维护成本&#xff0c;二来带…

【算法学习】—n皇后问题(回溯法)

【算法学习】—n皇后问题(回溯法) 1. 什么是回溯法&#xff1f; 相信"迷宫"是许多人儿时的回忆&#xff0c;大家小时候一定都玩过迷宫游戏。我们从不用别人教&#xff0c;都知道走迷宫的策略是&#xff1a; 当遇到一个岔路口&#xff0c;会有以下两种情况&#xf…

vue组件之间的数据共享

1.组件之间的关系 在项目开发中&#xff0c;组件之间的最常见的关系分为如下两种: ① 父子关系 ② 兄弟关系 1.父向子传值---使用自定义属性 父组件向子组件共享数据需要使用自定义属性。示例代码如下: 父组件&#xff1a; <Son :msg"message" :user"…

使用AI优化慢SQL,开发秒变DBA

“AI不会替代他们&#xff0c;但善用AI的人会” 慢 SQL 经常会让应用程序响应变慢&#xff0c;轻者影响用户体验&#xff0c;严重的时候可能会导致服务不可用。如果&#xff0c;每次遇到慢 SQL 都求助于 DBA&#xff0c;一方面效率很低&#xff0c;另一方面也会很没面子。所以…

PowerShell批量修改、替换大量文件的文件名

本文介绍基于PowerShell语言&#xff0c;对文件夹中全部文件的名称加以批量替换、修改的方法。 在之前的文章基于Python一次性批量修改多个文件的文件名&#xff08;https://blog.csdn.net/zhebushibiaoshifu/article/details/115869725&#xff09;中&#xff0c;我们介绍了基…

为什么重写equals时必须重写hashCode()

不重写equals和不重写 hashCode()之前&#xff1a;equals()比较的是对象的内存地址&#xff0c;hashCode()比较的其实也是内存地址(内存地址输入到哈希函数中得到的整数) 重写了之后&#xff0c;equals()比较的是对象的内容值&#xff0c;如果hashCode()不重写&#xff0c;还是…

log是什么文件可以删除吗?log文件被删怎么恢复?

在工作和生活中&#xff0c;我们难免会遇到因操作失误、软件崩溃或病毒攻击等原因导致重要文件被误删的情况&#xff0c;这其中包括log文件。如果您不幸遇到log文件被误删的情况&#xff0c;不要过于担心&#xff0c;本文将为您介绍几种方法&#xff0c;帮助您找回被误删除的lo…

【hello Linux】动静态库

目录 1. 了解动静态库 1. ldd 命令的使用 2. C语言库与C语言库 3. 库的分类 4. 库的命名 5. 库的内容 2. 静态库的制作和使用 1. 静态库的制作 2. 静态库的使用 3.动态库的制作和使用 1. 动态库的制作 2. 动态库的使用 Linux&#x1f337; 1. 了解动静态库 1. ldd 命令的使用 …