当前位置: 首页 > news >正文

单片机长短按简单实现

单片机长短按简单实现

目录

  • 单片机长短按简单实现
    • 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 = 10kHztimer_initpara.alignedmode       = TIMER_COUNTER_EDGE;timer_initpara.counterdirection  = TIMER_COUNTER_UP;timer_initpara.period            = 10 - 1;             // 10 * 0.01ms = 1mstimer_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为例,实现按键的短按和长按

相关文章:

单片机长短按简单实现

单片机长短按简单实现 目录 单片机长短按简单实现1 原理2 示例代码2.1 按键实现 3 测试log4 其他实现方式 1 原理 按键检测和处理的步骤如下: 1:定时扫描按键(使用定时器定时扫描,也可以用软件延时或者系统心跳之类的方式&#…...

如何用好通义灵码企业知识库问答能力?

通义灵码企业版:通义灵码企业标准版快速入门_智能编码助手_AI编程_智能编码助手通义灵码(Lingma)-阿里云帮助中心 通义灵码提供了基于企业知识库的问答检索增强的能力,在开发者使用通义灵码 IDE 插件时,可以结合企业知识库内上传的文档、文件…...

C语言自定义类型:联合体

目录 前言一、联合体1.1 联合体类型的声明1.2 联合体的特点1.3 相同成员的结构体和联合体对比1.4 联合体大小的计算1.5 联合体的⼀个练习 总结 前言 前面我讲到C语言中的自定义结构——结构体,其实C语言中的自定义结构不只有结构体,还有枚举和联合体&am…...

【JavaEE】——线程池大总结

阿华代码,不是逆风,就是我疯, 你们的点赞收藏是我前进最大的动力!!希望本文内容能够帮助到你! 目录 引入:问题引入 一:解决方案 1:方案一——协程/纤程 (1…...

编程中为什么使用0和1表示状态

前言 这是我在这个网站整理的笔记,有错误的地方请指出,关注我,接下来还会持续更新。 作者:神的孩子都在歌唱 我们看到很多项目都使用0和1表示某些状态信息,具体含义取决于上下文。以下是一些常见的用法: 布尔值&#x…...

C++入门基础知识90(实例)——实例15【求两数的最大公约数】

成长路上不孤单😊😊😊😊😊😊 【14后😊///C爱好者😊///持续分享所学😊///如有需要欢迎收藏转发///😊】 今日分享关于求两数的最大公约数的相关内容&#xff…...

自动化办公-Python-os模块的使用

os.path 模块的使用 在指定文件路径时,由于操作系统的差异,直接使用硬编码的路径可能会导致程序在不同平台上无法正常运行。为了解决这个问题,Python 提供了 os.path 模块,它包含了一系列用于路径操作的函数,可以帮助您…...

无人机之数据处理技术篇

一、数据采集 无人机通过搭载的各种传感器和设备,如GPS、加速度计、陀螺仪、磁力计、激光雷达(LiDAR)、高光谱相机(Hyperspectral)、多光谱相机(Multispectral)以及普通相机等,实时采集飞行过程中的各种数据。这些数据包括无人机的位置、速度、高度、姿态…...

828华为云征文|部署多功能集成的协作知识库 AFFiNE

828华为云征文|部署多功能集成的协作知识库 AFFiNE 一、Flexus云服务器X实例介绍二、Flexus云服务器X实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置2.4 Docker 环境搭建 三、Flexus云服务器X实例部署 AFFiNE3.1 AFFiNE 介绍3.2 AFFiNE 部署3.3 AFFiNE 使用 四、…...

c++(AVL树及其实现)

一、AVL树的概念 AVL树是最先发明的自平衡⼆叉查找树,AVL是⼀颗空树,或者具备下列性质的⼆叉搜索树:它的 左右子树都是AV树,且左右子树的高度差的绝对值不超过1。AVL树是⼀颗高度平衡搜索⼆叉树, 通过控制高度差去控…...

Cesium GIS项目关于湖泊识别与提取的实现

1. 引言 项目背景 随着遥感技术的发展,地理信息系统的应用越来越广泛。本项目旨在开发一个基于Cesium的地理信息系统,利用深度学习技术自动识别并显示湖泊的位置。 目标与意义 通过自动化处理大量遥感影像数据,提高湖泊监测的效率和准确性,为水资源管理和环境保护提供支…...

两个圆形 一个z里面一个z外面,z里面的大,颜色不同 html

两个圆形 一个z里面一个z外面&#xff0c;z里面的大&#xff0c;颜色不同 html <!DOCTYPE html> <html> <head> <style> .outer-circle {width: 150px;height: 150px;border-radius: 50%;background-color: #ff9999; /* 外圆的颜色 */position: relat…...

【Power Query】M函数-table

M函数-table 添加列&#xff08;AddColumn&#xff09;&#xff1a;条件语句&#xff08;If..then..else&#xff09;&#xff1a;容错语句&#xff08;try..otherwise&#xff09;&#xff1a; 排序&#xff08;ReorderColumns&#xff09;&#xff1a;筛选&#xff08;Selec…...

uni-app 封装websocket 心跳检测,开箱即用

class websocketUtils {constructor(url, needbeat, options {}) {this.needbeat needbeat;this.url url;this.options options;this.ws null;this.heartbeatInterval options.heartbeatInterval || 10000; // 心跳间隔&#xff0c;默认为10秒 this.reconnectInterval …...

ASP.NET Core8.0学习笔记(十九)——EF Core DbSet

一、DbSet概述 1.DbSet提供了通过DbContext对表进行查询操作的路径。DbSet对应的属性名称将默认映射为实体T的表名。 2.使用DbSet<T>进行查询的方法&#xff1a; (1)直接在DbContext中创建对应的DbSet<T>属性 (2)使用DbSet DbContext.Set<T>方法操作数据表。…...

Android Camera 预览角度和拍照保存图片角度相关

–基于Android R(11) 关于Camera Camera Framework 的架构 Android Camera Framework 是一个分层架构&#xff0c;由以下组件组成&#xff1a; HAL&#xff08;硬件抽象层&#xff09;: HAL 抽象底层相机硬件,提供与不同设备相机进行交互的标准接口.CameraService : Camera…...

新手如何使用Qt——方法使用

前言 那么这篇文章其实是我在使用Qt的过程当中呢&#xff0c;我发现在Qt使用过程中&#xff0c;在我理解信号和槽这个概念后&#xff0c;在编写槽函数数的时候&#xff0c;发现了自身存在的问题&#xff0c;我的难点是在于当我在编写槽函数的时候&#xff0c;我知道这个槽函数是…...

友元运算符重载函数

目录 1.定义友元运算符重载函数的语法形式 2.双目运算符重载 3.单目运算符重载 1.定义友元运算符重载函数的语法形式 &#xff08;1&#xff09;在类的内部&#xff0c;定义友元运算符重载函数的格式如下&#xff1a; friend 函数类型 operator 运算符&#xff08;形参表&a…...

从0开始实现es6 promise类

主要由基础实现和静态类的实现两部分组成。 1 基础实现&#xff08;不含静态类&#xff09; 1.1 使用类实现完成构造函数 实现代码如下&#xff0c;构造函数传入一个回调函数&#xff0c;定义resolve和reject函数&#xff0c;将两个函数作为参数执行回调函数。 // 1. 使用类实…...

XML 编码

XML 编码 XML&#xff08;可扩展标记语言&#xff09;是一种用于存储和传输数据的标记语言。它由万维网联盟&#xff08;W3C&#xff09;开发&#xff0c;旨在提供一种标准的方式来结构化、存储和传输数据。XML的设计目标是既易于人类阅读&#xff0c;也易于机器解析。 XML的…...

Ubuntu系统下交叉编译openssl

一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机&#xff1a;Ubuntu 20.04.6 LTSHost&#xff1a;ARM32位交叉编译器&#xff1a;arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

IGP(Interior Gateway Protocol,内部网关协议)

IGP&#xff08;Interior Gateway Protocol&#xff0c;内部网关协议&#xff09; 是一种用于在一个自治系统&#xff08;AS&#xff09;内部传递路由信息的路由协议&#xff0c;主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

【网络安全产品大调研系列】2. 体验漏洞扫描

前言 2023 年漏洞扫描服务市场规模预计为 3.06&#xff08;十亿美元&#xff09;。漏洞扫描服务市场行业预计将从 2024 年的 3.48&#xff08;十亿美元&#xff09;增长到 2032 年的 9.54&#xff08;十亿美元&#xff09;。预测期内漏洞扫描服务市场 CAGR&#xff08;增长率&…...

【JVM】- 内存结构

引言 JVM&#xff1a;Java Virtual Machine 定义&#xff1a;Java虚拟机&#xff0c;Java二进制字节码的运行环境好处&#xff1a; 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收的功能数组下标越界检查&#xff08;会抛异常&#xff0c;不会覆盖到其他代码…...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...