祖平的笔记 https://passport2.21ic.com/?570078 [收藏] [复制] [RSS]

日志

单片机基础学-按键

已有 844 次阅读2010-9-18 16:22 |系统分类:单片机| 按键

                     按键总结篇


                                                 作者:HouZuping


在所有智能产品中,按键是最为常用的,所以按键程序的好坏很重要。以前我们在学校里学的按键检测方法都是不适用,很浪费时间,减少了CPU的效率。在大家的不断努力下,基本上编程都是用状态转移思想。用状态机思想编的按键检测程序也很多,像一些高手就写过很好的按键检测程序,下面我总结了用一个定时器的独立按键检测,能检测单击,连击,长击等。这个按键检测程序中包含了状态转移思想,时间片段思想,对象编程。


关于按键流程图我就不说了,这个大家都清楚。首先对按键进行一个数据结构定义。


struct KeyData


{


       unsigned char KeyValue;                     //按键返回值


       unsigned char LongClickFlag;          //长击标志


       unsigned char RunClickFlag;        //连击标志


       unsigned char JudgeRunFlag;           //软定时器的连击标志


       unsigned int  RunCount;                 //连击间隔时间计数


};


 


//按键常量的定义,根据具体的时钟来跟改


#define  KeyLongTime_2s       100  //    1/Fcy*timerValue*1000


#define  KeyRunClick_500Ms    40   //连击间隔时间大约是500ms左右


 


这个数据结构是把按键当成一个对象,这样每次按键按下后都会有一个返回值,其他标志都是按键需要其他功能功能。不管有几个独立按键,每个按键返回值都不同。需要几个按键时,就定义几个按键关键字。


//------------接口区


#define KeyOne   P1_0


#define KeyTwo   P1_1


 


//----------初始状态


#define IdleState   1


 


这里我只用了两个按键,其实本按键最为不好的地方就是按键检测和读取按键的值,每次移植时都需要修改,这里还需要修改,在读取按键值时,我可以采用关键字固定,是来确定按键返回值固定这种方式,但是我觉得没有必要,移植时就改吧,有的单片机直接支持位操作,有的不支持。


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


//键盘扫描接口部分,移植时需要修改


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


unsigned char KeySwap()


{


       if((KeyOne!=IdleState)||(KeyTwo!=IdleState))//按键数目判断状态,这里是位变量


       return 1;


       else


       return 0;


}


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


//读取接口值函数,移植时需要修改


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


unsigned char RdKeyValue()


{


       if(KeyOne!=IdleState)


       return 0x02;


       if(KeyTwo!=IdleState)


       return 0x03;


       else


       return 0;


}


 


下面来看看我主要的按键检测部分,也是最重要的部分,我在定时器内定义每隔12ms就去扫描一次按键,这样每个状态间隔的时间就是12ms,我就是利用这个时间来去抖动,我也是利用这个时间来进行连击和长击的判断。不说了,看程序吧


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


基于软定时器的通用模块的建立之一:独立按键通用模块


创建人  :侯祖平


创建日期:2010.7.01


博客网址:


欢迎大家到我的博客交流..........


*********************************************************************


 


       #include "EvenKey.h"


       #include "KeyBasic.h"


 


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


//状态变量


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


enum 


{


       KeyIdle = 0,


       KeyDownDelay,            //判断按键按下


       KeyWait,            //按下等待每隔一个软定时器,变量就加一


       Key,         //长击和短击判断


       KeyLongClick,              //若按下时间超出,则为长按


       KeyShortClick,             //


       KeyRunClick,        //连击,可以用一个变量来记录连击的次数


       KeyUp,                        //按键抬起


       KeyUpDelay,             //长击按下延时


       KeyOver


};


 


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


//键值扫描循环


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


void KeyLoop(struct KeyData *KeyNumber)                           


{


       static unsigned char KeyCurValue = 0 , KeyLastValue = 0;


       static unsigned char KeyState = 0;


       static unsigned int  LongCount = 0;


       switch(KeyState)


       {


              case KeyIdle :


                                   if(KeySwap())


                                   {


                                          KeyState = KeyDownDelay;


                                   }


                                   else


                                   {


                                          KeyState = KeyIdle;


                                   }


                                   break;


 


              case KeyDownDelay :


                                   if(KeySwap())


                                   {


                                          KeyState = KeyWait;


                                   }


                                   else


                                   {


                                          KeyState = KeyIdle;


                                   }


                                   break;


 


              case KeyWait :                                                        //有键按下,看是否是连击


                                   KeyCurValue = RdKeyValue();


                                   if((KeyCurValue==KeyLastValue)&&(KeyNumber->JudgeRunFlag))


                                   {                                                             //


                                      if(KeyNumber->RunCount < KeyRunClick_500Ms)


                                          {                                                      //则为连击


                                                 KeyNumber->RunCount = 0;


                                                 KeyNumber->JudgeRunFlag = 0;


                                                 KeyState = KeyRunClick;            //跳转到连击状态


                                          }


                                          else


                                          {


                                                 KeyNumber->RunClickFlag = 0;


                                                 KeyNumber->RunCount = 0;


                                                 KeyNumber->JudgeRunFlag = 0;


                                                 KeyState = Key;             //若不是继续判断


                                          }


                                   }


                                   else


                                   {


                                          KeyState = Key;     


                                   }


 


                                   break;


 


              case Key :


                                   if(KeySwap())


                                   {      


                                           KeyCurValue = RdKeyValue();


                                           LongCount ++;


                                           if(LongCount >= KeyLongTime_2s)


                                           {


                                                LongCount = 0;


                                                KeyState = KeyLongClick;   


                                           }


                                   }


                                   else


                                   {


                                          if(LongCount < KeyLongTime_2s)


                                           {


                                                LongCount = 0;


                                                KeyState = KeyShortClick;  


                                           }


                                   }


                    


                                   break;


 


              case KeyLongClick :                                       //长击


                                   #ifdef  HaveLongKey


                                   KeyNumber->LongClickFlag = 1;


                                   KeyNumber->KeyValue = KeyCurValue|0x80;


                                   #endif


                                   if(!KeySwap())                           //若按键抬起,则状态跳转


                                   KeyState = KeyUpDelay;


             


                                   break;


 


 


              case KeyRunClick :                                          //连击


                                   #ifdef  HaveRunKey


                                   KeyNumber->RunClickFlag = 1;


                                   KeyNumber->KeyValue = KeyCurValue|0x40;


                                   #endif


                                   if(!KeySwap())                           //若按键抬起,则状态跳转


                                   KeyState = KeyUpDelay;


             


                                   break;


 


              case KeyShortClick :                               //短击和连击的判断


                                   KeyNumber->JudgeRunFlag = 1; //连击判断启动标志位


                                   KeyNumber->KeyValue = KeyCurValue;


                                   KeyState = KeyUp;


                                                       


                                   break;


 


              case KeyUp :


                                   KeyNumber->RunClickFlag  = 0;


                                   KeyNumber->LongClickFlag = 0;


                                   KeyState = KeyOver;


                                   break;


 


             


              case KeyUpDelay:


                                   if(!KeySwap())                           //若按键抬起,则状态跳转


                                   KeyState = KeyOver;


 


                                   break;


 


              case KeyOver :                                               //结束


                                   KeyLastValue = KeyCurValue;     


                                   KeyState = KeyIdle;


                                   break;


                    


       }    


}


 


 


 


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


//end of file


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


其中连击的判断也是我写这个按键检测的真正目的,以前我都是用两个定时器来判断连击的,我在以前的程序添加了一个程序。


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


//参数传递函数,用于传递连击的时间间隔


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


void WrRunTime(struct KeyData *KeyNumber)


{    


        if(KeyNumber->JudgeRunFlag)


        {


             KeyNumber->RunCount ++;          //间隔计数启动


              if(KeyNumber->RunCount >= KeyRunClick_500Ms)


              KeyNumber->JudgeRunFlag = 0;  //若大于等待间隔,自动关闭标志


        }


        else


        {


             KeyNumber->RunCount = 0;


        }          


}


 


void WrRunTime(struct KeyData *KeyNumber)这个函数放在定时器中断函数中,在一次短击过后,程序会启动连击检测标志KeyNumber->JudgeRunFlag = 1; //连击判断启动标志位


在中断函数中,若标志KeyNumber->JudgeRunFlag = 1则启动计时。


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


//中断函数非常重要,一切以中断函数为参考


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


void Timer0Interrupt(void) interrupt 1


{


        TH0 = (65536-22000)/256;  //12MS


        TL0 = (65536-22000)%256;


        Run = 1;                   //时间片段标志位


        WrRunTime(&KeyNumber);              //连击时间判断


}


 


一旦检测到if(KeyNumber->RunCount >= KeyRunClick_500Ms)


则连击检测自动关闭。


 


我的长击和连击判断后并没有在返回值中加什么东西,只有返回了一个标志位。


所以在测试程序中,你需要写看标志是否至1


你的测试程序中需要写一下函数:


struct KeyData  KeyNumber;


//时间片段标志位


unsigned char Run = 0;


 


void TimerInit()


{


        TMOD = 0x01;


        TH0 = (65536-22000)/256;  //12MS


        TL0 = (65536-22000)%256;


        ET0 = 1;


        EA = 1;


        TR0 = 1;


}


//测试


void Loop_f(unsigned char KeyValue)


{


       switch(KeyValue)


       {


       }


}


//test


 


//test


 


void main()


{    


       TimerInit();


       while(1)


       {


              if(Run)


              {


                     Run = 0;


                     KeyLoop(&KeyNumber);


                     Loop_f(KeyNumber.KeyValue);


              }    


       }


}


 


 


路过

鸡蛋

鲜花

握手

雷人

全部作者的其他最新日志

评论 (0 个评论)