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

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

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

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

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

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

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


目录

原理图解析

设置STM32CubeMX

按键配置

定时器配置

手搓代码

中断回调(服务)函数

 按键判断函数

按键单击判断函数

按键双击判断函数

按键长按判断函数


原理图解析

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

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

设置STM32CubeMX

按键配置

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

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

定时器配置

关于定时器的详细知识点与解析可前往 【蓝桥杯嵌入式】STM32定时器的配置,解析预分频系数和重装载值与时钟频率的关系 这篇博客,在这里我们只讲定时器的应用

如果大家有去做过省赛或国赛题目,就都会看到过对按键响应时间是有要求的,一般都是响应时间在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;
			}
		}
	}
}

### 蓝桥杯嵌入式项目中按键按和短按的功能实现蓝桥杯嵌入式的项目开发中,按键按与短按功能可以通过定时器配合软件逻辑来实现。以下是具体的实现方式: #### 变量定义 为了实现按键检测以及区分按与短按的操作,需要定义以下几个关键变量: - `u8 KEY_Flag`:用于标记当前按键的状态,例如是否有按键被按下或者释放。 - `char num`:记录按键触发的具体次数或其他状态信息。 - `u32 TimingDelay`:作为延时计数器,通常由系统滴答定时器(SysTick)驱动,用来计算按键持续时间。 这些变量的作用在于跟踪按键的时间度并决定其行为模式[^1]。 #### 函数设计 程序的核心部分涉及两个主要方面:硬件处理和事件判定逻辑。具体如下: ##### 定时器初始化 利用STM32微控制器中的SysTick定时器设置固定周期中断服务例程(ISR),每次进入ISR都会更新全局变量`TimingDelay++`以便后续使用该值评估按键保持多久未发生变化[^2]。 ```c void SysTick_Init(void){ SysTick->LOAD = SystemCoreClock / 1000 - 1; /* 设置重装载寄存器 */ SysTick->VAL = 0x00; /* 清空当前值寄存器 */ SysTick->CTRL |= SysTick_CTRL_CLKSOURCE_Msk |/* 使用处理器时钟源 */ SysTick_CTRL_TICKINT_Msk |/* 开启SYSTICK异常请求 */ SysTick_CTRL_ENABLE_Msk; /* 启动SYSTICK */ } ``` 上述代码片段展示了如何配置SysTick以每毫秒产生一次中断。 ##### 按键扫描函数 编写一个循环调用的按键读取子过程,在其中加入必要的延迟机制防止误判,并依据累积起来的时间间隔辨别属于哪种类别的操作—即瞬间轻触还是较时间维持接触状态下的响应动作。 当检测到某个特定开关处于闭合位置超过预设阈值,则认定为“按”,反之如果迅速放开则视为“短按”。下面给出简化版伪码描述这一流程控制结构: ```c if (KEY_Read() == KeyPressed && !KEY_Flag){ TimingStart(); // 记录起始时刻 while(KEY_Read()==KeyPressed); // 等待松开 if(GetElapsedTime()>LONG_PRESS_TIME) { HandleLongPress(); }else{ HandleShortPress(); } } ``` 此段落说明了基本原理框架下如何捕捉不同类型的输入信号转换成相应的应用程序接口(API). 综上所述, 结合以上提到的技术要点可以有效地达成题目所要求的目标.
评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

勾栏听曲_0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值