蓝桥杯篇---按键长按与双击


文章目录

  • 前言
  • 1. 新增全局变量和宏定义
    • 解释
      • 1.1宏定义
        • KEY_EVENT_*
        • DEBOUNCE_TIME
        • HOLD_TIME
        • DOUBLE_TIME
        • MULTI_TIME
      • 1.2全局变量
        • Key_Val
        • Key_Old
        • Key_Down
        • Key_Up
        • sys_tick
        • key_event
        • key_pressed
        • key_press_start
        • key_last_release
        • click_cnt
  • 2. 定时器初始化(1ms中断)
    • 解释
      • 2.1定时器初始化
      • 2.2中断服务函数
  • 3. 按键扫描与状态机修改
    • 解释
      • 3.1ScanKey函数
      • 3.2Key_Loop函数
  • 4. 主函数使用示例
    • 解释
  • 5. 关键逻辑说明
    • 5.1消抖处理
    • 5.2长按检测
    • 5.3双击检测
    • 5.4连击检测
  • 6. 优化建议
    • 6.1矩阵键盘冲突
    • 6.2低功耗优化
    • 6.3事件调回


前言

本文简单介绍了一段代码,该代码是基于STC15F2K60S2单片机的按键检测程序,新增了长按、短按、双击、连击等功能,并采用状态机设计来适配原有的矩阵键盘扫描逻辑


1. 新增全局变量和宏定义

#include <STC15F2K60S2.H>// 新增按键事件类型定义
#define KEY_EVENT_NONE    0
#define KEY_EVENT_SHORT   1  // 短按/单击
#define KEY_EVENT_LONG    2  // 长按
#define KEY_EVENT_DOUBLE  3  // 双击
#define KEY_EVENT_MULTI   4  // 连击(如3次)// 时间阈值配置(单位:ms,需适配定时器频率)
#define DEBOUNCE_TIME   20   // 消抖时间
#define HOLD_TIME       1000 // 长按判定时间
#define DOUBLE_TIME     300  // 双击间隔
#define MULTI_TIME      500  // 连击间隔// 全局按键状态变量
unsigned char Key_Val = 0;    // 当前键值
unsigned char Key_Old = 0;    // 上一次键值
unsigned char Key_Down = 0;   // 按下瞬间
unsigned char Key_Up = 0;     // 释放瞬间volatile unsigned int sys_tick = 0;  // 系统时间戳(需定时器1ms中断更新)
unsigned char key_event = KEY_EVENT_NONE;  // 当前按键事件
unsigned char key_pressed = 0;        // 当前按下的键值
unsigned int key_press_start = 0;     // 按键按下开始时间
unsigned int key_last_release = 0;    // 按键最近释放时间
unsigned char click_cnt = 0;          // 连击次数

解释

1.1宏定义

KEY_EVENT_*

KEY_EVENT_*:定义了按键事件的类型,包括无事件、短按、长按、双击、连击

DEBOUNCE_TIME

DEBOUNCE_TIME:按键消抖时间,避免按键抖动误触发。

HOLD_TIME

HOLD_TIME:长按判定时间,超过该时间触发长按事件。

DOUBLE_TIME

DOUBLE_TIME:双击间隔时间,两次按下之间的时间小于该值则判定为双击。

MULTI_TIME

MULTI_TIME:连击间隔时间,多次按下之间的时间小于该值则判定为连击。

1.2全局变量

Key_Val

Key_Val:当前扫描到的键值

Key_Old

Key_Old:上一次扫描到的键值,用于检测按键状态变化。

Key_Down

Key_Down:按键按下瞬间的标志

Key_Up

Key_Up:按键释放瞬间的标志

sys_tick

sys_tick:系统时间戳,由定时器中断更新,用于时间相关的逻辑判断

key_event

key_event:当前检测到的按键事件

key_pressed

key_pressed:当前按下的键值。

key_press_start

key_press_start:按键按下的起始时间

key_last_release

key_last_release:按键最近一次释放的时间

click_cnt

click_cnt:连击次数计数器

2. 定时器初始化(1ms中断)

void Timer0_Init() {AUXR |= 0x80;       // 定时器0为1T模式TMOD &= 0xF0;       // 设置定时器模式TL0 = 0xCD;         // 1ms定时初值(11.0592MHz)TH0 = 0xD4;TF0 = 0;            // 清除标志TR0 = 1;            // 启动定时器ET0 = 1;            // 使能中断EA = 1;             // 全局中断使能
}// 定时器0中断服务函数
void timer0_isr() interrupt 1 {sys_tick++;         // 系统时间戳自增
}

解释

2.1定时器初始化

配置定时器0为1T模式(1个时钟周期计数一次),设置初值以实现1ms定时。

启动定时器并开启中断。

2.2中断服务函数

1ms进入一次中断,sys_tick自增,用于记录系统运行时间。

3. 按键扫描与状态机修改

// 原有扫描函数(返回键值1~19,0表示无按键)
unsigned char ScanKey() {unsigned char temp = 0;P44=0; P42=P35=P34=1;if(P33==0) temp=4;  if(P32==0) temp=5;if(P31==0) temp=6;  if(P30==0) temp=7;P42=0; P44=P35=P34=1;if(P33==0) temp=8;  if(P32==0) temp=9;if(P31==0) temp=10; if(P30==0) temp=11;P35=0; P44=P42=P34=1;if(P33==0) temp=12; if(P32==0) temp=13;if(P31==0) temp=14; if(P30==0) temp=15;P34=0; P44=P42=P35=1;if(P33==0) temp=16; if(P32==0) temp=17;if(P31==0) temp=18; if(P30==0) temp=19;return temp;
}// 按键处理主逻辑(循环调用)
void Key_Loop() {Key_Val = ScanKey();Key_Down = Key_Val & (Key_Old ^ Key_Val);  // 检测下降沿Key_Up = ~Key_Val & (Key_Old ^ Key_Val);   // 检测上升沿Key_Old = Key_Val;// 无按键时重置状态if (Key_Val == 0) {if (key_pressed != 0) {// 记录释放时间并计算连击if (sys_tick - key_last_release < MULTI_TIME) {click_cnt++;} else {click_cnt = 1;  // 超过连击间隔则重置}key_last_release = sys_tick;key_pressed = 0;}return;}// 处理按键按下if (Key_Down != 0) {key_pressed = Key_Val;          // 记录当前按键key_press_start = sys_tick;    // 记录按下时间click_cnt = 0;                 // 连击计数重置}// 处理长按检测if (key_pressed != 0 && (sys_tick - key_press_start > HOLD_TIME)) {key_event = KEY_EVENT_LONG;     // 触发长按事件key_pressed = 0;               // 长按后标记处理完成}// 处理按键释放if (Key_Up != 0 && key_pressed != 0) {// 短按判定(未达到长按时间)if (sys_tick - key_press_start < HOLD_TIME) {// 双击检测:在DOUBLE_TIME内检测第二次按下if (sys_tick - key_last_release < DOUBLE_TIME) {key_event = KEY_EVENT_DOUBLE;} else {key_event = KEY_EVENT_SHORT;}}}// 连击检测(需配合释放后的时间窗口)if (click_cnt >= 2) {key_event = KEY_EVENT_MULTI;click_cnt = 0;  // 连击后重置}
}

解释

3.1ScanKey函数

扫描矩阵键盘,返回当前按下的键值**(1~19)**,0表示无按键。

3.2Key_Loop函数

  1. 检测按键按下和释放的边沿(Key_Down和Key_Up)。

  2. 无按键时,记录释放时间并计算连击次数

  3. 按键按下时,记录按下的键值和时间

  4. 长按检测:按下时间超过HOLD_TIME则触发长按事件

  5. 短按和双击检测:在释放时判断按下时间是否小于HOLD_TIME,并结合DOUBLE_TIME判断是否为双击

  6. 连击检测:在MULTI_TIME内多次按下同一键则触发连击事件。

4. 主函数使用示例

void main() {Timer0_Init();      // 初始化定时器while (1) {Key_Loop();     // 循环调用按键检测// 处理按键事件switch (key_event) {case KEY_EVENT_SHORT:printf("Key %d: 短按\r\n", key_pressed);key_event = KEY_EVENT_NONE;break;case KEY_EVENT_LONG:printf("Key %d: 长按\r\n", key_pressed);key_event = KEY_EVENT_NONE;break;case KEY_EVENT_DOUBLE:printf("Key %d: 双击\r\n", key_pressed);key_event = KEY_EVENT_NONE;break;case KEY_EVENT_MULTI:printf("Key %d: 连击%d次\r\n", key_pressed, click_cnt + 1);key_event = KEY_EVENT_NONE;break;}}
}

解释

  1. 初始化定时器并进入主循环。

  2. 调用Key_Loop检测按键事件。

  3. 根据key_event的值处理不同的按键事件,并输出相应的信息。

5. 关键逻辑说明

5.1消抖处理

消抖处理:通过Key_Down和Key_Up检测边沿,隐式消抖。

5.2长按检测

长按检测:按下时间超过HOLD_TIME触发长按事件

5.3双击检测

双击检测:在DOUBLE_TIME内检测第二次按下

5.4连击检测

连击检测:在MULTI_TIME检测多次按下

6. 优化建议

6.1矩阵键盘冲突

矩阵键盘冲突:改进ScanKey函数以支持多键同时按下

6.2低功耗优化

低功耗优化:在空闲时进入休眠模式,通过按键唤醒。

6.3事件调回

事件回调:通过函数指针绑定事件处理函数,提高代码灵活性。


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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LabVIEW烟气速度场实时监测

本项目针对燃煤电站烟气流速实时监测需求&#xff0c;探讨了静电传感器结构与速度场超分辨率重建方法&#xff0c;结合LabVIEW多板卡同步采集与实时处理技术&#xff0c;开发出一个高效的烟气速度场实时监测系统。该系统能够在高温、高尘的复杂工况下稳定运行&#xff0c;提供高…

【系统架构设计师】操作系统 - 特殊操作系统 ③ ( 微内核操作系统 | 单体内核 操作系统 | 内核态 | 用户态 | 单体内核 与 微内核 对比 )

文章目录 一、微内核操作系统1、单体内核 操作系统2、微内核操作系统 引入3、微内核操作系统 概念4、微内核操作系统 案例 二、单体内核 与 微内核 对比1、功能对比2、单体内核 优缺点3、微内核 优缺点 一、微内核操作系统 1、单体内核 操作系统 单体内核 操作系统 工作状态 : …

人工智能之数学基础:线性方程组

本文重点 线性方程组是由两个或两个以上的线性方程组成的方程组,其中每个方程都是关于两个或两个以上未知数的线性方程。 记忆恢复 我们先从小学学习的线性方程组找到感觉 解答过程: 将第二个方程乘以2,得到: 2x−2y=2 将第一个方程减去新得到的方程,消去x: (2x+y)−…

​第十一届传感云和边缘计算系统国际会议

重要信息 时间地点&#xff1a;2025年4月18-20日 中国-珠海 会议官网&#xff1a;www.scecs.org 简介 第十一届传感云和边缘计算系统 (SCECS 2025&#xff09;将于2025年4月18-20日在中国珠海召开。将围绕“传感云”、“边缘计算系统”的最新研究领域&#xff0c;为来自国…

MDM设备管控,企业移动设备管理方案

目录&#xff1a; 目录 目录&#xff1a; 1. MDM&#xff1a;含义与定义 2. MDM如何工作&#xff1f; 3. BYOD与MDM&#xff1a;挑战与解决方案 4. 移动设备管理的主要优势 5. 移动设备管理的基本要素 6. 移动设备管理最佳实践 --地平线-- 移动设备管理 (MDM)历经多年…

S32k3XX MCU时钟配置

今天想从头开始配置S32K312中EB中的MCU模块&#xff0c;以下是我的配置思路与理解。 关键是研究明白&#xff0c;这些频率是如何通过一个总时钟&#xff0c;一步步分频得到的。 参考时钟&#xff0c;供外设模块使用&#xff0c;不同外设需要配置合理的参考时钟。 clock genera…

GitHub 超火的开源终端工具——Warp

Warp 作为近年来 GitHub 上备受瞩目的开源终端工具&#xff0c;以其智能化、高性能和协作能力重新定义了命令行操作体验。以下从多个维度深入解析其核心特性、技术架构、用户评价及生态影响力&#xff1a; 一、背景与核心团队 Warp 由前 GitHub CTO Jason Warner 和 Google 前…

SpringBoot 第二课(Ⅰ) 整合springmvc(详解)

目录 一、SpringBoot对静态资源的映射规则 1. WebJars 资源访问 2. 静态资源访问 3. 欢迎页配置 二、SpringBoot整合springmvc 概述 Spring MVC组件的自动配置 中央转发器&#xff08;DispatcherServlet&#xff09; 控制器&#xff08;Controller&#xff09; 视图解…

八股学习-JUC java并发编程

本文仅供个人学习使用&#xff0c;参考资料&#xff1a;JMM&#xff08;Java 内存模型&#xff09;详解 | JavaGuide 线程基础概念 用户线程&#xff1a;由用户空间程序管理和调度的线程&#xff0c;运行在用户空间。 内核线程&#xff1a;由操作系统内核管理和调度的线程&…

C++基础 [八] - list的使用与模拟实现

目录 list的介绍 List的迭代器失效问题 List中sort的效率测试 list 容器的模拟实现思想 模块分析 作用分析 list_node类设计 list 的迭代器类设计 迭代器类--存在的意义 迭代器类--模拟实现 模板参数 和 成员变量 构造函数 * 运算符的重载 运算符的重载 -- 运…

VScode的debug

如果有命令行参数的话&#xff1a; 打开调试配置&#xff1a; 在 VS Code 中&#xff0c;按下Ctrl Shift D打开调试面板。点击面板顶部的齿轮图标&#xff0c;选择“添加配置…” (Add Configuration...)。 创建新的调试配置&#xff1a; 选择Python&#xff0c;然后选择…

工作记录 2017-02-08

工作记录 2017-02-08 序号 工作 相关人员 1 修改邮件上的问题。 更新RD服务器。 郝 更新的问题 1、CPT的录入页面做修改 1.1、Total 改为 Price 1.2、当删除行时&#xff0c;下面的行自动上移。 2、Pending Payments、Payment Posted、All A/R Accounts页面加了CoIns…

Java SE 面经

1、Java 语言有哪些特点 Java 语言的特点有&#xff1a; ①、面向对象。主要是&#xff1a;封装&#xff0c;继承&#xff0c;多态。 ②、平台无关性。一次编写&#xff0c;到处运行&#xff0c;因此采用 Java 语言编写的程序具有很好的可移植性。 ③、支持多线程。C 语言没…

springboot基于session实现登录

文章目录 1.理解session2.理解ThreadLocal2.1 理解多线程2.2 理解lambda表达式2.3 ThreadLocal 3.基于session登录流程图4.具体登录的代码实现4.1短信发送功能4.2 短信验证码登录注册功能4.登录校验功能4.1 配置登录拦截器LoginInterceptor4.1.1 ThrealLocal类实现 4.2登录拦截…

【ArduPilot】Windows下使用Optitrack通过MAVProxy连接无人机实现定位与导航

Windows下使用Optitrack通过MAVProxy连接无人机实现定位与导航 配置动捕系统无人机贴动捕球配置无人机参数使用MAVProxy连接Optitrack1、连接无人机3、设置跟踪刚体ID4、校正坐标系5、配置IP地址&#xff08;非Loopback模式&#xff09;6、启动动捕数据推流 结语 在GPS信号弱或…

MSys2统一开发环境,快速搭建windows opencv环境

文章目录 摘要下载msys2安装Mingw64安装Cmake安装opencv报错一报错二问题一 摘要 本篇基于之前发布的opencv两篇文章,进行的流程简化,旨在优化windows opencv环境和实例运行&#xff0c;Msys2统一开发环境&#xff0c;有利于长远的开发环境&#xff0c;也简化了后续集成的难度…

基于单片机的多功能热水器设计(论文+源码)

1系统方案设计 基于单片机的多功能热水器系统&#xff0c;其系统框图如图2.1所示。主要采用了DS18B20温度传感器&#xff0c;HC-SR04超声波模块&#xff0c;STC89C52单片机&#xff0c;液晶&#xff0c;继电器等来构成整个系统。硬件上主要通过温度传感器进行水温的检测&am…

详解Sympy:符号计算利器

Sympy是一个专注于符号数学计算的数学工具&#xff0c;使得用户可以轻松地进行复杂的符号运算&#xff0c;如求解方程、求导数、积分、级数展开、矩阵运算等。其中比较流行的深度学习框架pytorch的用到了Sympy,主要用于将模型的计算图转换为符号化表达式&#xff0c;以便进行分…

《Python实战进阶》No27: 日志管理:Logging 模块的最佳实践(上)

No27: 日志管理&#xff1a;Logging 模块的最佳实践(上) 摘要 日志记录是软件开发中不可或缺的一部分&#xff0c;尤其是在复杂的生产环境中。Python 的内置 logging 模块提供了强大的工具来管理和记录程序运行中的各种信息。本集将深入探讨 logging 模块的核心概念&#xff0…

每日Attention学习27——Patch-based Graph Reasoning

模块出处 [NC 25] [link] Graph-based context learning network for infrared small target detection 模块名称 Patch-based Graph Reasoning (PGR) 模块结构 模块特点 使用图结构更好的捕捉特征的全局上下文将图结构与特征切片(Patching)相结合&#xff0c;从而促进全局/…