关于ARM的独立按键识别程序消抖的讨论

1、独立按键的一般性功能要求

对于独立按键我们一般要求功能 比较简单,能识别按键按下,不考虑识别多个按键同时按下,按下按键一次执行一次操作。不需要识别长按。

2、需要处理的内容

独立按键一般都连接一个上拉电阻,当按键没有按下的时候 GPIO 读到的是高电平 当有按键按下的时候 读到的是低电平。那么我们都知道 机械按键有一个抖动问题,在这里需要 5ms 左右的时间来消抖。然后等待按键松开 ,按键松开后执行相应的操作。

3、一般的识别程序

在 51或者AVR 等8 位机时代,我常常采用一种按键检测方法,一般步骤如下:

A:检测按键是否按下------是否有低电平在 GPIO上出现 如果没有 return 0;如果有进入 B;

B:软件延时(for循环)5ms左右 进入C;

C:检测GPIO 是否有低电平----是否有按键按下 如果没有 ---- 是抖动 return0 ;如果有低电平,则有按键被按下 进入 D;

D:检测是哪一个按键被按下,保存对应的编码到变量 key,然后进入E;

E:等待按键松开 (while---GPIO 是高电平),如果没有这一步 将造成 一次按下 检测到很多次的情况(这是我们不希望看到的);

F:如果E松开 则 return key ; 返回检测到按键值 ;

这就是一个完整的按键检测的过程,包含消抖和等待松开;

4、ARM时代遇到的尴尬

如3所说的检测方法固然简单,然而这里面有几个东西需要注意 ;第一 5ms 的延时 将极大的浪费 ARM的时间资源。 第二 while 等待松手 这将导致很多问题 如浪费 ARM时间资源,给其他处理带来问题等。虽然在8bit MCU时代,5ms 和while 貌似没关系,但是在 100MHz 主频 32bit 的ARM时代,这样的资源浪费是巨大的,将限制ARM的处理能力,拖慢整个系统。

5、适合于ARM使用的检测方法的提出

我们看下面的图(虽然很丑哈,凑合着看):

无标题

一个标准的按键按下 ,就像 蓝色的线条一样,按下开始 和结束都充满着抖动。那么 我们能否想一个方法检测红色线条呢;

很显然我们只需要 首先检测到 红色的高电平(红色虚线前面)然后 在虚线处检测到低电平,然后 我们延时 T (5ms),这样我们在检测 GPIO,是不是就相当于检测的是红色的线。

我们将按键 检测变成一个 有延时 消抖功能的 边沿检测,这样既保证消抖 又保证 按下一次只动作一次 无需 while等待。这样讲极大的释放我们的ARM资源。

我们这里虽然还行需要 5ms 延时 ,但是 我们的延时采用 系统节拍定时器 来做,而且整个检测算法采用状态机的方式,这样我们的检测过程就变成如下所示了:

A:检测GPIO是否是高电平,如果是保存状态和数据,检测函数退出;

B:下一个循环在检测GPIO是否是高电平 如果是 同 A ,如果不是 说明检测到了 红色的虚线,转移状态并保存数据;启用 延时;函数推出;

C:延时是否到达 如果没有 函数推出;

D:延时函数到达,检测GPIO是否是低电平,如果不是 那么是错误触发,转移状态到初始状态,如果是低电平 说明有按键按下,处理数据准备输出 return key ,将上上一个状态数据转移;函数退出;

E:接续检测,如果在底部横线间检测到 2个低电平,那么由于没有初始状态的 高电平,将不会输出,因此也不会重复输出造成输出多次的现象。

整个检测过程,没有一个需要函数内部 while 和for 循环的,这将极大的提高ARM的运行效率。

附录:示例代码:

//GPIO3-11 to 14 是4个按键,SysMs是系统节拍定时器定时变量 单位是 1ms;SYSMSMAX宏 系统节拍定时器 的最大数值。

/****************************************************************
*名称:uint32_t ReadKey(void)
*参数:NULL
*功能:返回按键 key1--0x01  key2--0x02 key3--0x04 key4--0x08
*****************************************************************/
uint32_t ReadKey(void)
{
  static uint32_t LastLastKey=0x0000000f;
  static uint32_t LastKey=0x0000000f;
  static uint32_t CountMs=SYSMSMAX;
  static uint32_t StartSign=0;
  uint32_t Key=0,KeyOut=0;
  Key=((LPC_GPIO_PORT->PIN[3])>>11)&0x0000000f;
  if(0==StartSign)
    {
      if((Key!=0x0000000f)&&(0x0000000f==LastLastKey))                                         //第一次检测到有低电平 保存并延时
      {
        LastKey=Key;
        CountMs=SysMs;
        StartSign=1;
      }
      else
       {
        LastLastKey=Key;
       }
    }
     if(1==StartSign)                                                   //判断延时是否到时间
      {
        if(SysMs>CountMs)
          {
            if((SysMs-CountMs)>5)                              //判断延时 5ms 是否到达
              {
                Key=((LPC_GPIO_PORT->PIN[3])>>11)&0x0000000f;    //读本次数据
                KeyOut=LastLastKey&(~Key)&(~LastKey)&0x0000000f;           //两次都是低电平 则有输出高电平
                LastLastKey=0;
                StartSign=0;                                   //翻转状态标志位
              }
          }
         else
          {
            if((SYSMSMAX+SysMs-CountMs)>5)                     //计时正好走到 翻越的地方 的处理
             {
                Key=((LPC_GPIO_PORT->PIN[3])>>11)&0x0000000f;
                KeyOut=LastLastKey&(~Key)&(~LastKey)&0x0000000f;
                LastLastKey=0;
                StartSign=0;
             }
          }
      }
  return KeyOut;
}
一见钟情 发表于09-09 10:04 浏览65535次
分享到:

已有0条评论

暂时还没有回复哟,快来抢沙发吧

添加一条新评论

只有登录用户才能评论,请先登录注册哦!

话题作者

一见钟情
一见钟情(大校)
金币:1529个|学分:3659个
立即注册
畅学电子网,带你进入电子开发学习世界
专业电子工程技术学习交流社区,加入畅学一起充电加油吧!

x

畅学电子网订阅号