按键FIFO

/*
 * @Author: John Luo
 * @Date: 2021-08-25 10:54:28
 * @LastEditTime: 2021-08-25 13:01:24
 * @LastEditors: John Luo
 * @Description: 1. 自定义IsKeyDown()函数
 *               2. gp_InitKeyVar()赋值函数指针
 *               3. 连按功能需要打开长按功能
 * @FilePath: \61F14x_UART\gp_key.c
 */
#include "gp_key.h"
#include "SYSCFG.h"

static KEY_T s_tBtn[KEY_COUNT];
static KEY_FIFO_T s_tKey;

/* 需要自己定义 *****************************************/
static uint8_t IsKeyDown0(void)
{
    if (KEY0 == 0)
        return 1;
    else
        return 0;
}

static uint8_t IsKeyDown1(void)
{
    if (KEY1 == 0)
        return 1;
    else
        return 0;
}

static uint8_t IsKeyDown2(void)
{
    if (KEY2 == 0)
        return 1;
    else
        return 0;
}

static uint8_t IsKeyDown3(void)
{
    if (KEY3 == 0)
        return 1;
    else
        return 0;
}
/********************************************************/

/**
 * @description: 
 * @param {KEY_ID_E} _ucKeyID
 * @param {uint16_t} _LongTime
 * @param {uint8_t} _RepeatSpeed
 * @return {*}
 */
static void gp_SetKeyParam(KEY_ID_E _ucKeyID, uint16_t _LongTime, uint8_t _RepeatSpeed)
{
    s_tBtn[_ucKeyID].LongTime = _LongTime;       /* 长按时间 0 表示不检测长按键事件 */
    s_tBtn[_ucKeyID].RepeatSpeed = _RepeatSpeed; /* 按键连发的速度,0表示不支持连发 */
    s_tBtn[_ucKeyID].RepeatCount = 0;            /* 连发计数器 */
}

/**
 * @description: 初始化按键结构体
 * @param {*}
 * @return {*}
 */
void gp_InitKeyVar(void)
{
    uint8_t i;

    /* 对按键FIFO读写指针清零 */
    s_tKey.Read = 0;
    s_tKey.Write = 0;

    /* 给每个按键结构体成员变量赋一组缺省值 */
    for (i = 0; i < KEY_COUNT; i++)
    {
        s_tBtn[i].LongTime = KEY_LONG_TIME;    /* 长按时间 0 表示不检测长按键事件 */
        s_tBtn[i].Count = KEY_FILTER_TIME / 2; /* 计数器设置为滤波时间的一半 */
        s_tBtn[i].State = 0;                   /* 按键缺省状态,0为未按下 */
        s_tBtn[i].RepeatSpeed = 0;             /* 按键连发的速度,0表示不支持连发 */
        s_tBtn[i].RepeatCount = 0;             /* 连发计数器 */
    }
    /* 这里赋值按键按键函数指针 */
    s_tBtn[KID_K0].IsKeyDownFunc = IsKeyDown0;
    s_tBtn[KID_K1].IsKeyDownFunc = IsKeyDown1;
    s_tBtn[KID_K2].IsKeyDownFunc = IsKeyDown2;
    s_tBtn[KID_K3].IsKeyDownFunc = IsKeyDown3;

    /* 如果需要单独更改某个按键的参数,可以在此单独重新赋值 */
    gp_SetKeyParam(KID_K0, 150, 50);
}

/**
 * @description: 10ms调用一次函数
 * @param {uint8_t} i 按键id
 * @return {*}
 */
void gp_DetectKey(uint8_t i)
{
    KEY_T *pBtn;

    /*
		如果没有初始化按键函数,则报错
		if (s_tBtn[i].IsKeyDownFunc == 0)
		{
			printf("Fault : DetectButton(), s_tBtn[i].IsKeyDownFunc undefine");
		}
	*/

    pBtn = &s_tBtn[i];
    if (pBtn->IsKeyDownFunc())
    {
        if (pBtn->Count < KEY_FILTER_TIME)
        {
            pBtn->Count = KEY_FILTER_TIME;
        }
        else if (pBtn->Count < 2 * KEY_FILTER_TIME)
        {
            pBtn->Count++;
        }
        else
        {
            if (pBtn->State == 0)
            {
                pBtn->State = 1;

                /* 发送按钮按下的消息 */
                gp_PutKeyFifo((uint8_t)(3 * i + 1));
            }

            if (pBtn->LongTime > 0)
            {
                if (pBtn->LongCount < pBtn->LongTime)
                {
                    /* 发送按钮持续按下的消息 */
                    if (++pBtn->LongCount == pBtn->LongTime)
                    {
                        /* 键值放入按键FIFO */
                        gp_PutKeyFifo((uint8_t)(3 * i + 3));
                    }
                }
                else
                {
                    if (pBtn->RepeatSpeed > 0)
                    {
                        if (++pBtn->RepeatCount >= pBtn->RepeatSpeed)
                        {
                            pBtn->RepeatCount = 0;
                            /* 常按键后,每隔10ms发送1个按键 */
                            gp_PutKeyFifo((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;

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

/**
 * @description: 按键动作压入队列
 * @param {uint8_t} _KeyCode
 * @return {*}
 */
void gp_PutKeyFifo(uint8_t _KeyCode)
{
    s_tKey.Buf[s_tKey.Write] = _KeyCode;

    if (++s_tKey.Write >= KEY_FIFO_SIZE)
    {
        s_tKey.Write = 0;
    }
}

/**
 * @description: 从队列中读取按键动作
 * @param {*}
 * @return {*}
 */
uint8_t gp_GetKeyFifo(void)
{
    uint8_t ret;

    if (s_tKey.Read == s_tKey.Write)
    {
        return KEY_NONE;
    }
    else
    {
        ret = s_tKey.Buf[s_tKey.Read];

        if (++s_tKey.Read >= KEY_FIFO_SIZE)
        {
            s_tKey.Read = 0;
        }
        return ret;
    }
}
/*
 * @Author: John Luo
 * @Date: 2021-08-25 10:54:37
 * @LastEditTime: 2021-08-25 12:00:02
 * @LastEditors: John Luo
 * @Description: 
 * @FilePath: \61F14x_UART\gp_key.h
 */

#define uint8_t unsigned char
#define uint16_t unsigned int

/* 需要自己定义 *****************************************/
#define KEY0 RB4
#define KEY1 RB3
#define KEY2 RB2
#define KEY3 RB1

#define KEY_FILTER_TIME 5 /* 短按滤波和连按时间 */

#define KEY_LONG_TIME 100 /* 单位10ms, 持续1秒,认为长按事件 */

#define KEY_FIFO_SIZE 10 /* 按键动作队列大小 */

#define KEY_COUNT 4 /* 按键个数, 4个独立建 */

typedef enum /* 按键ID 从0开始增加 */
{
	KID_K0 = 0,
	KID_K1,
	KID_K2,
	KID_K3
} KEY_ID_E;

typedef enum /* 记录按键动作值 */
{
	KEY_NONE = 0, /* 0 表示按键事件 */

	KEY_0_DOWN, /* 0键按下 */
	KEY_0_UP,	/* 0键弹起 */
	KEY_0_LONG, /* 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_3_DOWN, /* 3键按下 */
	KEY_3_UP,	/* 3键弹起 */
	KEY_3_LONG, /* 3键长按 */
} 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;

typedef struct
{
	uint8_t Buf[KEY_FIFO_SIZE]; /* 键值缓冲区 */
	uint8_t Read;				/* 缓冲区读指针 */
	uint8_t Write;				/* 缓冲区写指针 */
} KEY_FIFO_T;

/* 自定义按键函数 */
static uint8_t IsKeyDown0(void);
static uint8_t IsKeyDown1(void);
static uint8_t IsKeyDown2(void);
static uint8_t IsKeyDown3(void);

static void gp_SetKeyParam(KEY_ID_E _ucKeyID, uint16_t _LongTime, uint8_t _RepeatSpeed);
uint8_t gp_GetKeyFifo(void);
void gp_PutKeyFifo(uint8_t _KeyCode);
void gp_InitKeyVar(void);
void gp_DetectKey(uint8_t i);

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
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毫秒内信号不稳定,所以在设计检测机械按键是否按下的程序时,应考虑到按键消抖问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值