基于FIFO的按键检测(2)--按键扫描

目录

引言

按键变量定义

按键检测

按键初始化

按键状态判断

获取按键状态


引言

结合之前的 博文icon-default.png?t=N0U7https://blog.csdn.net/weixin_58670090/article/details/128824333,本文将继续往下书写基于FIFO的按键检测。上篇文章完成了FIFO的编写,所以我们在这篇文章需要考虑的是怎么样扫描按键并且写到FIFO里面。由简入繁,由浅入深。

按键变量定义

为了更好的描述按键的状态,需要定义几个按键变量来表示按键当前处于什么状态。如下所示

typedef enum
{
	KEY_NONE = 0, /* 0 表示按键事件 */

	KEY_1_DOWN, /* 1键按下 */
	KEY_1_UP,	/* 1键弹起 */
	KEY_1_LONG, /* 1键长按 */

	KEY_2_DOWN, /* 2键按下 */
	KEY_2_UP,	/* 2键弹起 */
	KEY_2_LONG, /* 2键长按 */
} KEY_ENUM;

除此之外还需要一些变量用于记录每个按键的按下时间计数器,长按需要按多长时间,相当于是功能配置寄存器。如下所示

typedef struct
{
	/* 下面是一个函数指针,指向判断按键手否按下的函数 */
	uint8_t (*IsKeyDownFunc)(void); /* 按键按下的判断函数,1表示按下 */

	uint8_t Count;		 /* 滤波器计数器 */
	uint16_t LongCount;	 /* 长按计数器 */
	uint16_t LongTime;	 /* 按键按下持续时间, 0表示不检测长按 */
	uint8_t State;		 /* 按键当前状态(按下还是弹起) */
	uint8_t RepeatSpeed; /* 连续按键周期 */
	uint8_t RepeatCount; /* 连续按键计数器 */
} KEY_T;

可以看到上面这个结构体里面包含了很多信息,其中函数指针指向我们以后定义的按键检测函数。这里还没有实例化,需要根据按键的数量来定义结构体数组。例如

static KEY_T s_tBtn[HARD_KEY_NUM] = {0};

按键检测

首先是最简单的,按键检测,直接检测按键的状态,相当于是直接获取对应管脚的电平

,判断按键有没有按下。

uint8_t IsKey1Down()
{
	if (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4) == 0)
		return 1;
	else
		return 0;
}

uint8_t IsKey2Down()
{
	if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 1)
		return 1;
	else
		return 0;
}

按键初始化

完成以上配置后,并没有将按键和配置联系起来,还需要一个初始化的过程。


static void KEY_FIFO_Init(void)
{
	uint8_t i;

	/* 给每个按键结构体成员变量赋一组缺省值 */
	for (i = 0; i < HARD_KEY_NUM; i++)
	{
		s_tBtn[i].LongTime = 100;  /* 长按时间 0 表示不检测长按键事件 */
		s_tBtn[i].Count = 0;   /* 计数器设置为滤波时间的一半 */
		s_tBtn[i].State = 0;	   /* 按键缺省状态,0为未按下 */
		s_tBtn[i].RepeatSpeed = 0; /* 按键连发的速度,0表示不支持连发 */
		s_tBtn[i].RepeatCount = 0; /* 连发计数器 */
	}
	/* 判断按键按下的函数 */
	s_tBtn[0].IsKeyDownFunc = IsKey1Down;
	s_tBtn[1].IsKeyDownFunc = IsKey2Down;
}

注意这里写的100是节拍数,100个tick。

按键状态判断

按键状态检测的基本思路就是,如果有按键按下,等待一个tick之后进行判断,如果仍然是按下,等待一个tick之后进行判断,如此重复,具体判断多少次根据自己的情况设定。确定按键成功按下后,存储信息到FIFO。

长按的判断也很简单,在每个tick里面,如果按键按下的话,就对变量累加,超过一定次数的话,就是长按。

这里还有一个功能是重复发送,个人感觉作用不是太大。原理也比较简单,在按键按下的情况下,每隔一段时间判断一次是都仍然按下,按下就在FIFO里面存储相关数据。

具体实现如下

void KEY_Detect(uint8_t i)
{
	KEY_T *pBtn;

	pBtn = &s_tBtn[i];
	if (pBtn->IsKeyDownFunc())
	{ // 这个里面执行的是按键按下的处理
		if (pBtn->Count < KEY_FILTER_TIME)
		{ // 按键滤波前给 Count 设置一个初值
			pBtn->Count = KEY_FILTER_TIME;
		}
		else if (pBtn->Count < 2 * KEY_FILTER_TIME)
		{ // 实现 KEY_FILTER_TIME 时间长度的延迟
			pBtn->Count++;
		}
		else
		{
			if (pBtn->State == 0)
			{
				pBtn->State = 1;

				/* 发送按钮按下的消息 */
				fifo_push(&Key_Fifo, (uint8_t)(3 * i + 1));
			}

			if (pBtn->LongTime > 0)
			{
				if (pBtn->LongCount < pBtn->LongTime)
				{
					/* 发送按钮持续按下的消息 */
					if (++pBtn->LongCount == pBtn->LongTime)
					{
						/* 键值放入按键FIFO */
						fifo_push(&Key_Fifo, (uint8_t)(3 * i + 3));
					}
				}
				else
				{
					if (pBtn->RepeatSpeed > 0)
					{
						if (++pBtn->RepeatCount >= pBtn->RepeatSpeed)
						{
							pBtn->RepeatCount = 0;
							/* 长按键后,每隔10ms发送1个按键 */
							fifo_push(&Key_Fifo, (uint8_t)(3 * i + 1));
						}
					}
				}
			}
		}
	}
	else
	{ // 这个里面执行的是按键松手的处理或者按键没有按下的处理
		if (pBtn->Count > KEY_FILTER_TIME)
		{
			pBtn->Count = KEY_FILTER_TIME;
		}
		else if (pBtn->Count != 0)
		{
			pBtn->Count--;
		}
		else
		{
			if (pBtn->State == 1)
			{
				pBtn->State = 0;

				/* 发送按钮弹起的消息 */
				fifo_push(&Key_Fifo, (uint8_t)(3 * i + 2));
			}
		}
		pBtn->LongCount = 0;
		pBtn->RepeatCount = 0;
	}
}

这里面的KEY_FILTER_TIME按键滤波的深度。值得一提的是,3*i+x,计算后你会发现,他和按键状态的枚举是对应的,非常巧妙。

获取按键状态

按键状态的获取,实际上就是调用FIFO对应的读取的函数,如下所示

void fifo_pop(fifo_t *fifo, FIFO_TYPE *data)
{
	/* 缓冲区为空 */
	if (fifo->fifoLen == 0)
	{
		*data = KEY_NONE;
	}
	else
	{

		fifo->fifoLen--;

		*data = fifo->buff[fifo->fifoRead];

		if (++fifo->fifoRead >= fifo->fifoSize)
		{
			fifo->fifoRead = 0;
		}
	}
}
//调用方法
fifo_init(&Key_Fifo, Key_Buff, Key_Buff_Size);

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
1、设计按键FIFO的优点   要介绍实现按键FIFO的优点,首先要了解FIFO的一些基本概念。FIFO即First In First Out,是一种先进先出的数据缓存方式,例如在超市购物之后我们会提着满满的购物车来到收银台排在结账队伍的最后等待付款,先排队的客户先付款离开,后面排队的只有等待前面付款离开才能进行付款。说白了FIFO就是这样一种先进先出机制,先存入的数据在读取时最先被读取到。   设计按键FIFO注意有三个方面的优点(来自于安富莱电子Eric2013大佬总结):   1、可以有效记录按键事件的发生,特别是系统要实现记录按键按下、松开、长按时,使用FIFO来实现是一种不错的选择方式。   2、系统是阻塞的,这样系统在检测按键按下的情况,由于机械按键抖动的原因不需要在这里等待一段时间,然后在确定按键是否正常按下。   3、按键FIFO程序在系统定时器中定时检测按键状态,确认按键按下后将状态写入FIFO中,不一定在主程序中一直做检测,这样可以有效降低系统资源的消耗。 2、按键的硬件设计   按键的原理图如上图所示,对于KEY0~KEY2这三个按键,一端接地,另一端连接stm32的GPIO端口。当按键按下时相应的IO口被拉低,如果把GPIO口配置为输入模式,此时读取相应的IO口电平,就可以检测按键是否被按下。对于KEY_UP按键则是与前面三个按键相反,IO口配置为输入模式时,读取到高电平时表示按键按下。因为机械固有的物理特性,按键按下内部弹簧片在瞬间接触的时候会有力学的回弹,造成2-8毫秒内信号不稳定,所以在设计检测机械按键是否按下的程序时,应考虑到按键消抖问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

苦瓜人生

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

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

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

打赏作者

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

抵扣说明:

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

余额充值