当前位置: 首页 > 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的…...

跨平台文件同步:OpenClaw调用GLM-4.7-Flash智能归类方案

跨平台文件同步&#xff1a;OpenClaw调用GLM-4.7-Flash智能归类方案 1. 为什么需要智能文件同步 作为一个长期在多台设备间切换工作的开发者&#xff0c;我深受文件管理混乱的困扰。Mac上的设计稿、Windows里的会议记录、手机拍摄的参考图&#xff0c;最终都会堆积在某个临时…...

RK3588部署MMPose模型踩坑实录:手把手教你解决ReduceL2算子溢出与精度丢失问题

RK3588部署MMPose模型实战&#xff1a;ReduceL2算子溢出问题的深度解析与手术级修复 当关键点检测模型的精度要求遇上边缘计算设备的硬件限制&#xff0c;RK3588平台上的MMPose部署往往会遭遇令人头疼的算子兼容性问题。其中ReduceL2算子的溢出问题尤为典型——它像一道无形的屏…...

前端国际化:别让你的应用只懂一种语言

前端国际化&#xff1a;别让你的应用只懂一种语言 毒舌时刻这应用写得跟方言似的&#xff0c;出了本地就没人懂。各位前端同行&#xff0c;咱们今天聊聊前端国际化。别告诉我你的应用还只有中文版本&#xff0c;那感觉就像在国际会议上只说方言——能说&#xff0c;但没人懂。 …...

Python内存暴涨突然崩溃?3个被90%开发者忽略的GC调优关键点揭秘

第一章&#xff1a;Python内存暴涨与崩溃的典型现象诊断当Python程序在运行中突然响应迟缓、频繁触发MemoryError&#xff0c;或进程被操作系统强制终止&#xff08;如Linux下收到SIGKILL (9)&#xff09;&#xff0c;往往标志着内存使用已严重失控。这类问题通常不会立即暴露&…...

NeoPixel Painter:嵌入式HSV动画的固定点实现

1. NeoPixel Painter 库概述NeoPixel Painter 是一个面向嵌入式 LED 动画开发的轻量级 Arduino 库&#xff0c;专为简化 WS2812B&#xff08;NeoPixel&#xff09;LED 灯带的动态效果编程而设计。它并非对 Adafruit_NeoPixel 库的简单封装&#xff0c;而是构建在其之上的状态驱…...

从0到1:Fugu14完美越狱工具实战指南

从0到1&#xff1a;Fugu14完美越狱工具实战指南 【免费下载链接】Fugu14 Fugu14 is an untethered iOS 14.3-14.5.1 jailbreak 项目地址: https://gitcode.com/gh_mirrors/fu/Fugu14 iOS 14.3-14.5.1设备如何突破系统限制&#xff1f;Fugu14作为一款强大的完美越狱工具&…...

TypeScript——编译器和编译选项

编译器和编译选项 1、编译器1.1、安装编译器1.1.1、--help、--all1.1.2、--version 2、编译程序2.1、编译单个文件2.2、编译多个文件2.3、--watch和-w2.4、--presserveWatchOutput 2、编译选项2.1、编译选项风格2.2、使用编译选项2.3、严格类型检查2.3.1、--strict2.3.2、--nol…...

线性代数小白必看:孔祥仁网课笔记整理(附二阶到n阶行列式详解)

线性代数入门&#xff1a;从二阶行列式到n阶行列式的完整指南 第一次接触线性代数时&#xff0c;行列式这个概念就像一堵高墙挡在面前。记得我大一时&#xff0c;光是理解二阶行列式的几何意义就花了整整一周时间。直到后来遇到一位好老师&#xff0c;用简单的例子帮我打通了任…...

5个秘诀:如何快速生成专业艺术二维码的完整指南

5个秘诀&#xff1a;如何快速生成专业艺术二维码的完整指南 【免费下载链接】amazing-qr &#x1f4ae; amazing QRCode generator in Python (supporting animated gif) - Python amazing 二维码生成器&#xff08;支持 gif 动态图片二维码&#xff09; 项目地址: https://g…...

深入排查k8s集群6443端口连接拒绝:从kubectl故障到系统级修复

1. 当kubectl突然罢工&#xff1a;6443端口连接拒绝的紧急处理 那天早上我像往常一样打开终端&#xff0c;准备用kubectl get pods查看集群状态&#xff0c;结果终端冷冰冰地抛出一行错误&#xff1a;"Unable to connect to the server: dial tcp 192.168.1.1:6443: conne…...