蓝桥杯电子类嵌入式(STM32G431)备赛学习记录(三)——定时器+按键

03、定时器+按键

按键需要消抖,用软件延时消抖会占用整个资源,导致整个程序卡死。反之,用定时器则可以提高资源利用率。用轮询的方式扫描按键,用多状态的思想消除按键抖动感觉非常巧妙。
首先将上一个例程文件复制一份再命名为Ag_02,在bsp文件夹中创建interrupt.c和interrupt.h。打开.ioc文件,参考芯片数据手册对相应GPIO口配置为input并设置为上拉模式。
在这里插入图片描述
在这里插入图片描述
配置定时器,随便选TIM几,将时钟源选定为内部时钟,将分频系数设置为80-1(在第一个博客中配置时钟树时将频率配置为80MHz),得到定时器工作频率为1MHz,将Counter设置为10000-1得到定时频率为100Hz(定时间隔10ms)(定时器工作频率=外部总线频率除以Prescaler)(定时频率=定时器工作频率除以Counter)
在这里插入图片描述
在NVIC栏勾选TIM2使能定时器中断。点击生成代码。
在这里插入图片描述
将interrupt.c包含进工程文件。
interrupt.h:

#ifndef _INTERRUPT_H
#define _INTERRUPT_H

#include "main.h"
#include "stdbool.h"    //后面定义的布尔类型需要包含这个头文件
struct KEY_STATE    //创建一个结构体,用来查看按键的状态
{
	bool flag;			//按键按下与否标志位
	bool state;			//按键状态(不确定)
	uint judge;			//步骤
	
};
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef * htim);//定时器溢出中断回调函数

#endif

interrupt.c:

#include "interrupt.h"

struct KEY_STATE key_state[4]={0};		//定义一个KEY_STATE类型的结构体数组,共四个按键
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef * htim) //调用HAL库函数,定时器中断回调函数
{
	if(htim->Instance==TIM2)   //判断是否是由TIM2引发的中断
	{
		key_state[0].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
		key_state[1].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
		key_state[2].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
		key_state[3].state=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);  //将引脚电位读到state变量
		
		for(int j=0;j<4;j++)     //轮询扫描按键
		{
			switch(key_state[j].judge)		 //设置三个步骤来消抖
			{
				case 0:
				{
					if(key_state[j].state==0)			//一开始检测到按键按下
						key_state[j].judge=1;				//进入状态2
				}
				break;
				
				case 1:
				{
					if(key_state[j].state==0)				//仍为按下
					{
						key_state[j].flag=1;					 //状态标志位置1
						key_state[j].judge=2;				//进入状态3
					}
					else
						key_state[j].judge=0;
				}
				break;
				
				case 2:
				{
					if(key_state[j].state==1)				//松开按键
						key_state[j].judge=0;				//回到状态1
				}
				break;
			}
		}
	}
}

在主函数while中加入功能代码:

		if(key_state[0].flag==1)
		{
			LCD_DisplayStringLine(Line1,(unsigned char *)dis);
			key_state[0].flag=0;
		}
		
				if(key_state[1].flag==1)
		{
			LCD_DisplayStringLine(Line2,(unsigned char *)dis);
			key_state[1].flag=0;
		}
		
				if(key_state[2].flag==1)
		{
			LCD_DisplayStringLine(Line3,(unsigned char *)dis);
			key_state[2].flag=0;
		}
		
				if(key_state[3].flag==1)
		{
			LCD_DisplayStringLine(Line4,(unsigned char *)dis);
			key_state[3].flag=0;
		}

要注意的是,我们在interrupt.c中定义的结构体及其参量要想在主函数中调用的话,需要加:

/* USER CODE BEGIN PTD */
extern struct KEY_STATE key_state[];
/* USER CODE END PTD */

同时,不要忘记在主函数中打开定时器中断:

HAL_TIM_Base_Start_IT(&htim2);

编译无误,下载完成!

长按键

蓝桥杯有时会考到长按键和短按键的设计。我们可以在上述基本按键功能的基础上,增加一个变量来记录按下的时间,以此区分长按键和短按键的不同功能。
interrupt.c:

#include "interrupt.h"

struct KEY_STATE key_state[4]={0};		//定义一个KEY_STATE类型的结构体数组,共四个按键
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef * htim) //调用HAL库函数,定时器中断回调函数
{
	if(htim->Instance==TIM2)   //判断是否是由TIM2引发的中断
	{
		key_state[0].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
		key_state[1].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
		key_state[2].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
		key_state[3].state=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);  //将引脚电位读到state变量
		
		for(int j=0;j<4;j++)     //轮询扫描按键
		{
			switch(key_state[j].judge)		 //设置三个步骤来消抖
			{
				case 0:
				{
					if(key_state[j].state==0)			//一开始检测到按键按下
					{
						key_state[j].judge=1;				//进入状态2
						key_state[j].time=0;
					}
				}
				break;
				
				case 1:
				{
					if(key_state[j].state==0)				//仍为按下
					{
						key_state[j].judge=2;				//进入状态3
					}
					else
						key_state[j].judge=0;
				}
				break;
				
				case 2:
				{
					if(key_state[j].state==1)				//松开按键
					{
						key_state[j].judge=0;				//回到状态1
						if(key_state[j].time<70)		//判断是否按下时间小于700ms
						{
							key_state[j].short_flag=1;		//短按键
						}
					}
					else
					{
						key_state[j].time++;
						if(key_state[j].time>70)
						{
							key_state[j].long_flag=1;			//长按键
						}
					}
				}
				break;
			}
		}
	}
}

记得在.h文件结构体中增加相应的变量,主函数也相应修改功能为长短按键逻辑判读:

		if(key_state[0].short_flag==1)
		{
			sprintf(dis,"short");
			LCD_DisplayStringLine(Line1,(unsigned char *)dis);
			key_state[0].short_flag=0;
		}
		
		if(key_state[0].long_flag==1)
		{
			sprintf(dis,"long");
			LCD_DisplayStringLine(Line1,(unsigned char *)dis);
			key_state[0].long_flag=0;
		}

编译无误,下载完成!

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值