单片机长短按简单实现
单片机长短按简单实现
目录
- 单片机长短按简单实现
- 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爱好者😊///持续分享所学😊///如有需要欢迎收藏转发///😊】 今日分享关于求两数的最大公约数的相关内容ÿ…...
自动化办公-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外面,z里面的大,颜色不同 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 添加列(AddColumn):条件语句(If..then..else):容错语句(try..otherwise): 排序(ReorderColumns):筛选(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; // 心跳间隔,默认为10秒 this.reconnectInterval …...

ASP.NET Core8.0学习笔记(十九)——EF Core DbSet
一、DbSet概述 1.DbSet提供了通过DbContext对表进行查询操作的路径。DbSet对应的属性名称将默认映射为实体T的表名。 2.使用DbSet<T>进行查询的方法: (1)直接在DbContext中创建对应的DbSet<T>属性 (2)使用DbSet DbContext.Set<T>方法操作数据表。…...
Android Camera 预览角度和拍照保存图片角度相关
–基于Android R(11) 关于Camera Camera Framework 的架构 Android Camera Framework 是一个分层架构,由以下组件组成: HAL(硬件抽象层): HAL 抽象底层相机硬件,提供与不同设备相机进行交互的标准接口.CameraService : Camera…...

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

友元运算符重载函数
目录 1.定义友元运算符重载函数的语法形式 2.双目运算符重载 3.单目运算符重载 1.定义友元运算符重载函数的语法形式 (1)在类的内部,定义友元运算符重载函数的格式如下: friend 函数类型 operator 运算符(形参表&a…...
从0开始实现es6 promise类
主要由基础实现和静态类的实现两部分组成。 1 基础实现(不含静态类) 1.1 使用类实现完成构造函数 实现代码如下,构造函数传入一个回调函数,定义resolve和reject函数,将两个函数作为参数执行回调函数。 // 1. 使用类实…...
XML 编码
XML 编码 XML(可扩展标记语言)是一种用于存储和传输数据的标记语言。它由万维网联盟(W3C)开发,旨在提供一种标准的方式来结构化、存储和传输数据。XML的设计目标是既易于人类阅读,也易于机器解析。 XML的…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...

算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...

Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...
【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权
摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题:安全。文章将详细阐述认证(Authentication) 与授权(Authorization的核心概念,对比传统 Session-Cookie 与现代 JWT(JS…...