单片机长短按简单实现

news2024/11/18 6:00:01

单片机长短按简单实现

目录

  • 单片机长短按简单实现
    • 1 原理
    • 2 示例代码
      • 2.1 按键实现
    • 3 测试log
    • 4 其他实现方式

1 原理

按键检测和处理的步骤如下:

1:定时扫描按键(使用定时器定时扫描,也可以用软件延时或者系统心跳之类的方式,总之能保证每次扫描间隔时间固定并且在一个较小的范围即可)。
2:扫描到有按键按下(通常是检测GPIO的电平状态来判断按键是否按下,具体情况需要结合实际硬件电路来看)。
3:开始计时,记录按键持续按下的时间。
4:若按下的时间达到了短按的时间(具体多长的时间为短按由自己定义),选择触发按键处理(按下即触发),或者先记录状态,等按键释放时再触发按键处理(弹起时触发)。
5:按键时间超过短按时间,继续计时。
6:按键时间达到长按时间(具体多长的时间为长按由自己定义),选择触发按键处理(按下即触发),或者先记录状态,等按键释放时再触发按键处理(弹起时触发)。

2 示例代码

该示例使用的GD32,共配置了4个按键,特点如下:

1:按键按下时电平为0,释放时为1。
2:短按时间为30ms。
3:长按时间为1s。
4:短按释放时触发按键处理。
5:长按按下时即触发按键处理。
6:按键扫描和按键处理均放在定时器中断服务函数,若按键处理的时间较长,建议分开操作(按键扫描还是放在中断,按键处理放在其他地方,以免长时间占用中断时间)。
7:按键处理我这里都是留空的,只用串口打印了一句话,表明已经触发了按键处理,具体处理什么东西看实际需求。

注:示例代码仅供参考,还需要按具体需求修改。

2.1 按键实现

key.c:

#include "key.h"
#include "main.h"

key_t key1;
key_t key2;
key_t key3;
key_t key4;

void timer_user_init(void);

// 按键初始化
void key_user_init(void)
{
    /* enable the key clock */
    rcu_periph_clock_enable(KEY1_CLOCK);
    rcu_periph_clock_enable(KEY2_CLOCK);
    rcu_periph_clock_enable(KEY3_CLOCK);
    rcu_periph_clock_enable(KEY4_CLOCK);

    /* configure key gpio port */ 
	gpio_init(KEY1_PORT, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, KEY1_PIN);
    gpio_init(KEY2_PORT, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, KEY2_PIN);
	gpio_init(KEY3_PORT, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, KEY3_PIN);
	gpio_init(KEY4_PORT, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, KEY4_PIN);

    timer_user_init();  // 启动定时器,定时扫描按键
}

// 按键扫描
int key_scan(void)
{
    if(READ_KEY1_STATE == KEY_PRESSED && (key1.status == 0))
    {
        if(++key1.debounce > KEY_SHORT_PRESSED)
        {// 短按
            key1.status = 1;
            // LOG("key1 short pressed.\n");
        }
    }
    else if(READ_KEY1_STATE == KEY_PRESSED && (key1.status == 1))
    {
        if(++key1.debounce > KEY_LONG_PRESSED)
        {// 长按
            key1.status = 2;
            key1.debounce = 0;
            // LOG("key1 long pressed.\n");
            return KEY1_LONG_PRESSED;
        }
    }
    else if((READ_KEY1_STATE == KEY_RELEASED) && (key1.status > 0))
    {
        if(key1.status == 1)
        {// 短按释放
            key1.status = 0;
            key1.debounce = 0;
            return KEY1_SHORT_PRESSED;
        }
        if(key1.status == 2)
        {// 长按释放
            key1.status = 0;
            key1.debounce = 0;
        }
    }
    
    if(READ_KEY2_STATE == KEY_PRESSED && (key2.status == 0))
    {
        if(++key2.debounce > KEY_SHORT_PRESSED)
        {// 短按
            key2.status = 1;
        }
    }
    else if(READ_KEY2_STATE == KEY_PRESSED && (key2.status == 1))
    {
        if(++key2.debounce > KEY_LONG_PRESSED)
        {// 长按
            key2.status = 2;
            key2.debounce = 0;
            return KEY2_LONG_PRESSED;
        }
    }
    else if((READ_KEY2_STATE == KEY_RELEASED) && (key2.status > 0))
    {
        if(key2.status == 1)
        {// 短按释放
            key2.status = 0;
            key2.debounce = 0;
            return KEY2_SHORT_PRESSED;
        }
        if(key2.status == 2)
        {// 长按释放
            key2.status = 0;
            key2.debounce = 0;
        }
    }

    if(READ_KEY3_STATE == KEY_PRESSED && (key3.status == 0))
    {
        if(++key3.debounce > KEY_SHORT_PRESSED)
        {// 短按
            key3.status = 1;
        }
    }
    else if(READ_KEY3_STATE == KEY_PRESSED && (key3.status == 1))
    {
        if(++key3.debounce > KEY_LONG_PRESSED)
        {// 长按
            key3.status = 2;
            key3.debounce = 0;
            return KEY3_LONG_PRESSED;
        }
    }
    else if((READ_KEY3_STATE == KEY_RELEASED) && (key3.status > 0))
    {
        if(key3.status == 1)
        {// 短按释放
            key3.status = 0;
            key3.debounce = 0;
            return KEY3_SHORT_PRESSED;
        }
        if(key3.status == 2)
        {// 长按释放
            key3.status = 0;
            key3.debounce = 0;
        }
    }

    if(READ_KEY4_STATE == KEY_PRESSED && (key4.status == 0))
    {
        if(++key4.debounce > KEY_SHORT_PRESSED)
        {// 短按
            key4.status = 1;
        }
    }
    else if(READ_KEY4_STATE == KEY_PRESSED && (key4.status == 1))
    {
        if(++key4.debounce > KEY_LONG_PRESSED)
        {// 长按
            key4.status = 2;
            key4.debounce = 0;
            return KEY4_LONG_PRESSED;
        }
    }
    else if((READ_KEY4_STATE == KEY_RELEASED) && (key4.status > 0))
    {
        if(key4.status == 1)
        {// 短按释放
            key4.status = 0;
            key4.debounce = 0;
            return KEY4_SHORT_PRESSED;
        }
        if(key4.status == 2)
        {// 长按释放
            key4.status = 0;
            key4.debounce = 0;
        }
    }
    return -1;
}

// 按键处理
void key_handle(void)
{
    static uint8_t key_state;
    uint8_t i;
    static uint8_t step = 0;
    key_state = key_scan();  // 按键扫描
    if(key_state == KEY1_SHORT_PRESSED)
    {// 按键1短按
        LOG("key1 short pressed.\n");
    }
    else if(key_state == KEY1_LONG_PRESSED)
    {// 按键1长按
        LOG("key1 long pressed.\n");
    }
    else if(key_state == KEY2_SHORT_PRESSED)
    {// 按键2短按
        LOG("key2 short pressed.\n");
    }
    else if(key_state == KEY2_LONG_PRESSED)
    {// 按键2长按
        LOG("key2 long pressed.\n");
    }
    else if(key_state == KEY3_SHORT_PRESSED)
    {// 按键3短按
        LOG("key3 short pressed.\n");
    }
    else if(key_state == KEY3_LONG_PRESSED)
    {// 按键3长按
        LOG("key3 long pressed.\n");
    }
    else if(key_state == KEY4_SHORT_PRESSED)
    {// 按键4短按
        LOG("key4 short pressed.\n");
    }
    else if(key_state == KEY4_LONG_PRESSED)
    {// 按键4长按
        LOG("key4 long pressed.\n");
    }
}

/********************** 定时器配置,用于定时扫描按键 *************************/ 
void TIMER2_IRQHandler(void)
{
    if(SET == timer_interrupt_flag_get(TIMER2, TIMER_INT_UP))
    {
        /* clear channel 0 interrupt bit */
        timer_interrupt_flag_clear(TIMER2, TIMER_INT_UP);

        key_handle();  // 按键扫描并处理
    }
}

void nvic_config(void)
{
    nvic_irq_enable(TIMER2_IRQn, 0, 0);
}

void timer_config(void)
{
    /* ----------------------------------------------------------------------------
    TIMER2 Configuration: 
    TIMER2CLK = SystemCoreClock/18000 = 10KHz, the period is 1s(10/10000 = 1s).
    ---------------------------------------------------------------------------- */
    timer_parameter_struct timer_initpara;

    /* enable the peripherals clock */
    rcu_periph_clock_enable(RCU_TIMER2);

    /* deinit a TIMER */
    timer_deinit(TIMER2);
    /* initialize TIMER init parameter struct */
    timer_struct_para_init(&timer_initpara);
    /* TIMER2 configuration */
    timer_initpara.prescaler         = 18000 - 1;           // 180MHz / 18000 = 10kHz
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = TIMER_COUNTER_UP;
    timer_initpara.period            = 10 - 1;             // 10 * 0.01ms = 1ms
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_init(TIMER2, &timer_initpara);

    /* enable the TIMER interrupt */
    timer_interrupt_enable(TIMER2, TIMER_INT_UP);
    /* enable a TIMER */
    timer_enable(TIMER2);
}

void timer_user_init(void)
{
    /* configure the TIMER peripheral */
    timer_config();
    /* configure the TIMER2 interrupt */
    nvic_config();
}

key.h:

#ifndef __KEY_H
#define __KEY_H

#include "gd32e50x.h"
#include "gd32e50x_gpio.h"

#define KEY1_SHORT_PRESSED   0
#define KEY2_SHORT_PRESSED   1
#define KEY3_SHORT_PRESSED   2
#define KEY4_SHORT_PRESSED   3
#define KEY1_LONG_PRESSED    4
#define KEY2_LONG_PRESSED    5
#define KEY3_LONG_PRESSED    6
#define KEY4_LONG_PRESSED    7

#define KEY_PRESSED          0      // 按下时电平为0
#define KEY_RELEASED         1      // 按下时电平为1
#define KEY_SHORT_PRESSED    30     // 1ms x 30 = 30ms
#define KEY_LONG_PRESSED     1000   // 1ms x 1000 = 1s

#define KEY1_CLOCK       RCU_GPIOB
#define KEY1_PORT        GPIOB
#define KEY1_PIN         GPIO_PIN_12
#define KEY2_CLOCK       RCU_GPIOB
#define KEY2_PORT        GPIOB
#define KEY2_PIN         GPIO_PIN_13
#define KEY3_CLOCK       RCU_GPIOB
#define KEY3_PORT        GPIOB
#define KEY3_PIN         GPIO_PIN_14
#define KEY4_CLOCK       RCU_GPIOB
#define KEY4_PORT        GPIOB
#define KEY4_PIN         GPIO_PIN_15

#define READ_KEY1_STATE  gpio_input_bit_get(KEY1_PORT, KEY1_PIN)
#define READ_KEY2_STATE  gpio_input_bit_get(KEY2_PORT, KEY2_PIN)
#define READ_KEY3_STATE  gpio_input_bit_get(KEY3_PORT, KEY3_PIN)
#define READ_KEY4_STATE  gpio_input_bit_get(KEY4_PORT, KEY4_PIN)

typedef struct
{
    uint16_t debounce;
    uint8_t status;
} key_t;

void key_user_init(void);
int key_scan(void);

#endif

main.c:

#include "main.h"
#include "uart.h"
#include "key.h"

int main(void)
{
    // systick_config();
    uart_user_init();
    key_user_init();
    printf("app init success.\n");
    while(1)
    {
    }
}

mian.h:

#ifndef MAIN_H
#define MAIN_H

#include "gd32e50x.h"
#include "gd32e50x_rcu.h"
#include "gd32e50x_gpio.h"
#include "systick.h"
#include "uart.h"

#define UART_DEBUG

#ifdef UART_DEBUG
    #define DEBUG(format, ...) printf(format, ##__VA_ARGS__)
    #define LOG  printf
#else
    #define DEBUG(format, ...)
    #define LOG(format, ...) 
#endif

#endif /* MAIN_H */

3 测试log

在这里插入图片描述

4 其他实现方式

详见我之前的博客:以STM32为例,实现按键的短按和长按

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

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

相关文章

【动态规划-分组背包】力扣1981. 最小化目标值与所选元素的差

给你一个大小为 m x n 的整数矩阵 mat 和一个整数 target 。 从矩阵的 每一行 中选择一个整数,你的目标是 最小化 所有选中元素之 和 与目标值 target 的 绝对差 。 返回 最小的绝对差 。 a 和 b 两数字的 绝对差 是 a - b 的绝对值。 示例 1: 输入…

DAY17||654.最大二叉树 |617.合并二叉树 |700.二叉搜索树中的搜索 |

654.最大二叉树 题目:654. 最大二叉树 - 力扣(LeetCode) 给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下: 二叉树的根是数组中的最大元素。左子树是通过数组中最大值左边部分构造出的最大二叉树。右子树…

物联网系统中TFT_LCD屏主流驱动方案详解

01 物联网系统中为什么要使用TFT-LCD驱动芯片 在物联网系统中使用TFT-LCD(薄膜晶体管液晶显示器)驱动芯片的原因主要可以归纳为以下几点: 专业性与高效性 1、专业图形处理:TFT-LCD驱动芯片内置了专业的图形处理引擎&#xff0…

全功能运营级开源跨境电商平台源码 —— 多语言全球化商城解决方案

实测分享【全功能运营级开源跨境电商平台】,它不仅默认集成了中英文双语系统,更内置了强大的翻译接口,支持自动翻译至全球133种语言,为商家打开通往世界的窗口。 核心特性亮点解析: 多语言自动翻译:该平台…

WEB 编程:富文本编辑器 Quill 配合 Pico.css 样式被影响的问题之还是 iframe

这个系列已经写了 3 篇了。这篇写如何使用 iframe 解决标题里面提到的问题。 前情提要 请看上一篇博文: WEB 编程:富文本编辑器 Quill 配合 Pico.css 样式被影响的问题之Shadow DOM WEB 编程:富文本编辑器 Quill 配合 Pico.css 样式被影响…

常见字符函数和字符串函数(上)

1. 字符分类函数 字符分类函数是一类用于判断字符是否属于特定的字符类别&#xff08;如字母、数字、标点符号等&#xff09;的函数。在C语言中&#xff0c;这些函数通常可以在<ctype.h>头文件中找到&#xff0c;它们对于文本处理和输入验证非常有用。 这些函数通常返回…

Windows系统镜像下载

目前官网还可以下载的系统有Win10和Win11&#xff0c;Win7因为已经停止维护&#xff0c;所以只能到第三方镜像站下载&#xff0c;下面将具体展示下载方法。 Win7 下载地址&#xff1a;MSDN, 我告诉你 - 做一个安静的工具站 在操作系统中找到Windows 7&#xff0c;在右侧选择…

Python | Leetcode Python题解之第445题两数相加II

题目&#xff1a; 题解&#xff1a; class Solution:def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:if head is None or head.next is None:return headnew_head self.reverseList(head.next)head.next.next head # 把下一个节点指向自己head…

魔都千丝冥缘——软件终端架构思维———未来之窗行业应用跨平台架构

一、创生-魔都千丝冥缘 在那神秘而复杂的数字世界里&#xff0c;软件的舞台上正上演着一场精彩的剧目。当面对终端单页面中如蛛网般交织的复杂业务逻辑&#xff0c;我们如同在迷雾中摸索前行。多个表单承载着各自的使命&#xff0c;却又有着相同字段的微妙关联&#xff0c;而内…

【C++前缀和 动态规划 贪心】813. 最大平均值和的分组|1936

本文涉及的基础知识点 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 C动态规划 贪心(决策包容性) LeetCode813. 最大平均值和的分组 给定数组 nums 和一个整数 k 。我们将给定的数组 nums 分成 最多 k 个非空子数组&#xff0c;且数组内…

【电商搜索】现代工业级电商搜索技术-Facebook语义搜索技术QueSearch

【电商搜索】现代工业级电商搜索技术-Facebook语义搜索技术Que2Search 目录 文章目录 【电商搜索】现代工业级电商搜索技术-Facebook语义搜索技术Que2Search目录0. 论文信息1. 研究背景&#xff1a;2. 技术背景和发展历史&#xff1a;3. 算法建模3.1 模型架构3.1.1 双塔与分类 …

物流货运托运发货单二联三联打印软件定制 佳易王物流单管理系统操作教程

一、前言 物流货运托运发货单二联三联打印软件定制 佳易王物流单管理系统操作教程 1、软件为绿色免安装版&#xff0c;解压即可使用&#xff0c;已经内置数据库&#xff0c;不需再安装。 2、软件下载可以到本文章最后点击官网卡片下。 二、软件程序教程 1、如图&#xff0c;…

C++入门(2)--引用

6.引用 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开辟内存空间&#xff0c;它和它引用的变量共用同一块内存空间。 类型& 引用变量名(对象名) 引用实体&#xff1b; //引用 //类型& 引用变量名(对象名) 引…

基于php的幸运舞蹈课程工作室管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

24-9-28-读书笔记(二十)-《契诃夫文集》(四)上([俄] 契诃夫 [译] 汝龙 )

文章目录 《契诃夫文集》&#xff08;四&#xff09;上&#xff08;[俄] 契诃夫 [译] 汝龙 &#xff09;目录阅读笔记记录总结 《契诃夫文集》&#xff08;四&#xff09;上&#xff08;[俄] 契诃夫 [译] 汝龙 &#xff09; 时间过得好快啊&#xff0c;马上又要十月份了&#x…

开源链动2+1模式AI智能名片小程序源码:放大特色,构建独特竞争力

摘要&#xff1a;本文探讨在当今社会背景下&#xff0c;开源链动21模式AI智能名片小程序源码如何通过坚持与众不同来构建独特竞争力。在信息传播便捷但个体易同质化的时代&#xff0c;拥有特色是脱颖而出的关键&#xff0c;而这种模式下的小程序源码具有独特的发展潜力。 一、引…

Golang | Leetcode Golang题解之第443题压缩字符串

题目&#xff1a; 题解&#xff1a; func compress(chars []byte) int {write, left : 0, 0for read, ch : range chars {if read len(chars)-1 || ch ! chars[read1] {chars[write] chwritenum : read - left 1if num > 1 {anchor : writefor ; num > 0; num / 10 {…

bidict:Python 中高效的双向字典库

在 Python 编程中&#xff0c;字典&#xff08;dict&#xff09;是用于存储键值对的强大数据结构&#xff0c;能够快速地根据键查找值。然而&#xff0c;有时我们不仅需要从键查找值&#xff0c;还需要根据值查找键。这种双向查询需求在很多场景中很常见&#xff0c;如映射表、…

openEuler 修改网卡和常用网络配置

简介 1、网卡启用、禁用和查看状态 2、编辑网卡&#xff0c;配置静态Ip 3、删除之前网卡缓存的Ip 4、机器即连接内网&#xff0c;又连接外网 的网卡配置 5、openEuler 修改默认的镜像仓库&#xff08;推荐&#xff09;1、网卡启用、禁用和查看状态 # 查看网络状态 nmcli devi…

muduo网络库介绍

文章目录 MuduoServer常见接口TcpServer类EventLoop类TcpConnection类 服务器搭建Client常见接口TcpClient类 客户端搭建 Muduo Muduo是陈硕大佬开发的,一个基于非阻塞IO和事件驱动的C高并发网络编程库 这是一个基于主从Reactor模型的网络编程库,线程模型是one loop per thre…