<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />2009年10月7日星期三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;
}
}
}
用户1203741 2012-7-14 21:36