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


     一、介绍

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

      注意:按键扫描函数需要每隔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

  • 相关阅读:
    12 购物车之一(用列表)
    centos7安装pycharm,并创建桌面快捷方式
    centos7安装xrdp
    centos7 安装gitlab
    mysql 新建用户和授权
    django项目连接mysql没有Mysqldb解决办法_使用pymysql代替
    selenium+python实现查询和下载文件
    Python3-使用Requests和正则表达式爬取猫眼Top100的数据
    python3-字典常用操作
    python3-列表常用操作
  • 原文地址:https://www.cnblogs.com/wenhao-Web/p/14447523.html
Copyright © 2020-2023  润新知