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

2010-3-30 22:07 3830 7 8 分类: MCU/ 嵌入式

点击下载智林STM32开发板的原理图


 


<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />2009107星期三20:22:08<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />


标题:嵌入式系统键盘扫描程序设计总结01


 


一、概述


       键盘是嵌入式系统中常用的构件,是人机界面的重要组成部分。近来,我查阅了几本书,并在网上搜索了一些关于键盘扫描的文章,感觉收获还是蛮大的。前面已经写了一些关于各种键盘工作方式的实现,接下来还要写一下总结。


       下面有我写的详细的键盘扫描程序,是基于智林STM32开发板的。


二、键盘扫描程序的关键问题


1、在ucos操作系统中完成


       操作系统有很好的任务调度和时间管理特性,在应用编程时有极大的好处。比如在键盘扫描任务中,可以很方便的实现每10ms扫描一次。


2、基于状态机


       采用状态机能使程序的思路比较清晰。


3、按键状态表示和按键消息格式


   在这里使用的键盘是线式键盘,总共七个键:摇杆五个键加上右下角两个按键,采用一个8位的U8型变量即可表示每个按键的状态,每个按键的状态对应变量中的一位,按下时对应位为0


   产生按键按下和释放消息时,对应的消息格式通过循环扫描得出,这个到具体的程序再分析。


4、该键盘扫描程序实现的功能


1可以有选择性的实现单击和连击


2)可以在无键盘操作时实现关闭液晶显示。


3)支持多键识别,几个键同时按下时,对每个键都可以发出键盘消息,且对按键编码值较大的按键支持连击。在每个键释放时,都能产生相应的消息。


4)我比较了一下,键盘输入特性跟PC键盘比较相似。组合按键功能可以在按键处理程序中实现。


三、详细的程序和分析


 


typedef enum{KeyKeepState,KeyVerify1,KeyVerify2}KeyStateMachine;


const u8 KeyDefine[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};最后一个没用到


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


//键值大的优先。


 


extern OS_EVENT *CanSendOSQ;  //按键用于我的CAN处理程序


extern OS_EVENT *CanRecOSQ;


extern OS_EVENT *KeyOSQ;             //按键消息队列


 


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


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


u8 KeyPress;  //  当前的按键状态,主要是与KeyLast配合求出新按下的键和新释放的键。


u8 KeyLast;  //记忆上次按键变化时的有效值


u8 KeyDown,KeyUp;  //新按下的键和新释放的键。


u8 KeyValue;  //连击按键消息值


 


u32 LongTimeCount="0",LongTimePress=0,ShortTimeCount=0; 


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


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


 


u8 KeyQueue[30];  按键消息所用的数值,循环使用


u8 KeyCount;


 


void LCD_DisplayOn(void)


{


  TIM_Cmd(TIM2, ENABLE);  //智林stm32板上显示器的电源由PWM滤波产生,故控制


}                                                      //定时器的开启和关闭即控制了显示器。


void LCD_DisplayOff(void)          //这里采用的是ST的库函数TIM_Cmd();


{


  TIM_Cmd(TIM2, DISABLE);


}


 


u8 GetKeyState(void)     //这里是线键盘,通过读取每个IO口状态,                              


//直接设置按键状态变量对应位


{  u8 KeyTemp="0";


  if ( GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11) ==RESET )


     { KeyTemp |= (1<<0); }    //哪一位电平为零,按键对应状态为设置为1.


  if ( GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12) ==RESET )


     { KeyTemp |= (1<<1); }


 


  if ( GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13) ==RESET )


     { KeyTemp |= (1<<2); }


  if ( GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14) ==RESET )


     { KeyTemp |= (1<<3); }


   if ( GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_15) ==RESET )


     { KeyTemp |= (1<<4); }


 


  if ( GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_13) ==RESET )


     { KeyTemp |= (1<<5); }


  if ( GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) ==RESET )


     { KeyTemp |= (1<<6); }


  return ( KeyTemp );          //矩阵键盘也可以采用相同的方法设置按键状态位        


}                                 //在王田苗的《嵌入式系统设计与实例开发》上由16位矩阵键盘的


                                   //的扫描程序,最后得到的键盘扫描码与此类似。


 


void Task_KeyScan(void *pdata)


{


  u8 i;


  KeyStateMachine KeyFSM="KeyKeepState";


  pdata=pdata;


for(;;)


 {


  OSTimeDly(1);          该键盘扫描任务每个10ms执行一次;


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


 


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


  {


    case KeyKeepState:               //处于保持状态


         if(KeyCurrent==KeyOld)   // 按键保持状态:新得到键与旧状态相同


          {


               if( KeyCurrent==0 )            //处于无按键状态:超过10s关闭显示器


                  {  NoKeyTime++;


                        if(NoKeyTime>=1000)


                          { NoKeyTime="0";


                               DisplayOn=0;   //显示状态标志,在按键处理任务终于恢复显示。


                               LCD_DisplayOff(); //关闭显示


                             }


                     }


               else if( KeyValue==5)          //条件语句中对应的数值为要实现连击的键,如果要求


                  {                          //所有键都具备连击功能,则改为KeyValue != 0


                       NoKeyTime=0;


                       if( !LongTimePress )  // 连击时间计数未到


                          {    LongTimeCount++;  //连击时间计数


                                   if ( LongTimeCount>=48 )


                                       { LongTimePress="1"; //超过0.5s,设置连击标志。


                                            LongTimeCount=0;


                                            ShortTimeCount=0;


                                          }


                             }


                       else


                         {


                              ShortTimeCount++;             //连击计时准备


                              if (ShortTimeCount==5)  //50ms时间到,发出按键信息


                                 {


                                        KeyQueue[KeyCount]=KeyValue; // KeyValue为新按键中键值较大者;


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


                                        KeyCount++;


                                        if ( KeyCount>=30)


                                           { KeyCount="0"; }


                                          ShortTimeCount=0;


                                   }


                            }


                      }


                }


         else


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


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


         }


         break;


 


    case KeyVerify1:


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


           { KeyFSM="KeyVerify2";


              }


         else


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


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


         break;


 


    case KeyVerify2:


         if(KeyCurrent==KeyOld)   //按键确实出现了变化


           { KeyFSM="KeyKeepState";


                NoKeyTime=0;


 


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


                KeyDown=(~KeyLast)&(KeyPress);  // 现在按下的键与以前未按下的键相与,得到新按键


                KeyUp=KeyLast&(~KeyPress);  //以前按下的键与现在未按下的键相与,得到新释放的键


 


                KeyLast=KeyPress;       //将稳定的现在按键状态保存,下一次扫描时作为检测变化的依据


 


                if ( KeyDown!=0 )      //如果有新按下的键


                   { for ( i="0" ;i<7; i++ )   //根据键值定义进行扫描


                            { if ( (KeyDown& KeyDefine) !=0 )   //该位置的键被按下


                                   {


                                       KeyQueue[KeyCount]=i+1;  //取得按键的键号,如果多键同时按下;


                                           KeyValue=i+1;                           //该键作为连击状态准备键,最后按下的优先键。


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


                                        KeyCount++;


                                        if ( KeyCount>=30)


                                           { KeyCount="0"; }


                                      }


                                   }


                            LongTimePress=0;          //有新的键按下,连击标志复位。


                            LongTimeCount=0;


                       }


                if (KeyUp !=0 )      //如果有键释放


                   { for ( i="0" ;i<7; i++ )   //根据键值定义进行扫描


                            { if ( (KeyUp& KeyDefine) !=0 ) //该位置的键被释放


                                   {


                                       KeyQueue[KeyCount]=(i+1)|0x80;      //取得按键的键号,将该键号与0x80相或;


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


                                        KeyCount++;


                                        if ( KeyCount>=30)


                                           { KeyCount="0"; }


                                          }


                                   }


                       }


                if ( (KeyUp& KeyDefine[KeyValue-1])!=0 ) //如果释放的键有连击准备键,取消连击状态


                   { KeyValue="0";


                        LongTimePress=0;


                        LongTimeCount=0;


                      }


              }


          else


           {


                KeyFSM=KeyKeepState;


              }


        break;


       default:


         break;


  }


 }   


}



 


 


文章评论1条评论)

登录后参与讨论

用户1203741 2012-7-14 21:36

正是我需要的
相关推荐阅读
nthq2004 2010-05-08 20:04
USB自定义设备驱动02
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />  本来还想编写应用程序测试一下自定...
nthq2004 2010-05-07 21:35
USB自定义设备驱动01
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />  一、USB设备驱动入门1、学习目...
nthq2004 2010-05-04 21:01
智林开发板上实现自定义的USB HID设备
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />  一、自定义HID设备的相关概念1...
nthq2004 2010-05-01 21:58
U盘例程在智林开发板上的移植
 <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 一、移植前的准备工作1、有哪些操...
nthq2004 2010-04-30 19:19
U盘实现流程跟踪分析02
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />   二、追踪USB大容量设备的实现...
nthq2004 2010-04-27 21:51
U盘实现流程跟踪分析01
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />   一、追踪USB大容量设备的实现...
我要评论
1
7
关闭 站长推荐上一条 /2 下一条