【蓝桥杯嵌入式】定时器实现按键单击,双击,消抖以及长按的代码实现

news2024/11/15 12:22:53

🎊【蓝桥杯嵌入式】专题正在持续更新中,原理图解析✨,各模块分析✨以及历年真题讲解✨都在这儿哦,欢迎大家前往订阅本专题,获取更多详细信息哦🎏🎏🎏

🪔本系列专栏 -  蓝桥杯嵌入式_勾栏听曲_0的博客

🍻欢迎大家  🏹  点赞👍  评论📨  收藏⭐️

📌个人主页 - 勾栏听曲_0的博客📝

🔑希望本文能对你有所帮助,如有不足请指正,共同进步吧🏆

🎇立班超志,守苏武节,歌武穆词,做易水别。📈


目录

原理图解析

设置STM32CubeMX

按键配置

定时器配置

手搓代码

中断回调(服务)函数

 按键判断函数

按键单击判断函数

按键双击判断函数

按键长按判断函数


原理图解析

我们以PB1为例来分析,假如按键没有被按下,那么PB1的电平就与左上角的VDD相等,也就是PB1 = 1;如果按键被按下,那么右下角的接地就会被导通,PB1的电平3就与GND相等,也就是PB1 = 2。

这样我们就能通过以上原理对按键进行判断,但是按键判断是一个事件触发程序,所以我们要使用定时器来使开发板能在任意时间都能对按键进行判断。因此我们再下一步设置STM32CubeMX中需要对定时器初始化。

设置​​​​​​​STM32CubeMX

按键配置

首先我们先根据按键的原理图配置好引脚,需要注意的是,再前几篇文章中讲LCD与LED我们讲引脚都是设置为GPIO_Output,但是按键的四个引脚,我们需要设置为GPIO_input,如图:

 然后在左侧选择GPIO中的按键的四个引脚,上下拉模式设为上拉,为以下状态:

定时器配置

关于定时器的详细知识点与解析可前往蓝桥杯嵌入式这篇博客,在这里我们只讲定时器的应用

如果大家有去做过省赛或国赛题目,就都会看到过对按键响应时间是有要求的,一般都是响应时间在0.1秒内,所以我们的定时器可以就设置为0.01秒。

以下是使能中断 

 

手搓代码

中断回调(服务)函数

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);

以下就是中断回调函数,很长,记不住怎么办。别担心,还有办法,我们打开keil5,在Project下找到stm32g4xx_it.c,在里面找到stm32g4xx_hal-tim.h并打开他,翻到最下面,再往上滑一点点,大概两千五百多行的位置,就可以找到这个函数啦,我们就可以直接复制使用。

 

 按键判断函数

实现创建一个按键的结构体,其含义写在注释中了:

struct keys
{
	uchar judge_sta;	//判断按键按键按下的动作到了第几步
	bool key_sta;		//如果按键被按下,为0
	bool key_flag;	//如果确认被按下,为1
};

 具体实现按键判断函数的思路:

1.判断中断回调函数收到的中断信号是不是我们刚刚给按键设置的定时器3的信号,如果是就进入按键判断函数

2.读取每个按键这一时刻的电平

3.判断有哪些按键为按下的状态(低电平)

4.按键抖动判断

5.状态重置

按键单击判断函数

具体实现代码如下,代码中也有详细注释,希望能有所帮助:

struct keys key[4]={0,0,0,0};

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM3)			//判断中断信号是否来自定时器3
	{
		key[0].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);		//读按键PB0现在的状态,如果被按下,PB0  = 0;如果没有被按下,PB0 = 1;
		key[1].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
		key[2].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
		key[3].key_sta=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
		
		for(int i = 0;i < 4; i++ )		//确认是哪个或哪些按键被按下了
		{
			switch (key[i].judge_sta)
			{
				case 0:
				{
					if(key[i].key_sta==0) key[i].judge_sta = 1;	//第一次判断是否按下
				}
				break;
				case 1:
				{
					if(key[i].key_sta==0)	//进入下一次定时器扫描,按键还是按下状态,那么就确认为按下,以此来消抖
					{
						key[i].judge_sta = 2;
						key[i].key_flag = 1;
					}
					else		//否则就是抖动,本次不算按键被按下
						key[i].judge_sta = 0;
				}
				break;
				case 2:
				{
					if(key[i].key_sta==1) key[i].judge_sta = 0;	//判断是否松手,松手后按键状态重置
				}
				break;
			}
		}
	}
}

按键双击判断函数

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM3)			//判断中断信号是否来自定时器3
	{
		key[0].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);		//读按键PB0现在的状态,如果被按下,PB0  = 0;如果没有被按下,PB0 = 1;
		key[1].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
		key[2].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
		key[3].key_sta=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
		
		for(int i = 0;i < 4; i++ )		//确认是哪个或哪些按键被按下了
		{
			switch (key[i].judge_sta)
			{
				case 0:
				{
					if(key[i].key_sta==0) key[i].judge_sta = 1;	//第一次判断是否按下
				}
				break;
				case 1:
				{
					if(key[i].key_sta==0)	//进入下一次定时器扫描,按键还是按下状态,那么就确认为按下,以此来消抖
					{
						if(a == i && key[a].key_time < 70)	//小于70,说明上次按下后到这次按下时间间隔小于0.7秒
						{
							key[i].double_key_flag = 1;		//这是一次双击事件
						}
						else
						{
							key[i].key_flag = 1;
							a = i;							//记录这一次是上面按键被按下
						}
						key[i].judge_sta = 2;
					}
					else		//否则就是抖动,本次不算按键被按下
						key[i].judge_sta = 0;
				}
				break;
				case 2:
				{
					if(key[i].key_sta==1) key[i].judge_sta = 0;	//判断是否松手,松手后按键状态重置
					key[i].key_time = 0;
				}
				break;
			}
		}
		key[a].key_time++;		//第一次被按下之后,开始计时
	}
}

按键长按判断函数

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM3)
	{
		key[0].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
		key[1].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
		key[2].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
		key[3].key_sta=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
		
		for(int i = 0;i < 4; i++ )
		{
			switch (key[i].judge_sta)
			{
				case 0:
				{
					if(key[i].key_sta==0) 
					{
						key[i].judge_sta = 1;	//第一次判断是否按下
						key[i].key_time = 0;
					}
				}
				break;
				case 1:
				{
					if(key[i].key_sta==0)	//进入下一次定时器扫描,按键还是按下状态,那么就确认为按下,以此来消抖
					{
						key[i].judge_sta = 2;
					}
					else
						key[i].judge_sta = 0;
				}
				break;
				case 2:
				{				
					if(key[i].key_sta==1) 		//判断是否松手
					{
						if(key[i].key_time < 100)
						{
							key[i].key_flag = 1;
						}
//						if(key[i].key_time > 100)				//一次扫描10毫秒,100次1000毫秒,就是判断是否长按超过1000毫秒
//																						//松手后,才会执行相应反应
//						{
//							key[i].long_flag = 1;
//						}
						key[i].judge_sta = 0;		
					}
					else
					{
						key[i].key_time++;
						if(key[i].key_time > 100)				//一次扫描10毫秒,100次1000毫秒,就是判断是否长按超过1000毫秒//未松手时,就会执行相应反应
						{
							key[i].long_flag = 1;
						}
					}
				}
				break;
			}
		}
	}
}

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

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

相关文章

盘点一下那些远程办公的神仙公司

其实远程办公已经有50多年的历史了&#xff0c;这几年&#xff0c;这种工作方式越来越受到大家的喜欢&#xff0c;对于员工来说&#xff0c;工作效率可以大幅提高&#xff0c;节省下来的通勤时间和成本&#xff0c;有更多的时间花在工作上。可以更好的平衡工作与生活。对于公司…

最近我的视频播放浅学总结

因为想做一个类似苹果的同播共享功能&#xff0c;这一段时间对音视频做了一些浅浅的学习&#xff0c;现简单总结记录。 我的需求是找到一个尽可能简单的方案来两人播放一段视频&#xff0c;并且能够进度和操作同步&#xff0c;所以基本不能有延迟&#xff0c;同时能够显示WebV…

12.1 基于Django的服务器信息查看应用(系统信息、用户信息)

文章目录新建Django项目创建子应用并设置本地化创建数据库表创建超级用户git管理项目&#xff08;requirements.txt、README.md、.ignore&#xff09;主机信息监控应用的框架搭建具体功能实现系统信息展示前端界面设计视图函数设计用户信息展示视图函数设计自定义过滤器的实现前…

华为OD机试用Python实现 -【广播服务器】

华为OD机试题 最近更新的博客华为 OD 机试 300 题大纲广播服务器题目输入输出示例一输入输出示例二输入输出Python代码代码编写思路最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题

常见的电脑运行卡顿原因及解决方法

大家在日常使用电脑过程中&#xff0c;会发现多开几个文件就卡顿&#xff0c;其实很多时候都跟C盘长期不清理有关&#xff0c;C盘的内存被下载的软件安装包、页面文件、休眠文件、更新文件等一系列的文件占据。大的文件甚至能占到20-30G&#xff0c;驱动人生就为大家带来几种解…

App防抓包的四种绕过方法(详细)

App防抓包的四种绕过方法简介&#xff1a;1、ssl证书校验&#xff08;https证书校验http请求ssl证书校验&#xff09;方法演示&#xff1a;安卓5.0怎么安装证书演示&#xff1a;安卓7及以上怎么把证书安卓到系统目录2、代理屏蔽3、证书绑定 &#xff08;SSL pinning&#xff09…

Easyrecovery数据恢复软件工作原理及使用介绍教程

Easyrecovery是一款强大的数据恢复软件&#xff0c;它专门解决磁盘数据恢复问题。在计算机世界里&#xff0c;数据丢失经常是一件令人头疼的事情&#xff0c;但是有了Easyrecovery&#xff0c;您可以放心大胆地享受数据备份和恢复的乐趣。EasyRecovery使用Ontrack公司复杂的模式…

(JUC)核心线程 和 救急线程的区别;Executors-固定大小线程池单线程线程池

核心线程 和 救急线程的区别 救急线程是有个生存时间的&#xff0c;它执行完任务了&#xff0c;过了一段时间&#xff0c;没有新任务了&#xff0c;救急线程就会销毁掉&#xff0c;变成结束的状态 核心线程没有生存时间&#xff0c;它执行完任务后&#xff0c;它仍然会被保存…

人机交互(软件工程视角)第一、二章部分题目答案

我认为日常生活中&#xff0c;我们学校的选课系统就在选课的时候就很不方便&#xff0c;具体是这样的&#xff0c;因为本来我们学校的选课的时候服务器负载能力就比较差&#xff0c;大家着急忙慌地选课的时候&#xff0c;很容易因为界面选课控件比较小&#xff0c;从而直接点击…

软测入门(四)Appium-APP移动测试基础

Appium 用来测试手机程序。 测试方面&#xff1a; 功能测试安装卸载测试升级测试兼容测试 Android系统版本不同分辨率不同网络 网络切换、中断测试使用中来电话、短信横竖屏切换 环境搭建 Java安装&#xff08;查资料&#xff09;Android SDK安装&#xff0c;配置 HOME和P…

FPGA纯verilog手写HDMI发送IP 提供源码和技术支持

目录1、前言2、设计思路和框架TMDS 编码算法OSERDESE串并转换3、顶层源码和IP封装4、源码和IP获取1、前言 本设计使用Xilinx原语和自己手写的代码实现了HDMI发送功能&#xff0c;纯verilog手写&#xff0c;有源码&#xff0c;也提供封装好的IP&#xff0c;你喜欢用例化的方式就…

WebRTC 系列之视频辅流

WebRTC 中的 SDP 支持两种方案&#xff1a; PlanB 方案 和 Unified Plan 方案。早期我们使用多PeerConnection的 Plan B 方案中只支持一条视频流发送&#xff0c;这条视频流&#xff0c;我们称之为”主流”。目前我们使用单 PeerConnection 的 Unified Plan 方案&#xff0c;新…

二叉树——把二叉搜索树转换为累加树

538. 把二叉搜索树转换为累加树 链接 给出二叉 搜索 树的根节点&#xff0c;该树的节点值各不相同&#xff0c;请你将其转换为累加树&#xff08;Greater Sum Tree&#xff09;&#xff0c;使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。 提醒一下&#xf…

春天到了,来一场 VoxEdit 创作大赛吧!

春天的气息扑面而来&#xff0c;这是让你尽情绽放创造力的最佳时机&#xff01;我们将以「春天」为主题来一场 VoxEdit 大赛。在这里&#xff0c;你可以展示你的才华并赢得 $SAND 奖励&#xff01; 无论你是专业的设计师&#xff0c;还是仅仅喜欢创造美丽的艺术&#xff0c;这场…

有趣的阻抗变换

阻抗变换在很多人看来很神秘&#xff0c;甚至不可理喻&#xff1a; “什么是匹配网络&#xff1f;” “为什么要在负载电路之前加这么多电感电容&#xff1f;” “如果负载是100欧姆要与源阻抗50欧匹配&#xff0c;直接在负载并联一个100欧负载不就行了吗”……这样的问题常…

项目管理软件中日历的作用

为什么在项目管理软件中使用日历&#xff1f;日历是跟踪即将举行的会议、截止日期和里程碑的有用工具。它们可以帮助您可视化您的日程安排并提醒您重要事件&#xff0c;例如假期和休假时间。 虽然人们经常有各种各样的日历工具可供选择&#xff0c;包括从办公室墙上的纸质日历…

Stochastic Approximation 随机近似方法的详解之(二)Robbins-Monro Algorithm

6.2 Robbins-Monro Algorithm RM算法是随机近似领域的先驱性工作。众所周知的随机梯度下降算法是RM算法的一种特殊情况。后面我们再介绍具体的细节。 先看一个例子&#xff1a; 我们想要去求下面这个等式的根&#xff0c; BTW&#xff0c;很多问题可以被转化为求根问题。比…

嵌入式学习笔记——基于Cortex-M的单片机介绍

基于Cortex-M的单片机介绍前言生产厂商及其产品线ARM单片机的产品线命名规则留个作业习单片机的资料准备STM32开发所需手册1.芯片的数据手册作业2前言 本文继续接着上一篇中关于Cortex-M的介绍&#xff0c;来记录一些关于ARM系单片机的知识。 生产厂商及其产品线 芯片厂商在…

论坛性能测试难点有哪些?

1 测试工具方面 用户和业务模型分析搭建合适的脚本开发&#xff08;不根据用户和业务的模型来开发脚本&#xff0c;认为要回归成功即可&#xff09;合适的需求分析转化为场景设计&#xff08;不知道如何根据需求进行场景设计&#xff09;大容量系统的数据生成和使用大型系统的…

金蝶国产化中间件和人大金仓数据库

金蝶Apusic分布式消息队列不需要配置用户名密码rabbitmq:enable: truehost: 192.168.1.233port: 5672<!-- Spring Boot RabbitMQ 依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</ar…