地址:https://www.eefocus.com/luo_xinli/blog/15-03/310848_30574.html 新到公司同事写了一个键盘处理程序,我们可以浏览一下这段代码 /*********************************/
//采集当前键盘端口数据,并且返回键盘端口的值
/*********************************/
unsigned char scanKey( )
{
unsigned char value1,value2;
unsigned char m,n;
P2 =0xff;
value1 = P2;
if(value1!=0xff)
{
for(m=0;m<100;m++)
for(n=0;n<100;n++);
value2 = P2;
if(value1==value2)
{
while(P2!=0xff)
{
}
return value1;
}
}
return 0xff;
}
这段代码运行是没有问题的,通过
for(m=0;m<100;m++)
for(n=0;n<100;n++);
value2 = P2;
if(value1==value2)
{
while(P2!=0xff)
{
}
return value1;
实现了键盘去抖。关于键盘一般书上也是这么介绍的。但是这段代码确实有不足之处。首先 for(m=0;m<100;m++)
for(n=0;n<100;n++); 是延时代码,程序一直会停留在这里,浪费了系统的效率,是系统响应其他任务的变得迟钝。而且容易造成看门狗复位。在EMC实验中容易造成系统不稳定。如果有长按下键盘不太容易实现。 我找到另外一位工程师的一段键盘代码,如下: sbit SET_KEY_PIN = P2^1; // 设定键盘 S1
sbit WATER_KEY_PIN = P1^7; // 排水键盘 S3
sbit UP_KEY_PIN = P2^2; // 加键 S2
sbit DOWN_KEY_PIN = P2^5; // 减键 S4
sbit STOP_KEY_PIN = P2^4; // 停机键 S5
sbit START_KEY_PIN = P2^0; // 启动键 S6
sbit SET_CF_PIN = P3^0; //设定也许键盘
static unsigned int KEY_VALUE =0;
unsigned char readKey( )
{
unsigned char keyValue = 0x00;
SET_KEY_PIN =1;
WATER_KEY_PIN =1;
UP_KEY_PIN =1;
DOWN_KEY_PIN =1;
STOP_KEY_PIN =1;
START_KEY_PIN =1;
if( !SET_KEY_PIN ) keyValue = SET_KEY_BIT;
if( !WATER_KEY_PIN ) keyValue |= WATER_KEY_BIT;
if( !UP_KEY_PIN ) keyValue |= UP_KEY_BIT;
if( !DOWN_KEY_PIN ) keyValue |= DOWN_KEY_BIT;
if( !STOP_KEY_PIN ) keyValue |= STOP_KEY_BIT;
if( !START_KEY_PIN ) keyValue |= RUN_KEY_BIT;
return keyValue;
}
void keyScan( )
{
static unsigned char stateMachine = 0;
static unsigned char keyValue1 = 0;
static unsigned char keyValue2 = 0;
switch( stateMachine )
{
case 0:
keyValue1=readKey( );
if(!keyValue1) return;
KEY_DELAY_DEC_TIMER = KEY_DELAY_DEC_TIMER_MAX;
stateMachine = 1;
break;
case 1:
if( KEY_DELAY_DEC_TIMER )
{
keyValue2=readKey( );
if( keyValue1 != keyValue2) stateMachine = 0;
}
else
{
if(( keyValue2== STOP_KEY_BIT )
||( keyValue2== SET_KEY_BIT ))
{
stateMachine = 2;
KEY_DELAY_INC_TIMER =0;
}
else
{
KEY_VALUE = keyValue2;
stateMachine = 3;
}
}
break;
case 2: //等待延时3秒以上
keyValue2 = readKey( );
if( keyValue2 )
{
if( KEY_DELAY_INC_TIMER > KEY_DELAY_INC_TIMER_MAX)
{
if(keyValue2== STOP_KEY_BIT )
{
//清除错误
KEY_VALUE = CLEAR_ER_KEY_CMD;
}
else
{
if( keyValue2== SET_KEY_BIT )
{
//设定键--进入用户设定菜单
KEY_VALUE = ENTER_USER_MENU_CMD;
}
else
{
KEY_VALUE = 0;
}
}
stateMachine = 3;
}
}
else
{
KEY_VALUE = keyValue1;
stateMachine = 3;
}
break;
case 3: //等待键盘释放
keyValue2 = readKey( );
if( !keyValue2 )
{
stateMachine = 0;
}
break;
default:
break;
}
}
unsigned int getKey( )
{
unsigned int keyVlue ;
keyVlue = KEY_VALUE;
KEY_VALUE = 0x00;
return keyVlue;
}
readKey( )是读取键盘IO脚电压。键盘弹起时IO脚电平为低,键盘按下时IO脚电平位高。读取并且返回。 keyScan( )是键盘扫描程序,调用 readKey( )更新本地全局变量KEY_VALUE 。KEY_DELAY_DEC_TIMER 是键盘扫描定时器,在定时器中递减。代码如下:
INTERRUPT (TIMER4_ISR, INTERRUPT_TIMER4)
{
if( KEY_DELAY_DEC_TIMER > 0 ) KEY_DELAY_DEC_TIMER--;
TMR4CN &= ~0xC0; // Clear Timer 4 overflow/underflow flag
}
在KEY_DELAY_DEC_TIMER_MAX定时器中断次数时间范围完成一次键盘扫描。采用了类似状态机的处理方法。主程序每执行一次keyScan( ),读取一次IO脚,执行效率比较高,键盘处理任务需要的时间比较短。在KEY_DELAY_DEC_TIMER时间范围内更新一次KEY_VALUE 。 getKey( )是提供给应用程序,获取当前键盘的稳定状态的值。并且将KEY_VALUE 清零。程序架构比较好,分层也很清楚!
我们看看在主程序调用:
while(1)
{
Watchdog_operation(FEED);
。。。。
keyScan( );
key = getKey( );
exeKeyCmd( key );
。。。。。
}
我推荐这位工程师的键盘处理方法,显得比较成熟、老练。扩展性好,代码复用率也很好!
|