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;
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 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 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 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();
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;
}
void stop(void)
{
P3 = 0xff;
}
//所有LED闪烁
void flash(void)
{
P3 = 0xff;
led_delay();
P3 = 0x00;
led_delay();
}
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 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; //
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;
}
}
}
{
key_scan(); //调用键值检测函数
switch(keyval) //根据键值实现相应功能
{
case 1:forward();
break;
case 2:backward();
break;
case 3:stop();
break;
case 4:flash();
break;
}
}
}
5、在proteus中新建仿真文件ex41.dsn,电路原理图如下所示:
6、将ex41.hex文件载入at89c51中,启动仿真,分别按下4个按键,观察程序运行结果。
7、可以看到,当第一次按下KEY1键时,8位流水灯开始正向轮流点亮,但是当按下KEY2或其它按键时,流水灯并没有按照我们的设计初衷相应改变,这是为什么呢?大家仔细想一想!
8、由此可见,使用这种方法,有时候程序运行效果并不如我们预料的那样,关于按键的更好的处理方法,我们将在后面的定时器的学习中进行进一步的阐述。在此先暂时不提。
下一节开始,我们就要对51单片机的内部功能进行深入学习了,争取通过更多的实例,加深我们的理解。