搜索
bottom↓
回复: 113

按键消抖和一次有效的思路问题!大家讨论下

[复制链接]

出0入0汤圆

发表于 2009-11-9 12:06:32 | 显示全部楼层 |阅读模式
我的思路是这样的:
   1.先用一个寄存器记录上次按键状态,一个寄存器存放新的按键状态。当两次按键状态不同时,新的按键状态取代旧的按键状态;当两次按键状态相同,则用计数器+1。若N(N自定)次状态一样,则认为该次按键有效,将按键有效的状态位置“1”。
   2.当读到按键有效状态位被置“1”后,再开始判断有效的是哪一个按键。此时只有0->1,或者1->0的变化才引起对应按键的作用。不会出现长按一直有效的情况。
   但是实现的时候还是觉得有些麻烦,不知道大家都怎样解决抖动和按键一次有效的问题?

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入0汤圆

发表于 2009-11-9 12:35:23 | 显示全部楼层
 一直都这么做的,习惯设1ms中断(容易算),连得到8次同样的键值就认为安定可读。

 有个可以偷懒的地方是:严格说每个按键有应该有独立的计数器,但实际上一般没必要,可以共用一个计数器,即检测到全部键值都安定时才去读。

出0入0汤圆

 楼主| 发表于 2009-11-9 12:59:07 | 显示全部楼层
恩,我就是把所有键值都看成整体的。这种读按键状态变化比较方便,但是处理按键的时候就有点麻烦啊,得逐位判断啊。。。仙猫同志怎么解决啊?

出0入0汤圆

发表于 2009-11-9 13:11:43 | 显示全部楼层
仙猫大师能否上传个按键处理程序让小弟参考一下啊?谢谢了!

出0入0汤圆

发表于 2009-11-9 13:43:34 | 显示全部楼层
 扫瑞,现在一时手头上没有很合适的,只找到一个模拟PC键盘对长按键的处理程序(在1kHz中断里被调通)供参考,晚上回去再翻下看看。


#define FIRST_RPT   500                 /* 按下后第1次重复时间 (ms) */
#define CONT_RPT    80                  /* 第2次之后的重复时间 (ms) */

void Int_SysTimer(void)                 /********************************/
{                                       /* Timer (TPU1.TGI1A, 1ms=1kHz) */
                                        /********************************/

    /*** Key-in 检测 *************************************************/

    if (new_Key && (new_Key == KeyIn())) {
        if (++cnt_Key == KEY_DET_TM) {
            Ctrl.key = new_Key;
        } else if (cnt_Key >= (KEY_DET_TM + FIRST_RPT + CONT_RPT)) {
            cnt_Key = (KEY_DET_TM + FIRST_RPT);
            Ctrl.key = new_Key;
        } else if (cnt_Key == (KEY_DET_TM + FIRST_RPT)) {
            Ctrl.key = new_Key;
        }
    } else if (KeyIn() != new_Key) {
        new_Key = KeyIn();
        cnt_Key = 0;
    }
    /********************************************************************/
}

出0入0汤圆

 楼主| 发表于 2009-11-9 13:53:46 | 显示全部楼层
我有一个简单的,只有两个按键
key_read:       //读键盘
in_key=PINB>>5;
if(new_key==in_key)
   {
    if(count==3)
       {
         count=0;
         sign=BIT(0);
         new_key=in_key;
       }
    else count++;
   }
else
   {
    count=0;
    new_key=in_key;
   }

key_process:    //处理按键
void key_process(void)
{
sign=0x00;    //标记位清除
if(new_key==BIT(0))
    {
        if(key_buff==BIT(0))
           refresh(number1,number2);
        else if(number1==0x0F)
              {
               number1=0;
               number2++;
               refresh(number1,number2);
               if(number2==0x0F)
              {
               number2=0;
               refresh(number1,number2);
              }
           else
              refresh(number1,number2);
           }
         else
          {
           number1++;
           refresh(number1,number2);
          }         
        }

else if(new_key==BIT(1))
      {
          if(key_buff==BIT(1))
          refresh(number1,number2);
          else if(number1==0)
             {
               if(number2==0)
               refresh(number1,number2);
               else
               {
                number1=0x0F;
                number2--;
                refresh(number1,number2);
               }   
             }
          else
            {
              number1--;
              refresh(number1,number2);
             }          
          }
          else refresh(number1,number2);
          key_buff=new_key;
}
一直用汇编,刚开始学C,写的比较乱。。。

出0入0汤圆

发表于 2009-11-9 15:58:35 | 显示全部楼层
仙猫大帅能不能给个4X4键盘的实际程,看了别人的看不出个所以然来啊~~

出0入0汤圆

发表于 2009-11-9 16:07:20 | 显示全部楼层
按键是基础,俺刚学会了用状态机

出0入0汤圆

发表于 2009-11-9 16:11:27 | 显示全部楼层
我也发个4*4(用PB口)程序,希望高手指点下写得好不好。


#define KEY                PINB
#define KEYSET                PORTB
#define KEYTIMEMAX        0XFF00
#define KEYLINE                0xf0         
#define KEYDDR                DDRB



/*
返回值:0  未有控键按下或按键按下未稳定
                1  按键处于按下状态
                2  按键松开
参数:*RKeyData  返回按键值  (可返回组合按键的值)
      *RKeyTime  返回按键按下的时间(用于判断是长按键或是短按键)
*/


uchar keySand(uchar *RKeyData,uint *RKeyTime)
{
    static uchar i=0;
    static uint KeyTime=0;
    KEYDDR=~(KEYLINE);                                                //设按键IO方向
    KEYSET=KEYLINE;                                                        //设按键IO初值
    *RKeyData=0x00;
    //i<2之前,按键未消抖动
    if(i<2)
    {
        if(KEY!=KEYLINE)
        {
             i++;
        }
        else
        {
            i=0;
        }
        return 0;                                                        //按键未按下或按下未稳定
    }
    else                                                                        //按键已消抖并且有按键按下
    {
        if(KEY!=KEYLINE)
        {
              if(i==2)
              {
                   i++;
              }
              *RKeyData=KEY;
              KEYSET=~(KEYLINE);
              KEYDDR=KEYLINE;
              _delay_us(2);
              *RKeyData|=KEY;
              if(KeyTime<KEYTIMEMAX)
           {
                   *RKeyTime=++KeyTime;
              }
              return 1;                                                //键处于按下状态
        }
        else
        {
             if(i==3)
             {
                  i=0;
                  KeyTime=0x0000;
                  return 2;                                        //按键已松开
             }
             else
             {
                  i=0;
                  return 0;                                        //未有按键按下或按键未稳定
             }
        }
    }
}
每 5MS 调用一次,可以很容易算出按键时间
如果是两个按键的组合按键的话比较易判断,只要返回值为 1 时,用多键按下的值替换少键按下的值即可

出0入0汤圆

发表于 2009-11-10 00:10:46 | 显示全部楼层
【6楼】kyughanum 初级玩家:
  抱歉,翻箱倒柜也实在找不回以前写的程序了。其实键扫描网上很容易搜到的,而且电路不同程序写法各异,处理方法也并不唯一,理清思路自己写比读别人的程序可能还更省事些。

出0入0汤圆

发表于 2009-11-10 08:28:44 | 显示全部楼层
 另外,并不存在一种“最好”的程序,要根据硬件条件灵活选择算法。比如CPU性能不太富裕时5~10ms中断一次,高端CPU时则可取1ms甚至更短时间中断;4x4键盘可一次扫描读完,也可用电容做硬件降噪,软件每次只读一行,节省等待过渡过程的时间。

出0入0汤圆

发表于 2009-11-10 09:21:03 | 显示全部楼层
仙猫大师啊,最主要是想跟你学习啊!

出0入0汤圆

发表于 2009-11-10 09:33:31 | 显示全部楼层
【11楼】kyughanum 初级玩家:
  被你说得诚惶诚恐的……,俺可是没一门专的,只是玩得杂一点罢了。软件权威有上官,硬件特别是模电有牛仔,系统软硬兼施的有鲨鱼等高手,另外好些平时不太插嘴的专业人士也不少。不如你把自己写的贴出来讨论,岂不收获更大?

出0入4汤圆

发表于 2009-11-10 09:44:15 | 显示全部楼层
送你一个程序,思路和你是一样的
比较中断,1ms

// Timer 0 output compare interrupt service routine
interrupt [TIM0_COMP] void timer0_comp_isr(void)
{
    register static UI8 keytemp;
    register static UI8 keycounter;
    register static UI8 keykeepct;
    register UI8 keyin;
    keyin = (PIND>>2) & 0x0F;
    if (TMPIN)
    {
        keyin |= (1<<6);
    }
    if (START)
    {
        keyin |= (1<<5);
    }
    if (CANCEL)
    {
        keyin |= (1<<4);
    }
    if (keyin==keytemp)
    {// 本次按键和上次相同
        keycounter++; // 计数值累加
        if (keycounter>=50)
        {// 50mS相同,一个周期
            keycounter=0;
            if (keytemp==NOKEY)
            {// 无按键,清除连续按下标志和设置按键释放标志
                gb_KeyRelease=1;
                gb_KeyKeep100mS=0;
                keykeepct=0;
            }
            else
            {// 有按键,设置按下标志
                gb_KeyPress=1;
                if(gui8_Keys == keytemp)
                {// 本次和上周期相同
                    keykeepct++;// 连续计数标志累加
                    if (keykeepct>=22)// 首次按下超过1.1S或连续按下超过100mS
                    {
                        keykeepct=20; // 置100mS累加的初值
                        gb_KeyKeep100mS=1; // 连续按下标志
                    }
                }
            }
            gui8_Keys = keytemp;
        }
    }
    else
    {// 按键有变化或抖动,计数清零,刷新缓存
        keytemp=keyin;
        keycounter=0;
    }
}

出0入0汤圆

发表于 2009-11-10 09:56:21 | 显示全部楼层
加个电容就ok了,从示波器上面看,波形没有抖动

出0入0汤圆

发表于 2009-11-10 10:05:20 | 显示全部楼层
晕啊~~仙猫大师也这样说~~那坛内都无高手了!

出0入0汤圆

发表于 2009-11-24 13:31:04 | 显示全部楼层
收藏了

出0入0汤圆

发表于 2009-11-24 13:40:39 | 显示全部楼层
如果按键有外部RC滤波电路,是不是不用软件消抖动了?

出0入0汤圆

发表于 2009-12-19 01:11:25 | 显示全部楼层
收藏了

出0入0汤圆

发表于 2009-12-19 12:19:24 | 显示全部楼层
谢谢。

出0入0汤圆

发表于 2009-12-19 12:52:10 | 显示全部楼层
mark 学习

出0入0汤圆

发表于 2009-12-19 23:12:25 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-12-20 00:03:48 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-12-21 10:52:33 | 显示全部楼层
我到愿意专为键盘矩阵准备一片单片机,比如2051。

使用线反转扫描法,代码倒是简介多了:

unsigned char KXH,KXL,TMP,KEY,i,DX;

TMP=0xF0;
P1=0xF0;
while(TMP==0xF0){TMP=P1&0xF0;}  //等待,直到有键按下
KXH=P1&0xF0;    //获取扫描码高四位
longdelay(1);   //延时消抖
               
TMP=0x0F;
P1=0x0F;
while(TMP==0x0F){TMP=P1&0x0F;}
KXL=P1&0x0F;   //获取扫描码低四位。

while(TMP!=0x0F){TMP=P1&0x0F;} //等到按键弹起时才继续
               
KEY=KXL+KXH;                //组合扫描码         
for(i=0;i<16;i++)
     {
        if(KEY==MTX)
                {
                        DX=i;
                        break;
                }

出0入0汤圆

发表于 2009-12-21 22:49:02 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-12-24 14:52:58 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-12-25 19:25:41 | 显示全部楼层
mark-------------

出0入0汤圆

发表于 2009-12-25 22:41:18 | 显示全部楼层
状态机一毫秒扫描一次,长按100MS后以10MS一次返回键值

//L0 为高
#define L0H  PORTB=PORTB|0b00000010;
//L1 为高
#define L1H  PORTD=PORTD|0b01000000;
//L2 为高
#define L2H  PORTD=PORTD|0b10000000;
//L0 为低
#define L0L  PORTB=PORTB&(~(1<<1));
//L1 为低
#define L1L  PORTD=PORTD&(~(1<<6));
//L2 为低
#define L2L  PORTD=PORTD&(~(1<<7));

//读H0-H2口电平
#define KEYDATA ((PIND>>2)&0b00000111)
void KEYBOARD_INT(void)
{
//H输入.L输出
DDRB=DDRB|0b00000010;  //L0 为输出
DDRD=DDRD|0b10000000;  //L1 为输出
DDRD=DDRD|0b01000000;  //L2 为输出

DDRD=DDRD&0b11100011;   //H0,H1,H2为输入
PORTD=PORTD|0b00011100; //H0,H1,H2为上拉

L0H
L1H
L2H

}


void KEYBOARD(unsigned char *DATA)
{
unsigned char JIANZHI[3][8]={
                             {7,6,5,4,3,2,1,0},
                             {14,13,12,11,10,9,8,0},
                                                         {21,20,19,18,17,16,15,0}
                                                         };
L0L
DATA[3]=JIANZHI[0][KEYDATA];
L0H  
L1L
DATA[3]=JIANZHI[1][KEYDATA]+DATA[3];
L1H
L2L
DATA[3]=JIANZHI[2][KEYDATA]+DATA[3];
L2H



if(DATA[3]!=0)
{
if(DATA[1]==DATA[3])
   {
        if(DATA[2]<100) //长按100MS后
              {
               DATA[2]++;
               if(DATA[2]==10)//10MS一次返回键值
                  DATA[0]=DATA[1];
               else
                 DATA[0]=0;
               }
                  
        else
           {
                 
                if(DATA[4]==10)
                  {
                   DATA[0]=0;
                       DATA[4]=0;        
                  }            
            DATA[4]++;
                if(DATA[4]==10) DATA[0]=DATA[1];
                       
               }
        }          
                  
else
    {
         DATA[1]=DATA[3];
         DATA[2]=0;
         DATA[4]=0;
         DATA[0]=0;
        }
}       
else
   {
     DATA[2]=0;
         DATA[4]=0;
         DATA[0]=0;
   }

               
}

出0入0汤圆

发表于 2009-12-25 23:58:15 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-12-26 02:45:47 | 显示全部楼层
学习了~~~~~~~``

出0入0汤圆

发表于 2009-12-26 09:26:01 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-12-26 10:33:05 | 显示全部楼层
基于滴答定时器和状态机,结合楼主思想应该就是很完美了

出0入0汤圆

发表于 2009-12-26 11:34:42 | 显示全部楼层
马潮的书上有一章讲键盘的,用定时器+状态机实现,思路简单,实现的代码也少

出0入0汤圆

发表于 2009-12-26 22:39:22 | 显示全部楼层
去抖动没人用硬件了

出0入0汤圆

发表于 2009-12-26 23:03:45 | 显示全部楼层
顶一个

出0入0汤圆

发表于 2009-12-27 17:10:54 | 显示全部楼层
学习了!

出0入0汤圆

发表于 2009-12-27 23:02:56 | 显示全部楼层
我是在自己写的协作分时里边定时扫描的。
可以实现 keydown,keyup,keypress。
基本上就是把按键的状态采集起来,比如低有效的时候,采集回来10110000就说明有一次抖动但是按键已经按下了。基本上是10000000表示keydown,00000001表示keyup,00000000表示keypress。
就是维持一个队列的开销比较大。

出0入0汤圆

发表于 2009-12-28 13:20:31 | 显示全部楼层
发个 抄来的 代码,感觉实用
#define        KEYDELAY        250
#define        KEYLOOP                15
unsigned char key_bak=0; //上次按键值,初值只要不属于键盘扫描值中的 就可以
unsigned char key_temp=1;//键值临时存放变量
unsigned char key_old=2; //按下键的年龄
unsigned char key_data=3;//存放键值
bit key_ok=0;                           //有有效键按下的标志  1为有,处理键值后 置0
void KeyScan(void)                    //键扫描
    {
        if((add==0)||sub==0)//本次扫描可能有键按下
                {_nop_();_nop_();_nop_();
                if((add==0)||sub==0)   
                {//本次扫描确认有键按下
                        if(add==0)
                                key_temp=0x88;
                        else
                                key_temp=0x99;
                if (key_temp == key_bak)
                    {//和上次扫到的相同
                    key_old++;//年龄老化+1 如果不释放,就1次有效,把此句//掉就可以
                    if (key_old==KEYDELAY)  //
                        {//老化到头(死了),当做有按了一次(重活一生)
                        key_ok=1;//按键有效标志(新生命开始)
                                        key_data=key_temp;//fnKeyCode(key_temp);    //键解码
                        key_old=KEYDELAY-KEYLOOP; //以后重复的年龄老化初值不在是0,加快以后的重复速度
                            }
                        }
                else
                    {//和上次扫到的不同
                                key_data=key_temp;//新键值放到key_data中
                    key_old=0;//老化年龄=0
                    key_bak=key_temp;//保存本次扫描码
                                key_ok=1; //置有 有效按键标志
                        }
                     }
                }
    else                           
        {//本次扫描无键按下
                key_bak=0;//老键值=0;避免松开后再按同一键
        key_old=0;//老化年龄=0//key_ok=0;
            }
}

出0入0汤圆

发表于 2009-12-28 15:20:30 | 显示全部楼层
学习了  高手们都谦虚啊

出0入0汤圆

发表于 2009-12-28 18:28:06 | 显示全部楼层
学习了

出0入0汤圆

发表于 2010-1-22 11:16:08 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-1-23 19:58:03 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-1-23 21:39:03 | 显示全部楼层
在大部分的程序中,100ms扫描一次就可以了。
    如果第一次和第二次的键值一样,可以认为是按了2次键,也可以认为是重复键,这要看灵敏度如何定义。
    更短的扫描时间可以到50ms,再短的话,纯属浪费CPU执行时间。因为按键灵敏度能达到这个级别就够了。所谓的消抖,是指这方面要注意,而不一定要按教科书中说的那么去做。   
    “实践是检验真理的唯一标准”,“反对本本主义”。

出0入0汤圆

发表于 2010-4-9 08:59:53 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-4-9 09:07:01 | 显示全部楼层
哈哈,都是精华。

出0入0汤圆

发表于 2010-4-9 10:13:07 | 显示全部楼层
按下时,加电容充电,
松开时,电容快速方电。

电容的大小决定按键的灵敏度,这样不好吗?
非要用软件来解决吗?

出0入0汤圆

发表于 2010-4-9 10:54:43 | 显示全部楼层
#define LP 200
void key_get(void)
{
        key_now=key_scan();
        key_st=0;
        if(key_now==0&&key_prev==0)key_cnt=0;
        if(key_now==key_prev&&key_now)key_cnt++;
        if(key_cnt>60000)key_cnt=1000;
        if(key_cnt==10)key_st=0x10;//短按
        if(key_cnt>LP&&key_cnt%4==0)key_st=0x20;//长按
        if(key_cnt>10&&key_cnt<=LP&&key_prev&&key_now==0)key_st=0x40;//短按释放
        if(key_cnt>LP&&key_now==0)key_st=0x80;//长按释放
        if(key_st)key_val=key_st|key_prev;else key_val=0;
        key_prev=key_now;
}
key_scan()为键值扫描函数
key_val高4位储存4个状态,低4位储存键值

出0入0汤圆

发表于 2010-4-12 02:08:04 | 显示全部楼层
这个思路还是初次见 支持

出0入0汤圆

发表于 2010-4-12 08:02:46 | 显示全部楼层
MARK 按键扫描

出0入0汤圆

发表于 2010-4-15 00:14:38 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-5-17 22:38:15 | 显示全部楼层
学习ing

出0入0汤圆

发表于 2010-5-18 11:51:04 | 显示全部楼层
很好的思路,学习了

出0入0汤圆

发表于 2010-5-18 12:33:55 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-5-19 12:54:50 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-5-19 16:07:13 | 显示全部楼层
有空看看UCOSII作者所著的<嵌入式系统模型>

出0入0汤圆

发表于 2010-5-19 16:13:17 | 显示全部楼层
简单的流程:

  switch(key_state)
  {
    case key_idle:  //(key_state==0)
      if(有键按下==true)
      {
        key_state++;
      }
      break;
    case key_down:
      if(有键按下==true)
      {
        key_code=key_read();
        key_state=key_repeat;
      }else
      {
        key_state=key_idle;
      }
      break;
    case key_repeat:
      //可加入时间重复时间控制
      if(有键按下==true)
      {
        key_code=key_read();
      }else
      {
        key_state=key_idle;
      }
      break;
   default:
      key_state=key_idle;
      break;
}


个人经验,每隔50mS左右调用一次就能可靠的读取按键.

出0入0汤圆

发表于 2010-6-18 13:07:18 | 显示全部楼层
不错

出0入0汤圆

发表于 2010-12-27 23:37:31 | 显示全部楼层
记号下

出10入10汤圆

发表于 2010-12-28 08:17:08 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-12-28 08:36:45 | 显示全部楼层
mark

出100入0汤圆

发表于 2010-12-28 09:03:11 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-12-28 10:44:29 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-12-28 23:01:43 | 显示全部楼层
mark!

出0入0汤圆

发表于 2010-12-29 17:31:22 | 显示全部楼层
学习

出0入0汤圆

发表于 2010-12-29 18:33:27 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-12-30 09:19:41 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-12-30 12:45:54 | 显示全部楼层
键盘--mark

出0入0汤圆

发表于 2010-12-30 20:57:15 | 显示全部楼层
太棒了 思路很多啊 mark

出0入0汤圆

发表于 2010-12-30 21:52:47 | 显示全部楼层
收藏

出0入0汤圆

发表于 2010-12-30 22:33:17 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-12-30 22:36:48 | 显示全部楼层
收益匪浅啊 收藏了!

出0入0汤圆

发表于 2011-9-6 10:37:45 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-9-7 00:41:35 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-10-5 20:43:56 | 显示全部楼层
#define KEYDELAY 250
#define KEYLOOP 15
unsigned char key_bak=0; //上次按键值,初值只要不属于键盘扫描值中的 就可以
unsigned char key_temp=1;//键值临时存放变量
unsigned char key_old=2; //按下键的年龄
unsigned char key_data=3;//存放键值
bit key_ok=0;    //有有效键按下的标志  1为有,处理键值后 置0
void KeyScan(void)                    //键扫描
    {
if((add==0)||sub==0)//本次扫描可能有键按下
{_nop_();_nop_();_nop_();
if((add==0)||sub==0)     
        {//本次扫描确认有键按下
if(add==0)
key_temp=0x88;
else
key_temp=0x99;
        if (key_temp == key_bak)
            {//和上次扫到的相同
            key_old++;//年龄老化+1 如果不释放,就1次有效,把此句//掉就可以
            if (key_old==KEYDELAY)  //
                {//老化到头(死了),当做有按了一次(重活一生)
                key_ok=1;//按键有效标志(新生命开始)
key_data=key_temp;//fnKeyCode(key_temp);    //键解码
                key_old=KEYDELAY-KEYLOOP; //以后重复的年龄老化初值不在是0,加快以后的重复速度
             }
         }
        else
            {//和上次扫到的不同
key_data=key_temp;//新键值放到key_data中
            key_old=0;//老化年龄=0
            key_bak=key_temp;//保存本次扫描码
key_ok=1; //置有 有效按键标志
         }
      }  
}
    else                           
        {//本次扫描无键按下
key_bak=0;//老键值=0;避免松开后再按同一键
        key_old=0;//老化年龄=0//key_ok=0;
     }
}

出0入0汤圆

发表于 2011-10-5 21:55:20 | 显示全部楼层
回复【73楼】zyw19987
-----------------------------------------------------------------------

想不到键盘消抖的方法挺多的啊

出0入0汤圆

发表于 2011-10-11 14:23:38 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-10-11 14:40:33 | 显示全部楼层
火呀!mark一下!

出0入0汤圆

发表于 2011-10-11 15:29:54 | 显示全部楼层
学习了mark一下!
大家都看到

出0入0汤圆

发表于 2011-10-11 16:12:39 | 显示全部楼层
码住。

出0入0汤圆

发表于 2011-10-18 00:11:46 | 显示全部楼层
马克

出0入0汤圆

发表于 2011-10-18 01:15:47 | 显示全部楼层
没有系统的直接软件延时,有系统的用系统定时器。

出0入0汤圆

发表于 2011-10-18 02:19:34 | 显示全部楼层
Mark~

出0入0汤圆

发表于 2011-10-18 08:12:45 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-10-18 08:19:10 | 显示全部楼层
实用 mark

出0入0汤圆

发表于 2011-10-18 08:55:53 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-10-18 09:23:20 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-10-18 12:35:06 | 显示全部楼层
一个按键就让你们发挥成这样,有料

出0入0汤圆

发表于 2011-10-29 08:12:48 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-10-29 09:56:48 | 显示全部楼层
mark..

出0入0汤圆

发表于 2011-10-30 22:05:52 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-10-31 08:44:39 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-11-2 10:04:44 | 显示全部楼层
嘿,学习阶段都是用延时来的,MARK下以后再学

出0入0汤圆

发表于 2011-11-29 22:20:50 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-11-29 23:55:21 | 显示全部楼层
void time0(void) interrupt 1 using 1//10mS中断一次
{
        TH0=DTH0;
        TL0=DTL0;
        WX0B=WX0A&~DX0;//消抖
        WX0A=~DX0;//消抖,DX0来自IO口
}                                                                       
void in(void)//放在大循环的开始处,用来取上下沿
{
        WXP0=WX0&~WX0B;//上升沿
        WXF0=WX0B&~WX0;//下降沿
        WX0=WX0B;//当前键值
}
我也来发一个,绝对简单好用,

出0入0汤圆

发表于 2011-11-30 00:14:47 | 显示全部楼层
十在太好了,学习了

出0入0汤圆

发表于 2011-11-30 07:53:38 | 显示全部楼层
mk

出0入0汤圆

发表于 2011-11-30 09:25:13 | 显示全部楼层
MARK  都很有思路

出0入0汤圆

发表于 2011-11-30 09:26:50 | 显示全部楼层
我也发一个,AVR设计的,定时器CTC中断(10ms)执行线翻转法扫描,直到按键弹起时才算扫描结束,键盘矩阵在B端口。
扫描过程全在中断中完成,相当于底层驱动。

初始化的状态:
PORTB=0xF0; //初始状态:高四位输入带上拉,低四位输出0
DDRB=0x0F;

unsigned char KEYH,KEYL,CNT,KEY;
bit F1,FLAG;
interrupt [TIM0_COMP] void timer0_comp_isr(void)
    {
        unsigned char i;
        KEYH=PINB&0xF0;         //读取高四位状态
        if(KEYH!=0xF0)          //如果有键按下
            {
                CNT++;         //计数
            }
        if(CNT>5)              //采样5次(50ms后)不变,认为有可靠按键
            {
                CNT=0;
                PORTB=0x0F;     //翻转,设置高四位输出0,低四位输入带上拉。
                DDRB=0xF0;               
                #asm("nop")
                KEYL=PINB&0x0F;  //读取低四位状态
                F1=1;
                PORTB=0xF0;     //恢复初始状态:高四位输入带上拉,低四位输出0
                DDRB=0x0F;   
                KEY=KEYH+KEYL;    //组合为扫描码         
            }
        if(F1)
            {
                KEYH=PINB&0xF0;         //读取高四位状态
                if(KEYH==0xF0)          //如果按键弹起,过程结束
                    {      
                        F1=0;
                        FLAG=1;         //设置标志
                    }            
            }
    }

每次中断时,都执行其中一个if选项中的内容,这样的好处是单片机速度越快,中断程序需要时间越少,即CPU负担越小。在主程序中只需要检查FLAG标志,只要其为1就表示有按键,然后读取KEY即可。

(原文件名:key.PNG)

出0入0汤圆

发表于 2011-12-2 20:33:57 | 显示全部楼层
来学习的!

出0入0汤圆

发表于 2011-12-3 10:34:47 | 显示全部楼层
mark
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-27 06:42

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表