基于proteus的51单片机仿真实例五十三、4个按键的组成的按键扫描实例

1、本例实现用4个按键控制8位流水灯的不同显示效果。
按下KEY1键时,接在P3口的8位流水灯从P0.0开始依次点亮;
按下KEY2键时,接在P3口的8位流水灯从P0.7开始依次点亮;
按下KEY3键时,接在P3口的8位流水灯全部点亮;
按下KEY4键时,接在P3口的8位流水灯全部熄灭;
2、本例中使用4个按键,分别对应4种功能,因此,给4个按键分别命名并设置键值,如下:
KEY1键:键值为1;
KEY2键:键值为2;
KEY3键:键值为3;
KEY4键:键值为4;
3、4个按键分别接在P1口的P0.0--P0.3这4个引脚上,按键判别过程如下:
1)首先判断是否有按键被按下。将P1口的低四位置为高电平,然后读取这4位的电平值(想一想,为什么要先置为高电平,然后再读取电平值),只要有一位不是高电平,则说明有按键被按下。读取方法如下:
P1=0x0f;
if((P1&&0x0f)!=0x0f);
//低四位与“1”相与,如果结果不是1,则说明低四位中必然有一位是‘0’,说明有按键被按下。
2)按键消抖。但判别到有按键被按下后,调用延时函数,延时一段时间后 ,再进行判断,若果仍然检测到有按键被按下,则进行键值判断,否则认为是干扰信号,不再进行按键识别处理;
3)键值判断:当确认有按键被按下时,可采用逐位扫描的方法判断那个按键被按下。
 
4、根据上述分析,编写程序代码。在keil c51中新建工程ex41,编写如下程序代码,编译并生成ex41.hex文件。
#include <reg51.h>    //包含头文件
sbit KEY1=P1^0;     //用位定义指令分别定义4个按键所对应的引脚
sbit KEY2=P1^1;
sbit KEY3=P1^2;
sbit KEY4=P1^3;
unsigned char keyval;  // 全局变量定义,定义键值变量
//LED流水灯延时函数
void led_delay(void)
{
  unsigned char i,j;
 for(i = 0;i < 251;i++)
 {
   for(j = 0;j < 251;j++);
 }
}
//按键消抖延时函数
void delay20ms(void)
{
  unsigned char i,j;
 for(i = 0;i < 100;i++)
 {
   for(j = 0;j < 60;j++);
 }
}
//正向流水灯
void forward(void)
{
  P3 = 0xfe;     //点亮P3.0引脚的LED灯
 led_delay();
 P3 = 0xfd;
 led_delay();
 P3 = 0xfb;
 led_delay();
 P3 = 0xf7;
 led_delay();
 P3 = 0xef;
 led_delay();
 P3 = 0xdf;
 led_delay();
 P3 = 0xbf;
 led_delay();
 P3 = 0x7f;
 led_delay();
 P3 = 0xfe;
 led_delay();
}
//反向流水灯
void backward(void)
{
  P3 = 0x7f;    //点亮P3.7引脚的LED灯
 led_delay();
 P3 = 0xbf;
 led_delay();
 P3 = 0xdf;
 led_delay();
 P3 = 0xef;
 led_delay();
 P3 = 0xf7;
 led_delay();
 P3 = 0xfb;
 led_delay();
 P3 = 0xfd;
 led_delay();
 P3 = 0xfe;
 led_delay();
 P3 = 0x7f;
 led_delay();
}
//关闭所有LED灯
void stop(void)
{
  P3 = 0xff; 
}
//所有LED闪烁
void flash(void)
{
  P3 = 0xff;
 led_delay();
 P3 = 0x00;
 led_delay(); 
}
//键盘扫描函数
void key_scan(void)
{
  if((P1 & 0x0f)!=0x0f)    //检测是否有按键被按下
 {
   delay20ms();
  if((P1 & 0x0f)!=0x0f)    //延时后再次检测是否有按键被按下
  {
   if(KEY1 == 0)    //判断是那个按键被按下
   {
     keyval = 1;
   }
   if(KEY2 == 0)
   {
     keyval = 2;
   }
   if(KEY3 == 0)
   {
     keyval = 3;
   }
   if(KEY4 == 0)
   {
     keyval = 4;
   }
  }
 }
}
//主函数
void main(void)
{
 keyval = 0;      //
 while(1)
 {
   key_scan();    //调用键值检测函数
  switch(keyval)     //根据键值实现相应功能
  {
    case 1:forward();
          break;
   case 2:backward();
          break;
   case 3:stop();
          break;
   case 4:flash();
          break;
  }
 }
 
5、在proteus中新建仿真文件ex41.dsn,电路原理图如下所示:
基于proteus的51单片机仿真实例五十三、4个按键的组成的按键扫描实例
 
6、将ex41.hex文件载入at89c51中,启动仿真,分别按下4个按键,观察程序运行结果。
7、可以看到,当第一次按下KEY1键时,8位流水灯开始正向轮流点亮,但是当按下KEY2或其它按键时,流水灯并没有按照我们的设计初衷相应改变,这是为什么呢?大家仔细想一想!
 
8、由此可见,使用这种方法,有时候程序运行效果并不如我们预料的那样,关于按键的更好的处理方法,我们将在后面的定时器的学习中进行进一步的阐述。在此先暂时不提。
 
下一节开始,我们就要对51单片机的内部功能进行深入学习了,争取通过更多的实例,加深我们的理解。