当前位置: 网站首页 > 技术应用 > 单片机应用

浅谈按键消抖

不管设计一个什么作品,按键总是少不了的,对于按键你知道那些呢?

  通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了不产生这种现象而作的措施就是按键消抖。


 图片关键词
 

    图1

抖动时间

  抖动时间的长短由按键的机械特性决定,一般为5ms~10ms。这是一个很重要的时间参数,在很多场合都要用到。

  按键稳定闭合时间的长短则是由操作人员的按键动作决定的,一般为零点几秒至数秒,大家可以用示波器测试一下。键抖动会引起一次按键被误读多次。为确保CPU对键的一次闭合仅作一次处理,必须去除键抖动。在键闭合稳定时读取键的状态,并且必须判别到键释放稳定后再作处理。

方法

按键的消抖,可用硬件或软件两种方法。

硬件消抖

  在键数较少时可用硬件方法消除键抖动。下图所示的RS触发器为常用的硬件去抖。

图片关键词
 

图2

   图中两个“与非”门构成一个RS触发器。当按键未按下时,输出为1;当键按下时,输出为0。此时即使用按键的机械性能,使按键因弹性抖动而产生瞬时断开(抖动跳开B),中要按键不返回原始状态A,双稳态电路的状态不改变,输出保持为0,不会产生抖动的波形。也就是说,即使B点的电压波形是抖动的,但经双稳态电路之后,其输出为正规的矩形波。这一点通过分析RS触发器的工作过程很容易得到验证。

  另一种硬件消抖的方法利用电容的放电延时,采用并联电容法,也可以实现硬件消抖,如图3所示:

图片关键词
 

图3

软件消抖

   如果按键较多,常用软件方法去抖,即检测出键闭合后执行一个延时程序,5ms~10ms的延时,让前沿抖动消失后再一次检测键的状态,如果仍保持闭合状态电平,则确认为真正有键按下。当检测到按键释放后,也要给5ms~10ms的延时,待后沿抖动消失后才能转入该键的处理程序。还可以利用定时器中断来消抖。

  下面再介绍一种按键消抖的方法:利用switch()结构,程序设计如下:

无延时的软件消抖
 

 

/*********************************************
名称:键盘扫描子函数
功能:在按键稳定期内判断键值,并返回键值
**********************************************/
uchar keyscan(void)
{
static char key_state = 0;
static char key_value = 0;
uchar key_press, key_return = 0;
key_press=turn_left&turn_right;     //读按键I/O电平
switch (key_state) 
{
   case 0 :   // 按键初始态
    if (key_press==0) key_state = 1; // 键被按下,但需要确认是否是干扰
    break;
   case 1 : // 按键确认态
    if (key_press==0)
//如有键按下则不是干扰,判断键值
    {       if(turn_left==0)   //判断是哪一个按键被按下
                 key_value=1;       //按键较多时可采用switch选择结构
             else if(turn_right==0)
                 key_value=2;
             else  key_value=0;
           key_state = 2; // 状态转换到键释放态
    }
    else  key_state = 0; // 按键已抬起,属于干扰,转换到按键初始态
    break;
   case 2 :
    if (key_press==1) 
    {
       key_return=key_value;//按键释放后再输出键值 
       key_value=0;
       key_state = 0; //如果按键释放,转换到按键初始态
    }  break;
}return key_return; //返回键值
}
/*********************************************
名称:按键处理子函数
功能:
**********************************************/
void key_operation(void)
{    
    switch (keyscan())      //根据键值不同,执行不同的内容
    { case 1:
    hight_votage-=1;
    if(hight_votage<5)
     hight_votage=5; break;
     case 2:
    hight_votage+=1;
    if(hight_votage>25)
     hight_votage=25; break;
    default :break;
    } 
}

  只要有按键就一定要想到消抖,总之不管是硬件消抖还是软件消抖,在脑海里始终要想到按键按下时出现图一的情景,然后再进行相应的设计。

几个好的按键设计的总结
对于多个按键的设计常用思路是: 按照面向过程的编程方式, 将数据与过程分离. 把和按键状态相关的东西比如按键功能统统塞到结构里, 把消抖的代码放在一个函数中。这里介绍按键设计的几种方法:

1、 矩阵键盘

矩阵键盘程序设计

4×6的矩阵键盘程序设计采用的是扫描法,程序比较精简,定义行值为i(0~3),列值为j(0~5),当有键按下时返回按键位置(j-4)+6×i,当没有键按下时返回32,程序如下。

/*******************************************

函数名称:scan_key

    能:【4X6】矩阵按键扫描模块

    数:无

返回值  t或者32

********************************************/

unsigned char scan_key(void)

{

  unsigned char code biao[10]={0xfe,0xfd,0xfb,0xf7,0xe0,0xd0,0xb0,0x70,0x01,

                               0x02,};

  static unsigned char i=0;//寄存器变量,此变量也可以定义为全局变量,效果一样

  P1=biao[i];//把值赋到I/O口,键盘接口。

  temp=P1&0xf0;

  temp2=P3&0x03;

  if(((temp!=0xf0)&&(temp2==0x03))||((temp==0xf0)&&(temp2!=0x03)))//判断是

  否有键按下,以及按下键的位置

   {

     for(j=4;j<8;j++)//列扫描

     if((temp==biao[j])) return (t=(j-4)+6*i);//有键按下,返回键的位置

     for(j=8;j<10;j++)

     if((temp2==biao[j])) return (t=(j-4)+6*i); //有键按下,返回键的位置

    }

  i++;//行扫描

  i&=0x03;

  return(32);

}

 

2、 ADC按键

 

图片关键词

ADC按键的优点是节省IO口,但是需要调配好电阻值,个人觉得有点麻烦。

 

3、 并入串出按键

 一种并入串出带中断易扩展的按键输入方案74165驱动电路如图,输出直接在IO口用电平显示

图片关键词

4、 4*3键盘并且复用端口很牛

图片关键词

 

 

/********************************************************
 *
 *   4*3矩阵按键读写函数
 *
 ********************************************************/
//按键读写程序
//行1 P65//行2 P62//行3 P51//行4 P67 P5:1 P6:257
//列1 P63//列2 P64//列3 P66
//按键解码
//行4:0,1,2;行1:3,4,5;行3:6,7,8;行2:9,10,11;
//*0#   123   789   456   
const uchar key_rel[]={0x0a,0x00,0x0b,0x01,0x02,
0x03,0x07,0x08,0x09,0x04,0x05,0x06}; 
void scan_key(void)
{uchar i;
keybuf=0xff;
get_bit=0;
/*方法一:行输出,列输入,影响AT24C02,led闪烁
//睡眠唤醒,欠压检测,声光指示的端口方向不需改动
//行输出0
//P5CR&=0x0d;//0000 1101初始化P5全为输出口
P5CR=0xf0;
P6CR&=0x5b;//0101 1011
//列输入1
P6CR|=0x58;//0101 1000/
//方法二:列输出,行输入,列1串10K,输出不了低电平可能识别不了
//不会影响AT24C02,led指示,其它操作需要从新定义端口方向
//列输出0
P6CR&=0xa7;//1010 0111
//行输入1
P5CR|=0x02;//0000 0010
P6CR|=0xa4;//1010 1000*/
/*方法三:先使用方法二扫描列,最后列1单独读写
//列1读写实验
P5CR=0xf0;
P6CR&=0x5b;//0101 1011
//列输入1
P6CR|=0x58;//0101 1000/
KH1=0;KH2=0;KH3=0;KH4=0;
if(KL1==0)keybuf=0x01;//列1有按键按下
while(KL1==0);
//列23,读写*/
//按键读写程序终结版,低电平扫行2,行全拉低
//行输出0
P6CR&=0x5b;//0101 1011
//列输入1
P6CR|=0x58;//0101 1000/
for(i=0;i<4;i++)
  {switch(i)
    {//无AT24C02操作尽量不开启KH4给AT24C02供电
    case 0:KH1=1;KH2=1;KH3=1;KH4=0;break;//扫描行四
    case 1:KH1=0;KH2=1;KH3=1;KH4=0;break;//扫描行一
    case 2:KH1=1;KH2=1;KH3=0;KH4=0;break;//扫描行三
    //最后扫描红绿LED公共端,全部拉低,全扫描无LED点亮
    case 3:KH1=0;KH2=0;KH3=0;KH4=0;break;//扫描行二
    }
  delay(200);
  if(KL1==0){keybuf=0x00;get_bit=1;break;}
  if(KL2==0){keybuf=0x01;get_bit=1;break;}
  if(KL3==0){keybuf=0x02;get_bit=1;break;}
  }
if(get_bit==1)//获取按键值
  {get_bit=0;
  //延时去除抖动
  //delay(250);//
  //扫描状态已经保存
  switch(keybuf)
    {case 0x00:if(KL1==0)get_bit=1;;break;
    case 0x01:if(KL2==0)get_bit=1;;break;
    case 0x02:if(KL3==0)get_bit=1;;break;
    default:keybuf=0xff;
    }
  if(get_bit==1)
    {keybuf=i+i+i+keybuf;//解码按键
    i=key_rel[keybuf];//解码按键
    keybuf=i;//获取按键值
    //按键声光指示
    buzzer();
    //等待按键弹起
    KH1=0;KH2=0;KH3=0;KH4=0;
    delay(80);
    while((KL1&&KL2&&KL3)==0);
    }
  else keybuf=0xff;
  }
else keybuf=0xff;//无效按键
//行2读写无效分析
//TCC引脚下CONT指令0x0*
}
/********************************************************
 *
 *   END  key_scan
 *
 ********************************************************/ 

 

点击次数:  【关闭
  • 壹零零单片机©版权所有 2008-2023 粤ICP备17151077号

Powered by  xinfuke  5.2.5 ©2008-2024  www.100mcu.com