打印
[开发工具]

【转】MCU实战经验——多种的按键处理

[复制链接]
613|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
联通移不动|  楼主 | 2016-8-30 23:46 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式





之前的一个项目按键比较多,面板上面有按键,遥控器,处理的稍微复杂一点,MCU使用的是STM8S005K6.
关于按键部分的处理,现在拿处理来和大家分享一下,说的不对的地方还请各位大侠请教,大家共同进步。

按键通常分有IO口按键(BUTTON),AD按键(通过AD采样电压),IR(遥控器)
按按键功能分:有短按键,长按键,连续按键。打个比方,遥控电视机,按一下音量键,音量增加1,这个就是短按键。
按住音量键不放,音量连续加,这个就是连续按键。按住一个按键5s,系统会复位,这个是长按键。

怎么去处理这些不同的按键了,下面我们就细细来说

1,IO口按键,就是我们比较常见的一个IO接一个按键,或者是一个矩阵键盘。很多新人的处理方法可能是采样延时的方法,当年我也是这样的,如下
    if(GETIO==low)
    {
      delay_10ms()
      if(GETIO==low)
      {
        //得到按键值
      }
    }
   
   这种方法虽然简单,但是有很大弊端。
   首先 Delay 浪费很多时间,影响系统。
   第二,无法判断长短按键,连续按键。
   第三,如果这个按键是开关机按键系统在低功耗状态下,需要中断唤醒,这种方法比较容易出问题,如STM8S系列的 halt 模式。
   
   所以我们一般在产品开发的过程中,采用扫描的方法,就是每隔10ms 去检测IO的状态,看是否有按键,然后去抖动,判断按键功能。
   
   参考代码如下,这段代码是之前在一个论坛看到的比我自己写的更加优秀,所以拿出来和大家分享一下,也顺便感谢一下作者。
   这段代码,容易修改,可以根据自己的时间需要,进行长短按键,连续按键,还有组合按键的判断。
   
   
/* 按键滤波时间50ms, 单位10ms
只有连续检测到50ms状态不变才认为有效,包括弹起和按下两种事件
*/
#define BUTTON_FILTER_TIME         5
#define BUTTON_LONG_TIME         300                /* 持续1秒,认为长按事件 */

/*
        每个按键对应1个全局的结构体变量。
        其成员变量是实现滤波和多种按键状态所必须的
*/
typedef struct
{
        /* 下面是一个函数指针,指向判断按键手否按下的函数 */
        unsigned char  (*IsKeyDownFunc)(void); /* 按键按下的判断函数,1表示按下 */

        unsigned char  Count;                        /* 滤波器计数器 */
        unsigned char  FilterTime;                /* 滤波时间(最大255,表示2550ms) */
        unsigned short LongCount;                /* 长按计数器 */
        unsigned short LongTime;                /* 按键按下持续时间, 0表示不检测长按 */
        unsigned char   State;                        /* 按键当前状态(按下还是弹起) */
        unsigned char  KeyCodeUp;                /* 按键弹起的键值代码, 0表示不检测按键弹起 */
        unsigned char  KeyCodeDown;        /* 按键按下的键值代码, 0表示不检测按键按下 */
        unsigned char  KeyCodeLong;        /* 按键长按的键值代码, 0表示不检测长按 */
        unsigned char  RepeatSpeed;        /* 连续按键周期 */
        unsigned char  RepeatCount;        /* 连续按键计数器 */
}BUTTON_T;

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

        KEY_DOWN_Power,                        /* 按键键按下 */
        KEY_UP_Power,                        /* 按键键弹起 */
        KEY_LONG_Power,                        /* 按键键长按 */
        
        KEY_DOWN_Power_TAMPER        /* 组合键,Power键和WAKEUP键同时按下 */
}KEY_ENUM;

BUTTON_T s_Powerkey;               
//是否有按键按下接口函数
unsigned char  IsKeyDownUser(void)                 
{if (0==GPIO_ReadInputPin(POWER_KEY_PORT, POWER_KEY_PIN) ) return 1;return 0;}


void  PanakeyHard_Init(void)
{
   GPIO_Init (POWER_KEY_PORT, POWER_KEY_PIN, GPIO_MODE_IN_FL_NO_IT);//power key
}
void  PanakeyVar_Init(void)
{
        /* 初始化USER按键变量,支持按下、弹起、长按 */
        s_Powerkey.IsKeyDownFunc = IsKeyDownUser;                /* 判断按键按下的函数 */
        s_Powerkey.FilterTime = BUTTON_FILTER_TIME;                /* 按键滤波时间 */
        s_Powerkey.LongTime = BUTTON_LONG_TIME;                        /* 长按时间 */
        s_Powerkey.Count = s_Powerkey.FilterTime / 2;                /* 计数器设置为滤波时间的一半 */
        s_Powerkey.State = 0;                                                        /* 按键缺省状态,0为未按下 */
        s_Powerkey.KeyCodeDown = KEY_DOWN_Power;                        /* 按键按下的键值代码 */
        s_Powerkey.KeyCodeUp =KEY_UP_Power;                                /* 按键弹起的键值代码 */
        s_Powerkey.KeyCodeLong = KEY_LONG_Power;                        /* 按键被持续按下的键值代码 */
        s_Powerkey.RepeatSpeed = 0;                                                /* 按键连发的速度,0表示不支持连发 */
        s_Powerkey.RepeatCount = 0;                                                /* 连发计数器 */               
}
void Panakey_Init(void)
{
        PanakeyHard_Init();                /* 初始化按键变量 */
        PanakeyVar_Init();                /* 初始化按键硬件 */
}
/*
*********************************************************************************************************
*        函 数 名: bsp_DetectButton
*        功能说明: 检测一个按键。非阻塞状态,必须被周期性的调用。
*        形    参:按键结构变量指针
*        返 回 值: 无
*********************************************************************************************************
*/
void Button_Detect(BUTTON_T *_pBtn)
{
        if (_pBtn->IsKeyDownFunc())
        {
                if (_pBtn->Count < _pBtn->FilterTime)
                {
                        _pBtn->Count = _pBtn->FilterTime;
                }
                else if(_pBtn->Count < 2 * _pBtn->FilterTime)
                {
                        _pBtn->Count++;
                }
                else
                {
                        if (_pBtn->State == 0)
                        {
                                _pBtn->State = 1;

                                /* 发送按钮按下的消息 */
                                if (_pBtn->KeyCodeDown > 0)
                                {
                                        /* 键值放入按键FIFO */
                                        Pannelkey_Put(_pBtn->KeyCodeDown);// 记录按键按下标志,等待释放

                                }
                        }

                        if (_pBtn->LongTime > 0)
                        {
                                if (_pBtn->LongCount < _pBtn->LongTime)
                                {
                                        /* 发送按钮持续按下的消息 */
                                        if (++_pBtn->LongCount == _pBtn->LongTime)
                                        {
                                                /* 键值放入按键FIFO */
                                                Pannelkey_Put(_pBtn->KeyCodeLong);        
                                
                                        }
                                }
                                else
                                {
                                        if (_pBtn->RepeatSpeed > 0)
                                        {
                                                if (++_pBtn->RepeatCount >= _pBtn->RepeatSpeed)
                                                {
                                                        _pBtn->RepeatCount = 0;
                                                        /* 常按键后,每隔10ms发送1个按键 */
                                                        Pannelkey_Put(_pBtn->KeyCodeDown);        
                                
                                                }
                                        }
                                }
                        }
                }
        }
        else
        {
                if(_pBtn->Count > _pBtn->FilterTime)
                {
                        _pBtn->Count = _pBtn->FilterTime;
                }
                else if(_pBtn->Count != 0)
                {
                        _pBtn->Count--;
                }
                else
                {
                        if (_pBtn->State == 1)
                        {
                                _pBtn->State = 0;

                                /* 发送按钮弹起的消息 */
                                if (_pBtn->KeyCodeUp > 0) /*按键释放*/
                                {
                                        /* 键值放入按键FIFO */
                                Pannelkey_Put(_pBtn->KeyCodeUp);        
                        
                                }
                        }
                }

                _pBtn->LongCount = 0;
                _pBtn->RepeatCount = 0;
        }
}
//功能说明: 检测所有按键。10MS 调用一次
void Pannelkey_Polling(void)
{
        Button_Detect(&s_Powerkey);                /* USER 键 */
}
void Pannelkey_Put(void)
{
        
  // 定义一个队列 放入按键值        
}

   
2,遥控器按键,遥控器解码的一般就有两种 脉宽调制和脉冲调制,这里就不细讲解码的过程了。这里详细解码之后,如何处理遥控器按键
   实现遥控器按键的长短按功能和连续按键功能。
   代码裁剪过,大家根据实际需要改动
  其实AD按键,通过AD采样获得按键值之后,可以采取如下面的一样方法处理,提一个函数接口即可
   typedef struct
{
  unsigned char count;//
  unsigned char LongkeyFlag;/*是否长按键,1代表是*/
  unsigned char  PreKeyValue;/*按键值的一个备份,用于释放按键值*/
  
}ScanKeyDef;

#define SHORT_PRESS_TIME_IR                16 // 10ms
#define SERIES_PRESS_TIME_IR            10  
#define LONG_PRESS_TIME_IR                    22
#define KEY_RELEASE_TIME_OUT_IR     12  // 10ms
//提供5个接口函数,如下。
unsigned char get_irkey(void);
unsigned char ISSeriesKey(unsigned char temp);//按键是否需要做连续按键
unsigned char changeSeriesKey(unsigned char temp);//转换连续按键值
unsigned char ISLongKey(unsigned char temp);//按键是否需要做连续按键
unsigned char changeToLongKey(unsigned char temp);//转换连续按键值


unsigned char KeyScan(void)
{
    unsigned char  KeyValue = KEY_NONE,
                                KeyValueTemp = KEY_NONE;
    static   unsigned char KeyReleaseTimeCount =0;

    KeyValueTemp = get_irkey();

    if(KeyValueTemp != KEY_NONE)
    {
        ScanKeyDef.count++;
        KeyReleaseTimeCount =0;

        if(ScanKeyDef.count < LONG_PRESS_TIME_IR )
        {
            ScanKeyDef.LongkeyFlag = 0;
            if((ScanKeyDef.count == SERIES_PRESS_TIME_IR) && ISSeriesKey(KeyValueTemp)) //处理连续按键
                {
                    KeyValue = changeSeriesKey ( KeyValueTemp );
                    ScanKeyDef.PreKeyValue = KEY_NONE;
                }
            else if ( ScanKeyDef.count  < SHORT_PRESS_TIME_IR )
            {
                ScanKeyDef.PreKeyValue = KeyValueTemp;
            }
            else
            {
            
                ScanKeyDef.PreKeyValue  = KEY_NONE; // 无效按键
            }
        }
        else if ( ScanKeyDef.count  == LONG_PRESS_TIME_IR )
        {
      
           if (ISLongKey(KeyValueTemp))
            {
                {
                   ScanKeyDef.LongkeyFlag = 1;
                   KeyValue = changeToLongKey ( KeyValueTemp );
               }
          }
            ScanKeyDef.PreKeyValue = KEY_NONE;
         
        }
        else if (ScanKeyDef.count > LONG_PRESS_TIME_IR )
        {
      
            ScanKeyDef.PreKeyValue  = KEY_NONE; //无效按键
        }
    }
    else//release & no press
    {
        KeyReleaseTimeCount ++;
        if(KeyReleaseTimeCount >= KEY_RELEASE_TIME_OUT_IR)
        {
  
            if ( ScanKeyDef.PreKeyValue != KEY_NONE ) //释放按键值
            {
                if ( ScanKeyDef.LongkeyFlag == 0 )
                {
           
                    KeyValue =ScanKeyDef.PreKeyValue ;
                }
            }         
            ScanKeyDef.count  = 0;
            ScanKeyDef.LongkeyFlag = 0;
           ScanKeyDef.PreKeyValue = KEY_NONE;
   
        }
    }
    return(KeyValue);
}


   
   
   
  

沙发
联通移不动|  楼主 | 2016-8-30 23:48 | 只看该作者

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

67

主题

127

帖子

0

粉丝