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" />2009年10月9日星期五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.
//产生的按键消息分别是1到7.
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键以下,就用U16,32键以下,就用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条评论)
登录后参与讨论