STM32定时器实现不加延时的三种独立按键获取函数——返回1次,多次和长短按键

最近在准备蓝桥杯,虽然以前也写过按键函数,但是没保存,每次写的时候思路都不一样。这里把现在想到的思路记录一下,为大家提供参考。

使用的开发板是蓝桥杯嵌入式的STM32G431RBT6,开发环境是用的keil5+STM32CubeMX(HAL库)。

连续按键

这里的连续按键是指,按键按下后会在按下期间一直返回,可以用在快速计算中。主要思路是用定时器定10ms后改变标志位来实现去抖动。
首先定义了两个标志位的存放寄存器:

u8 key_contr=0;	//0位控制10ms,1位控制单独按键,2位控制500ms,3位控制长短按键
u8 key_tims=0;	//0位代表10ms,2位代表500ms,3位控制长短按键

定时器7(周期1ms)回调函数的内容:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)		//定时器中断里面不能加hal延时
{
	if(htim->Instance==TIM7)			//1ms进一次
	{
		static u8 tmp_10ms=0;
		static u16 tmp_500ms=0;
		
		(key_contr & 0x01)?(tmp_10ms++):(tmp_10ms = 0);//10ms计时
		if(tmp_10ms>10)
		{
			tmp_10ms = 0;
			key_tims |= 0x01;
		}
		
		(key_contr & 0x04)?(tmp_500ms++):(tmp_500ms = 0);//10ms计时
		if(tmp_500ms>500)	//用于流水灯控制
		{
			tmp_500ms = 0;
			key_tims |= 0x04;
		}
		
		
	}
}

按键扫描函数如下:

/*********************连续按键,返回多次键值(10ms)******************************/
u8 Key_Scan1(void)		 
{
	u8 key=0;			//保存键值
	
	key = (GPIOB->IDR&0x0007)|(GPIOA->IDR&0x0001)<<3;	//读取A0,B0,B1,B2,B3四个按键状态
	key ^= 0x0f;										//按键是低电平有效所以取反一下
	
	if(key)
	{
		key_contr |= 0x01;			//按下后标志位置1
		if(key && key_tims&0x01)	//按下且定时器记到10ms后进入
		{
			key_contr &= 0xfe;		//一次按键监测完成,清零标致位返回键值
			key_tims &= 0xfe;
			return key;
		}
		else						//没到10ms,程序不等待继续往下运行
			return 0;
	}
	else
	{
		key_contr &= 0xfe;			//松开或按键抖动期间,清零标致位
		return 0;
	}
}
/*************************************************************************/

单独按键

这里的单独按键是指,按下松手后返回一次键值。可用在一般的按键控制逻辑中。主要思路:结合前面的连续按键定时器去抖动,增加一个标志位控制只返回一次键值。

/*********************单独按键,松手后返回一次键值******************************/
u8 Key_Scan2(void)		//单独按键,返回键值
{
	u8 key=0;			//保存键值
	static u8 key_temp=0;		//储存获取到的次数,用于标记按下的时间,理论上是10ms加一
	
	key = (GPIOB->IDR&0x0007)|(GPIOA->IDR&0x0001)<<3;	//读取A0,B0,B1,B2,B3四个按键状态
	key ^= 0x0f;							//按键是低电平有效所以取反一下
	
	if(key)
	{
		key_contr |= 0x01;			//按下后10ms判断标志位置1
		if(key && key_tims&0x01)	//按下且定时器记到10ms后进入
		{
			key_contr &= 0xfe;		//获取到一次有效按键,清零10ms判断标志位
			key_tims &= 0xfe;
			key_contr |= 0x02;		//打开单独按键控制标志位
			key_temp = key;			//储存键值,松手后返回
		}
		else
			return 0;
	}
	else 						
	{	
		if(key_contr&0x02)		//按下后松开
		{
			key_contr &= ~0x03;	//清除单独按键控制标志位
			return key_temp;	//松手后返回键值
		}
		else					//未按下
		{
			key_contr &= 0xfe;	//松开或按键抖动期间,清零标致位
			return 0;
		}
	}
	return 0;
}
/*************************************************************************/

长短按键

这里的长短按键指,按下时间少于2S只返回一次键值,大于两秒后和连续按键一样,可用在输入比较大的数字的时候。主要思路:通过检测到的连续按键次数,大于设定值后一直检测到一次连续按键返回一次键值,小于的时候用标志位控制只返回一次键值。

/*********************长短按(用于实现长按2s以上连续加,2s以下加一次),返回按键值******************************/
u8 Key_Scan3(void)
{
	static u16 key_num=0;
	u8 key1=0,key2=0;
	
	key1 = (GPIOB->IDR&0x0007)|(GPIOA->IDR&0x0001)<<3;	//读取A0,B0,B1,B2,B3四个按键状态
	key1 ^= 0x0f;			//按键是低电平有效所以取反一下
	
	if(!key1)			//未按下清除计数和标志位
	{
		key_contr &= ~0x08;
		key_num = 0;
	}
	key2 = Key_Scan1();		//检测是否有有效按键
	if(key2)				//按下
	{
		key_num++;			//计数
		if(key_num>100)		//超过100后一直返回键值
			return key2;
		else if(!(key_contr & 0x08))	//小于100时,只返回1次键值
		{
			key_contr |= 0x08;		//标志已返回过键值
			return key2;
		}
		else				//按下返回过一次键值后一直返回0
			return 0;
	}
	else					//未按下
		return 0;
}
/*************************************************************************/

总结

感觉写的比较冗余,还可以在改进一下,欢迎大家批评指正!(0_0)!
上传不了视频就放个动态图吧…:在这里插入图片描述

版权声明:本文为CSDN博主「《CHANGE》」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_43698771/article/details/122565480

最近在准备蓝桥杯,虽然以前也写过按键函数,但是没保存,每次写的时候思路都不一样。这里把现在想到的思路记录一下,为大家提供参考。

使用的开发板是蓝桥杯嵌入式的STM32G431RBT6,开发环境是用的keil5+STM32CubeMX(HAL库)。

连续按键

这里的连续按键是指,按键按下后会在按下期间一直返回,可以用在快速计算中。主要思路是用定时器定10ms后改变标志位来实现去抖动。
首先定义了两个标志位的存放寄存器:

u8 key_contr=0;	//0位控制10ms,1位控制单独按键,2位控制500ms,3位控制长短按键
u8 key_tims=0;	//0位代表10ms,2位代表500ms,3位控制长短按键

定时器7(周期1ms)回调函数的内容:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)		//定时器中断里面不能加hal延时
{
	if(htim->Instance==TIM7)			//1ms进一次
	{
		static u8 tmp_10ms=0;
		static u16 tmp_500ms=0;
		
		(key_contr & 0x01)?(tmp_10ms++):(tmp_10ms = 0);//10ms计时
		if(tmp_10ms>10)
		{
			tmp_10ms = 0;
			key_tims |= 0x01;
		}
		
		(key_contr & 0x04)?(tmp_500ms++):(tmp_500ms = 0);//10ms计时
		if(tmp_500ms>500)	//用于流水灯控制
		{
			tmp_500ms = 0;
			key_tims |= 0x04;
		}
		
		
	}
}

按键扫描函数如下:

/*********************连续按键,返回多次键值(10ms)******************************/
u8 Key_Scan1(void)		 
{
	u8 key=0;			//保存键值
	
	key = (GPIOB->IDR&0x0007)|(GPIOA->IDR&0x0001)<<3;	//读取A0,B0,B1,B2,B3四个按键状态
	key ^= 0x0f;										//按键是低电平有效所以取反一下
	
	if(key)
	{
		key_contr |= 0x01;			//按下后标志位置1
		if(key && key_tims&0x01)	//按下且定时器记到10ms后进入
		{
			key_contr &= 0xfe;		//一次按键监测完成,清零标致位返回键值
			key_tims &= 0xfe;
			return key;
		}
		else						//没到10ms,程序不等待继续往下运行
			return 0;
	}
	else
	{
		key_contr &= 0xfe;			//松开或按键抖动期间,清零标致位
		return 0;
	}
}
/*************************************************************************/

单独按键

这里的单独按键是指,按下松手后返回一次键值。可用在一般的按键控制逻辑中。主要思路:结合前面的连续按键定时器去抖动,增加一个标志位控制只返回一次键值。

/*********************单独按键,松手后返回一次键值******************************/
u8 Key_Scan2(void)		//单独按键,返回键值
{
	u8 key=0;			//保存键值
	static u8 key_temp=0;		//储存获取到的次数,用于标记按下的时间,理论上是10ms加一
	
	key = (GPIOB->IDR&0x0007)|(GPIOA->IDR&0x0001)<<3;	//读取A0,B0,B1,B2,B3四个按键状态
	key ^= 0x0f;							//按键是低电平有效所以取反一下
	
	if(key)
	{
		key_contr |= 0x01;			//按下后10ms判断标志位置1
		if(key && key_tims&0x01)	//按下且定时器记到10ms后进入
		{
			key_contr &= 0xfe;		//获取到一次有效按键,清零10ms判断标志位
			key_tims &= 0xfe;
			key_contr |= 0x02;		//打开单独按键控制标志位
			key_temp = key;			//储存键值,松手后返回
		}
		else
			return 0;
	}
	else 						
	{	
		if(key_contr&0x02)		//按下后松开
		{
			key_contr &= ~0x03;	//清除单独按键控制标志位
			return key_temp;	//松手后返回键值
		}
		else					//未按下
		{
			key_contr &= 0xfe;	//松开或按键抖动期间,清零标致位
			return 0;
		}
	}
	return 0;
}
/*************************************************************************/

长短按键

这里的长短按键指,按下时间少于2S只返回一次键值,大于两秒后和连续按键一样,可用在输入比较大的数字的时候。主要思路:通过检测到的连续按键次数,大于设定值后一直检测到一次连续按键返回一次键值,小于的时候用标志位控制只返回一次键值。

/*********************长短按(用于实现长按2s以上连续加,2s以下加一次),返回按键值******************************/
u8 Key_Scan3(void)
{
	static u16 key_num=0;
	u8 key1=0,key2=0;
	
	key1 = (GPIOB->IDR&0x0007)|(GPIOA->IDR&0x0001)<<3;	//读取A0,B0,B1,B2,B3四个按键状态
	key1 ^= 0x0f;			//按键是低电平有效所以取反一下
	
	if(!key1)			//未按下清除计数和标志位
	{
		key_contr &= ~0x08;
		key_num = 0;
	}
	key2 = Key_Scan1();		//检测是否有有效按键
	if(key2)				//按下
	{
		key_num++;			//计数
		if(key_num>100)		//超过100后一直返回键值
			return key2;
		else if(!(key_contr & 0x08))	//小于100时,只返回1次键值
		{
			key_contr |= 0x08;		//标志已返回过键值
			return key2;
		}
		else				//按下返回过一次键值后一直返回0
			return 0;
	}
	else					//未按下
		return 0;
}
/*************************************************************************/

总结

感觉写的比较冗余,还可以在改进一下,欢迎大家批评指正!(0_0)!
上传不了视频就放个动态图吧…:在这里插入图片描述

版权声明:本文为CSDN博主「《CHANGE》」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_43698771/article/details/122565480

生成海报
点赞 0

《CHANGE》

我还没有学会写个人说明!

暂无评论

相关推荐

基于STM32的高精度频率计设计

前言 本文记录了博主完成的一个课设作品(学分为3.5分),题目需要利用ARM做出一个高精度频率计。具体要求如下: 1)实现对10M以内数字信号频率的高精度测量&#xff0c

HAL库控制PS2手柄

吐槽一下 最近买了个ps2手柄,结果买家发的例程全都是好几年前的库函数版本,尝试移植基本没啥可能。虽然PS2手柄已经被开发很久了,不过我看网上用hal库来写控制的很少,例程也都是用库函数