原创 嵌入式系统的按键扫描程序总结02

2010-3-30 22:07 3538 6 6 分类: MCU/ 嵌入式

https://static.assets-stash.eet-china.com/album/old-resources/2009/10/9/ef9fe711-a89a-4b78-856d-8254ed67ad3a.rar


这个是我的完整的源程序,在智林STM32板上使用,七个按键都有用到,除了OK键产生短击、长击和释放消息,其它六个键产生单击和连击消息。这些消息在液晶上显示。主程序中国有通过CAN接口和PC上上位机通信的部分,实现了一个简单的通信协议,可读性较差。


<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />2009109星期五16:23:31<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />


嵌入式系统键盘总结02


 


0、前言


       在前一篇文章里的程序实现的键盘的特征与电脑上的按键类似:每个键按下和释放时都能产生消息,能够识别多键同时按下和释放并分别产生消息,能够产生连击消息。举个例子:aaaaaaaaasssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss,前面这段是我先按下a,并不释放,又按下s时产生的。每次都能识别新按下的键,多键同时按下也能识别。


       不过一般的嵌入式系统中按键的特征与此不同:我试了一下我的手机键盘的按键特性,前面板上21个键具有3个特征,进入键、返回键和OK键产生按下和释放消息、不产生连击,手机程序在释放时进行处理,拨号键、上下左右方向键和数字键盘上12个键短按产生单击消息,长按时产生连击消息。关机键短按时产生返回消息,长按时产生关机消息。


       手机键盘不支持多键按下:我试了下,同时按下两个键,手机没有反应(当然如果没有同时按下,先按下的键有效);在已经有按下按下后,新按键无效,但旧的按下释放时,新的按键又有效了。还有一个有趣的现象,先按下一个键进入连击状态,比如数字键6,这时屏幕不断地输出6,如果又按下两个键,继续输出6,然后把数字6键释放,另两个键不松手,屏幕上还是继续输出6(我估计是手机按键处理的一个BUG,但是我也在我的键盘扫描程序中实现了,看看BUG是如何产生的,呵呵。)


       大部分嵌入式系统的键盘特征与此类似,在这里我用一个八位字节表示按键消息,低6位表示按键号,可以表示63个按键(0不用),高两位表示不同按键情况00表示单击按下或短击,01表示长击,10表示释放,这三个在程序中都实现了,11是留给按键双击用的,但我想了一下,实现双击会使程序复杂很多,按键处理的效率下降,因此暂时没有实现它。我觉得,如果要实现双击,用一个单独的任务来处理比较好,前面的文章里面介绍过,这里就不再叙述了。


 


1、以下是详细的程序分析


typedef enum{WaitForPress,KeyVerify1,KeyVerify2,WaitForRelease,KeyReleasing}KeyStateMachine;


const u8 KeyDefine[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};


//我的开发板总共用到了七个键分别是Up,Right,Left,Down,OK,PB2,PB3.


//产生的按键消息分别是17.


 


extern OS_EVENT *KeyOSQ;  【队列,在主程序中定义】


u8 KeyQueue[30]; 【按键消息存储点,是一个数组,循环使用】


u8 KeyCount;


u8 KeyValue; 【按键消息,在这个开发板上按下消息取值为1-7,释放消息取值81-87,长击消息取值41-47


 


 


u8 KeyCurrent;      //每次扫描得到的按键状态


u8 KeyOld;        //消抖时用到,与KeyCurrent配合使用。


u8 KeyPress;  //  当前的按键状态,用于简单控制,用于求取按键消息值。


 


u32 AutoRepeatFlag="0",AutoRepeatTimeL=0,AutoRepeatTimeS=0; 


//进入连击状态标志、连击准备计数和连击状态内时间计数


u32 NoKeyTime;      //无按键状态保持时间计数,


u32 KeyPressTime; //按键按下时间计数,在这里主要用于长击判断


 


u8 GetKeyState(void);  //不管是独立按键,还是矩阵键盘,得到的按键状态格式是一样的,一个键对应一位,按下对应位置位1,释放为0。如果16键以下,就用U1632键以下,就用U32,嵌入式键盘顶多也就这么多吧,再多就用专用键盘了。


void LCD_DisplayOn(void);  //这几个函数的实现前面的文章里有,这里就先删掉了。


void LCD_DisplayOff(void);


u8 GetKeyState(void)


 


void Task_KeyScan(void *pdata)


{


  u8 i;


  KeyStateMachine KeyFSM="WaitForPress";


  pdata=pdata;


 


for(;;)


 {


  OSTimeDly(1);


  KeyCurrent=GetKeyState();             //获取当前按键状态,某按键按下对应位为1


 


  switch(KeyFSM)                        //根据状态机进行散转


  {


    case WaitForPress:               //处于等待按键状态


         if(KeyCurrent==0)     // 没有按键时,进行无按键时间计数


          {


               NoKeyTime++;


               if(NoKeyTime>=3000)   //30s无按键,关闭显示。


                     { NoKeyTime="0";


                       DisplayOn=0;


                       LCD_DisplayOff();


                     }


              }


         else


         {  KeyOld=KeyCurrent;       //按键状态发生变化,有键按下


            KeyFSM=KeyVerify1;     //进入消抖确认状态,下一个10ms到来时,进行确认


         }


         break;


 


    case KeyVerify1:


         if( (KeyCurrent & KeyOld )==KeyOld )   //按键变化消抖确认


           { KeyFSM="KeyVerify2";


              }


         else


           { KeyFSM="WaitForPress";  //所取得按键状态与先前不同,视为干扰;


              }                                    //重新在保持状态下进行判断处理


         break;


 


    case KeyVerify2:


         if( (KeyCurrent & KeyOld )==KeyOld )    //按键确实按下了


           { KeyFSM="WaitForRelease";           //下一个状态为等待按键释放


 


                NoKeyTime=0;                           //取消无按键时间计数


                KeyPressTime=3;               //加上消抖时间按键,已经按下3个时钟了。


                AutoRepeatTimeL=3;    //给连击计数赋予初始值


                AutoRepeatFlag=0;              //每次有新的按键按下,连击标志复位


 


                KeyPress=KeyOld;       //把扫描键赋给KeyPress变量


 


                for ( i="0"; i<7; i++ )


                    { if ( KeyPress == KeyDefine )break;


                       }


                if (i<7)KeyValue=i+1;   //根据按键位定义,找到在按键列表中的位置


                                                         //按键消息等于位置+1,因为数组从0开始。


这里就是上面所说的BUG产生的地方,如果发现两个按键同时按下,KeyVale就会保持原理的值,也会阐释连击消息,如果要消除这种情况,可以在后面加一句。Else KeyValue="0".


          


                if (KeyValue!=5)        //5号按键在按键刚刚按下时,不产生按键消息。


                {


                  KeyQueue[KeyCount]=KeyValue; 


                  OSQPost(KeyOSQ,&KeyQueue[KeyCount]);      //发送到按键消息队列


                  KeyCount++;


                  if ( KeyCount>=30)


                     { KeyCount="0"; }


                 }


               }


          else


           {


                KeyFSM=WaitForPress;


              }


        break;


 


    case WaitForRelease:


         if( (KeyCurrent & KeyOld )==KeyOld )   //按键仍处于按下状态


           { KeyPressTime++;          //按键按下时间计数


 


                if (KeyValue!=5)        //5号按键实现长击与短击功能,其它键实现连击


                {


                       if( !AutoRepeatFlag )


                         { AutoRepeatTimeL++;


                              if( AutoRepeatTimeL>=50 )


                                { AutoRepeatFlag="1";


                                     AutoRepeatTimeL=0;


                                   }


                            }


                       else


                         { AutoRepeatTimeS++;


                              if ( AutoRepeatTimeS==10 )


                                 { AutoRepeatTimeS="0";


                                      KeyQueue[KeyCount]=KeyValue;    //连击消息。


                                     OSQPost(KeyOSQ,&KeyQueue[KeyCount]); //发送到按键消息队列


                                KeyCount++;


                                if ( KeyCount>=30)


                                   { KeyCount="0"; }


                                 }


                         }


                     }


              }           


         else


           { KeyFSM="KeyReleasing";  //所取得按键状态与先前不同,认为当前按键已经释放


              }                                    //进行消抖验证


         break;


    case KeyReleasing:


         if( (KeyCurrent & KeyOld )!=KeyOld )   //按键确实已经释放了


           { KeyFSM="WaitForPress";


 


                if( KeyValue==5 )       //5号键在这里根据按下时间的长短,判断长击和短击


                {


                              if ( KeyPressTime>50 ) //判断为长击。


                            {  KeyQueue[KeyCount]=KeyValue|0x40; //长击消息=短击消息|0x40


                                  OSQPost(KeyOSQ,&KeyQueue[KeyCount]); //发送到按键消息队列


                             KeyCount++;


                             if ( KeyCount>=30)


                                { KeyCount="0"; }


                             }


                       else


                            {  KeyQueue[KeyCount]=KeyValue;  //短击消息。


                                  OSQPost(KeyOSQ,&KeyQueue[KeyCount]); //发送到按键消息队列


                             KeyCount++;


                             if ( KeyCount>=30)


                                { KeyCount="0"; }


                             }


                }


                KeyPressTime=0;


 


                //在这里可以添加条件语句,决定是否输出按键释放消息,那些按键需要输出释放消息


                       if ( 1 )


                            {  KeyQueue[KeyCount]=KeyValue|0x80;//按键释放消息。单击消息与0x80相或。


                                  OSQPost(KeyOSQ,&KeyQueue[KeyCount]); //发送到按键消息队列


                             KeyCount++;


                             if ( KeyCount>=30)


                                { KeyCount="0"; }


                             }   


               //释放消息控制    


               }


         else


           { KeyFSM="WaitForRelease";  //所取得按键状态与先前相同,认为产生了一个干扰;


              }                                    //重新回到等待释放状态


         break;


       default:


         break;


  }


 }   


}



 


文章评论0条评论)

登录后参与讨论
我要评论
0
6
关闭 站长推荐上一条 /2 下一条