目录
引言
结合之前的 博文https://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);