打印

按键消抖的问题

[复制链接]
18113|27
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
mcu_lover|  楼主 | 2010-1-28 18:37 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
请教大家,普通的独立按键,通过一个上拉电阻后接到51单片机的I/O口上面。按下的时候是低电平有效。众所周知,按键按下的时候,会有一段抖动时期。我想请教的是,如果按键没有按下,与那个按键相连的单片机的I/O是否会出现抖动电平?即使单片机处的环境干扰很大。我认为如果按键不按下,那个I/O口因为上拉电阻的作用,始终是高电平。按键只要按下,不管抖动还是不抖动,都会出现低电平,因此只要检测到低电平了,就可以认为按下了,就可以返回键值了。这样理解对不对?

相关帖子

沙发
匠人粉丝团| | 2010-1-28 18:59 | 只看该作者
端个沙发~:)

使用特权

评论回复
板凳
zq1987731| | 2010-1-28 19:19 | 只看该作者
按键这类应用对于单片机而言是“蜗牛速度”,消抖的目的是防止一次按键处理多次,根本不是防止什么IO口电平意外翻转,毕竟外部干扰再强烈也不可能改变带上拉的IO口电平,又不是什么高速信号。
如果你的主程序走一遍(从本次扫描按键到下次回到这里再扫描按键)需要的时间超过按键抖动时间,那么根本无需消抖处理,但一般不可能,所以正常应用中都是用状态机跳转的方式进行按键0等待消抖及判长短击、组合键的操作

使用特权

评论回复
评分
参与人数 1威望 +1 收起 理由
mcu_lover + 1
地板
mcu_lover|  楼主 | 2010-1-28 19:28 | 只看该作者
“消抖的目的是防止一次按键处理多次,根本不是防止什么IO口电平意外翻转”
也就是说,只要检测到低电平就可以返回键值了,对吧?我看到好多按键程序,包括用了状态机的,但是在第二个状态以后才返回键值的。
如下伪代码描述一般:

KeyValue = GetKey() ;
switch(s_u8State)
{
      case 0 :
                   if(KEY_NULL != KeyValue)
                   {
                         s_u8State = 1 ;     
                   }
      break ;
     case 1 :
                   if(KEY_NULL != KeyValue)
                   {
                         s_u8State = 2 ;     
                   }
                   else
                   {
                          s_u8State  = 0 ;
                   }
      break ;
     case 2 :
                   if(KEY_NULL != KeyValue)
                   {
                         s_u8State = 3 ;
                         ReturnValue = KeyValue ;   //键值返回   
                   }
                   else
                   {
                          s_u8State  = 0 ;
                   }
      break ;
      .......
      .......
      default :
      break ;
}
上面的这种基于状态机的是在状态2返回的键值。我的理解是在状态0的if语句里面就可以返回键值了,这样对吗?

使用特权

评论回复
5
xwj| | 2010-1-28 19:48 | 只看该作者
LS,你没有理解3楼的意思,再去想想吧

使用特权

评论回复
6
smileagain| | 2010-1-28 20:48 | 只看该作者
“消抖的目的是防止一次按键处理多次,根本不是防止什么IO口电平意外翻转”
也就是说,只要检测到低电平就可以返回键值了,对吧?我看到好多按键程序,包括用了状态机的,但是在第二个状态以后才返回键值的。
如下伪 ...
mcu_lover 发表于 2010-1-28 19:28


你还是没有理解按键为什么要消抖啊。
通常来说一次按键动作单片机能检测到几个到几十个高低电平的变换,不消抖的话,单片机就认为你按了n多次键。这样说你可理解了?

使用特权

评论回复
7
rookieyeah| | 2010-1-28 22:42 | 只看该作者
按键消抖。。。弹簧的抖动次数绝对超过了大家可以忍受的范围。。。检测到低电平后直接做延时再读取io吧。。。

使用特权

评论回复
8
踢球老越位| | 2010-1-28 23:02 | 只看该作者
找一下hot大叔的零耗时键盘看看吧

使用特权

评论回复
9
fzp121| | 2010-1-28 23:58 | 只看该作者
来个简单点的解释,比如你按键来控制有个LED的亮和灭,按下时改变LED的状态,如果没有去抖动,你能决定按键最后松开,LED是亮还是灭吗?

使用特权

评论回复
10
tiger5z2012| | 2010-1-29 10:10 | 只看该作者
200ms检测一次按键就行了。

使用特权

评论回复
11
反质子| | 2010-1-29 11:07 | 只看该作者
消抖的目的是防止一次按键处理多次     :)GOOD

使用特权

评论回复
12
southboy| | 2010-1-29 11:39 | 只看该作者
检测到按键按下立即返回键值并disable按键,再延时enable按键。延时要用定时器来做。

使用特权

评论回复
13
M.gtd| | 2010-1-29 16:35 | 只看该作者

使用特权

评论回复
评分
参与人数 1威望 +1 收起 理由
mcu_lover + 1
14
mcu_lover|  楼主 | 2010-1-29 21:00 | 只看该作者
谢谢楼上各位老师指点。消抖得目的在于防止按键一次按下,多次响应,因此只要按键按下,在抖动前或者抖动稳定后返回键值是一样的效果,而要做的是防止多次响应即可。因为只要抖动电平出现,即表明按键已经按下了,干扰是无法干扰到出现抖动的波形。

使用特权

评论回复
15
何工| | 2010-1-29 21:42 | 只看该作者
找一下hot大叔的零耗时键盘看看吧
踢球老越位 发表于 2010-1-28 23:02
在那?我怎么没看到哦?

使用特权

评论回复
16
ljolove| | 2010-1-30 08:23 | 只看该作者
我献丑来个程序吧,供楼主参考,这个设计应该还是比较简单的.
void scan_key(void)
{
        switch(Keystate)
        {
                case KEY_IDEL :
                        Key_v=PIND&0x0f;
                        if(Key_v!=NONE_KEY_DOWN)
                        {
                                Keystate=KEY_DELAY;
                                KeyDelaytime=0;
                        }
                break;
                case KEY_DELAY :
                      if(KeyDelaytime>=KEY_DELAY_TIME)
                      {
                         Key_v=PIND&0x0f;
                         if(Key_v!=NONE_KEY_DOWN)
                         {
                                if(Keylock==1)
                                {
                                    if(Key_v==KEY_ENTER)
                                    {
                                        Key_lock_2s=0;
                                    }
                                    else if(Key_v==KEY_RESET)
                                    {
                                        if(Unstable_bit==0)
                                        {
                                            PORTB&=0xf9;
                                            Led_state&=0xf9;
                                            CH452_Write(CH452_NO_BCD);
                                            CH452_Write(CH452_DIG0|Led_state);
                                            Delay_timer=0;
                                        }
                                    }
                                }
                                else
                                {
                                    Key_pro(Key_v);
                                }
                                KeySound();
                                Keystate=WAIT_KEY_OPEN;
                        }
                        else
                        {
                             Keystate=KEY_IDEL;
                        }
                     }
                break;
                case WAIT_KEY_OPEN :
                        Key_v=PIND&0x0f;
                        if(Key_v==NONE_KEY_DOWN)
                        {
                            Keystate=KEY_IDEL;
                            Key_lock_2min=0;
                            return_disp=0;
                        }
                        else if(Key_v==KEY_ENTER)
                        {
                            if(Key_lock_2s>=2000)
                            {
                                Machine_state=DISP_UNLOCK;//DISP_Unlock();
                                Key_lock_disp_time=0;
                                Key_lock_2s=0;
                                Keylock=0;
                            }
                        }
                break;
        }
}
看着有点乱,主要看一下结构吧,一共三个状态KEY_IDEL,KEY_DELAY,WAIT_KEY_OPEN,通过一个变量转换三个状态,在KEY_IDEL判断是否有按键按下,如果有,就进入KEY_DELAY,然后在定时器里置一个变量,等待延时,在等待延时的时候并不会影响单片机工作,当延时到了在判断是否有键按下,如果有就进入到按键处理,然后状态转换为等待按键放开状态,如果没有按键按下,就反回KEY_IDEL状态.不知道说清楚没有

使用特权

评论回复
评分
参与人数 1威望 +1 收起 理由
mcu_lover + 1
17
lb0577| | 2010-1-30 09:50 | 只看该作者
200ms检测一次按键就行了。
tiger5z2012 发表于 2010-1-29 10:10

多了个0
20ms足也。

使用特权

评论回复
18
icmap| | 2010-1-30 12:21 | 只看该作者
4楼的理解没错,如果只是防止一次按键处理多次,那么在第一次读到按键时即可返回键码,然后隔一段时间才继续读取按键。

如果只是防止一次按键处理多次,只需要下面简单的代码即可:

if(Flag==0)
{
  ReturnValue = GetKey() ;
  if(KEY_NULL != ReturnValue)
  {
    Flag = 1;        //如果监测到按键,则在稍候的一段时间内停止读取按键。
  }
}

if(Flag 为 1 的时间达到 200ms)   //时间长短可根据需要自己定
{
   Flag=0;  //重新允许读取按键
}

那些用状态转换的人的目的应该不仅仅是防止一次按键处理多次吧,否则没道理用那么麻烦的状态转换。

使用特权

评论回复
19
ljolove| | 2010-1-30 18:31 | 只看该作者
LS,状态转换其实很通用的,并不是麻烦,使用起来也是很方便的,而且程序的可读性也比较强,其实你的程序跟我写的那个大概意思也差不多,但缺少了判断按键抬起这个状态,如果要是一直按着的话呢,LS的程序可能就不行了吧

使用特权

评论回复
20
icmap| | 2010-1-30 19:52 | 只看该作者
楼上,本帖讨论的是消抖,所以我也仅对消抖提出看法。我是不会把按键判断和消抖混在一起处理的。

使用特权

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

本版积分规则

10

主题

82

帖子

1

粉丝