单片机按键检测方案(一)

 一、介绍

  该按键实现方案摘录自安富莱,具备按键短按、按键抬起、按键长按和按键长按连发的功能;使用了一个缓存数组用于装载按键值,缓存数组的大小和按键的个数有关。

  注意:按键扫描函数需要每隔10ms调用一次,如果需要修改请细看程序。

二、代码实现

/*
 * key.c
 *
 *  Created on: Jul 30, 2020
 *      Author: Mr.W
 */
#include "./key/key.h"

static KEY_T         s_tBtn[KEY_FIFO_SIZE];         /* 定义按键,按键的数量为KEY_FIFO_SIZE */
static KEY_FIFO_T     s_tKey;                        /* 按键FIFO变量,结构体 */

/**
  * @brief 获取按键的状态
  * @retval none
  * @author Mr.W
  * @date 2020-7-30
  */
static uint8_t IsKeyDown0(void)            {if(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) == GPIO_PIN_RESET) return 1;else return 0;}
static uint8_t IsKeyDown1(void)            { /* 待添加其它按键 */ return 0;}
static uint8_t IsKeyDown2(void)            { /* 待添加其它按键 */ return 0;}
static uint8_t IsKeyDown3(void)            { /* 待添加其它按键 */ return 0;}
static uint8_t IsKeyDown4(void)            { /* 待添加其它按键 */ return 0;}
static uint8_t IsKeyDown5(void)            { /* 待添加其它按键 */ return 0;}
static uint8_t IsKeyDown6(void)            { /* 待添加其它按键 */ return 0;}
static uint8_t IsKeyDown7(void)            { /* 待添加其它按键 */ return 0;}
static uint8_t IsKeyDown8(void)            { /* 待添加其它按键 */ return 0;}
static uint8_t IsKeyDown9(void)            { /* 待添加其它按键 */ return 0;}

/**
  * @brief 按键引脚初始化
  * @retval none
  * @author Mr.W
  * @date 2020-7-30
  */
static void key_gpio_cfg(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* GPIO Ports Clock Enable */
    __HAL_RCC_GPIOC_CLK_ENABLE();

    /*Configure GPIO pin : GPIO_PIN_13 */
    GPIO_InitStruct.Pin = GPIO_PIN_13;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}


/**
  * @brief 初始化按键参数值
  * @retval none
  * @author Mr.W
  * @date 2020-7-30
  */
static void key_initKeyVar(void)
{
    uint8_t i;

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

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

    /* 如果需要单独更改某个按键的参数,可以在此单独重新赋值 */
    /* 比如,我们希望按键1按下超过1秒后,自动重发相同键值 */
    s_tBtn[0].LongTime = KEY_LONG_TIME;
    s_tBtn[0].RepeatSpeed = 5;                        /* 每隔50ms自动发送键值 */

    /* 判断按键按下的函数 */
    s_tBtn[0].IsKeyDownFunc = IsKeyDown0;
    s_tBtn[1].IsKeyDownFunc = IsKeyDown1;
    s_tBtn[2].IsKeyDownFunc = IsKeyDown2;
    s_tBtn[3].IsKeyDownFunc = IsKeyDown3;
    s_tBtn[4].IsKeyDownFunc = IsKeyDown4;
    s_tBtn[5].IsKeyDownFunc = IsKeyDown5;
    s_tBtn[6].IsKeyDownFunc = IsKeyDown6;
    s_tBtn[7].IsKeyDownFunc = IsKeyDown7;
    s_tBtn[8].IsKeyDownFunc = IsKeyDown8;
    s_tBtn[9].IsKeyDownFunc = IsKeyDown9;
}

/**
  * @brief 将按键值放入缓冲区
  * @param 按键编码
  * @retval none
  * @author Mr.W
  * @date 2020-7-30
  */
static void key_putKey(uint8_t _keyCode)
{
    s_tKey.Buf[s_tKey.Write] = _keyCode;

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

/**
  * @brief 查询被触发的按键
  * @retval none
  * @author Mr.W
  * @date 2020-7-30
  */
static void key_detectKey(uint8_t i)
{
    KEY_T *pBtn;

    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;
                /* 将短按键值放入缓冲区,按键值从1开始*/
                key_putKey((uint8_t)(3 * i + 1));
            }

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

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

/**
  * @brief 按键初始化
  * @retval none
  * @author Mr.W
  * @date 2020-7-30
  */
void key_init(void)
{
    key_gpio_cfg();
    key_initKeyVar();
}

/**
  * @brief 扫描按键(每隔10ms扫描一次按键)
  * @retval none
  * @author Mr.W
  * @date 2020-7-30
  */
void key_keyScan(void)
{
    uint8_t i;

    for (i = 0; i < KEY_FIFO_SIZE; i++)
    {
        key_detectKey(i);
    }
}

/**
  * @brief 获取按键值
  * @retval 获取到的按键值
  * @author Mr.W
  * @date 2020-7-30
  */
uint8_t key_getKey(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;
    }
}

 

 

 

/*
 * key.h
 *
 *  Created on: Jul 30, 2020
 *      Author: Mr.W
 */

#ifndef KEY_H_
#define KEY_H_

/* Includes ------------------------------------------------------------------*/
#include "stm32l0xx_hal.h"

/*
    按键滤波时间50ms, 单位10ms。
    只有连续检测到50ms状态不变才认为有效,包括弹起和按下两种事件
    即使按键电路不做硬件滤波,该滤波机制也可以保证可靠地检测到按键事件
*/
#define KEY_FILTER_TIME   5
#define KEY_LONG_TIME     100            /* 单位10ms, 持续1秒,认为长按事件 */


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_3_DOWN,                /* 3键按下 */
    KEY_3_UP,                /* 3键弹起 */
    KEY_3_LONG,                /* 3键长按 */

    KEY_4_DOWN,                /* 4键按下 */
    KEY_4_UP,                /* 4键弹起 */
    KEY_4_LONG,                /* 4键长按 */

    KEY_5_DOWN,                /* 5键按下 */
    KEY_5_UP,                /* 5键弹起 */
    KEY_5_LONG,                /* 5键长按 */

    KEY_6_DOWN,                /* 6键按下 */
    KEY_6_UP,                /* 6键弹起 */
    KEY_6_LONG,                /* 6键长按 */

    KEY_7_DOWN,                /* 7键按下 */
    KEY_7_UP,                /* 7键弹起 */
    KEY_7_LONG,                /* 7键长按 */

    KEY_8_DOWN,                /* 8键按下 */
    KEY_8_UP,                /* 8键弹起 */
    KEY_8_LONG,                /* 8键长按 */

    KEY_9_DOWN,                /* 9键按下 */
    KEY_9_UP,                /* 9键弹起 */
    KEY_9_LONG,                /* 9键长按 */

    KEY_10_DOWN,            /* 10键按下 */
    KEY_10_UP,                /* 10键弹起 */
    KEY_10_LONG,            /* 10键长按 */
}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;


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


void key_init(void);
void key_keyScan(void);
uint8_t key_getKey(void);

#endif /* KEY_H_ */

 

 

void StartTask02(void const * argument)
{
  uint8_t key_value;
  /* Infinite loop */
  for(;;)
  {
      key_keyScan();
      key_value = key_getKey();
      switch(key_value)
      {
            case 0:
                break;
            case 1:
                HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
                break;
            case 4:
                HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
                break;
            default:
                break;
      }
      osDelay(10);
  }
}

 

 

 

#endif

posted @ 2021-02-25 16:10  不要让自己太懒  阅读(286)  评论(0编辑  收藏  举报