这次,我们来学习51单片机的LED灯、按键、数码管的单独控制。
一、51单片机原理图
首先先看一下我们要学习的51单片机的原理图:
想要学好单片机,第一点就是要学会看原理图,学会看原理图,就知道那些器件是由哪些引脚来控制的。
二、LED的单独控制
我们先看一下LED灯是由哪些引脚控制的:
从图中可以看出,LED灯由P2引脚控制亮灭,其中P20~P27控制LED1~LED8。
我们可以看到,LED灯的正极全部连接着VCC,所以我们可以在LED灯的负极给一个0的信号,就可以点亮LED灯了。
1.点亮一个LED灯
点亮一个LED灯,可以有两种写法:
- 控制P2全部引脚
#include <REGX52.H>
void main()
{
P2=0xfe;//LED1点亮
while(1)
{
}
}
- 控制P2单个引脚
#include <REGX52.H>
void main()
{
P2=0xff;//关闭全部LED灯
P2_0=0;//LED1点亮
while(1)
{
}
}
这样,我们就可以用这两种方法来单独点亮一个LED灯了。当我们要让其他LED灯点亮的时候,只需要给其一个0的信号即可。
2.点亮全部LED灯
当我们需要点亮全部LED灯时,用到上面的第一种方法会简单很多,只需要P0=0x00就可以让LED灯全部点亮,不需要一个个的引脚去控制点亮。
#include <REGX52.H>
void main()
{
P2=0x00;//点亮全部LED灯
/*
P2=0xff;//关闭全部LED灯
P2_0=0;P2_1=0;P2_2=0;P2_3=0;P2_4=0;P2_5=0;P2_6=0;P2_7=0;//点亮全部LED灯
*/
while(1)
{
}
}
可以看到上面代码的注释,当我们使用上面第二种方法点亮LED灯时,就需要一个个引脚单独的去控制,这样就很麻烦。
3.LED流水灯
接下来,我们制作一个LED流水灯。
#include <REGX52.H>
#include <intrins.h>
void Delay1000ms() //1s延时
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 154;
k = 122;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void main()
{
while(1)
{
P0=0xfe;//点亮LED1
Delay1000ms();//延时1s
P0=0xfd;//熄灭LED1,点亮LED2
Delay1000ms();//延时1s,以此类推
P0=0xfb;
Delay1000ms();
P0=0xf7;
Delay1000ms();
P0=0xef;
Delay1000ms();
P0=0xdf;
Delay1000ms();
P0=0xbf;
Delay1000ms();
P0=0x7f;
Delay1000ms();
}
}
制作LED流水灯的程序设计不难,只要你学会怎么去控制点亮LED灯,就可以自己来设计LED流水灯了。
上面的代码还可以再简化,只要弄一个for循环就可以了。
#include <REGX52.H>
#include <intrins.h>
void Delay1000ms() //1s延时
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 154;
k = 122;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void main()
{
unsigned char i;
while(1)
{
for(i=0;i<8;i++)//循环点亮
{
P0=0xff&~(0x01<<i);
Delay1000ms();
}
}
}
三、按键的单独控制
51单片机的按键分有两种,一种是独立按键,另一种是矩阵按键。
1.按键的消抖
在学习按键之前,我们来说一下按键的消抖。
对于机械开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开,所以在开关闭合及断开的瞬间会伴随一连串的抖动。
所以在使用按键的时候,一定要进行消抖。
2.独立按键
首先先看一下独立按键的原理图:
从图中可以看到,独立按键的右端连接着GND,左端连接着P30~P33引脚,当按键按下的时候,P30~P33直接连接GND,所以我们只需要判断P30~P33是否为0,就可以知道按键是否按下了。
- 检查下降沿
当检测到按键按下时,立刻进行操作。
#include <REGX52.H>
void Delay15ms() //延时15ms,用于消抖
{
unsigned char i, j;
i = 30;
j = 43;
do
{
while (--j);
} while (--i);
}
void main()
{
while(1)
{
if(P3_1==0)//检测K1是否按下
{
Delay15ms();//消抖,等待按键稳定
if(P3_1==0)//再次检测K1是否按下
{
P0=0xfe;//产生下降沿,LED点亮
while(P3_1==0);//如果按键还是按下状态,不进行任何操作
Delay15ms();//消抖,等待按键稳定
}
}
if(P3_0==0)//检测K2是否按下
{
Delay15ms();//消抖,等待按键稳定
if(P3_0==0)//再次检测K2是否按下
{
P0=0xff;//产生下降沿,LED熄灭
while(P3_0==0);//如果按键还是按下状态,不进行任何操作
Delay15ms();//消抖,等待按键稳定
}
}
}
}
- 检测上升沿
当检测到按键按下时,不进行任何操作;当检测到按键松开时,立刻进行操作。
#include <REGX52.H>
void Delay15ms() //延时15ms,用于消抖
{
unsigned char i, j;
i = 30;
j = 43;
do
{
while (--j);
} while (--i);
}
void main()
{
while(1)
{
if(P3_1==0)//检测K1是否按下
{
Delay15ms();//消抖,等待按键稳定
while(P3_1==0);//如果按键还是按下状态,不进行任何操作
P0=0xfe;//产生上升沿,LED点亮
Delay15ms();//消抖,等待按键稳定
}
if(P3_0==0)//检测K2是否按下
{
Delay15ms();//消抖,等待按键稳定
while(P3_0==0);//如果按键还是按下状态,不进行任何操作;
P0=0xff;//产生上升沿,LED熄灭
Delay15ms();//消抖,等待按键稳定
}
}
}
3.矩阵按键
还是先来看矩阵按键的原理图:
从图中可以看到,矩阵按键的左右端都是连接着引脚的,我们不难发现,图中P10~P13的引脚是控制一整列的按键的,而P14~P17是控制一整行的。
所以,当我们想要控制矩阵按键时,需要先控制一整列或者一整行,再检测这一整列或者一整行是否有按键按下。
- 整行控制
#include <REGX52.H>
void Delay15ms() //延时15ms,用于消抖
{
unsigned char i, j;
i = 30;
j = 43;
do
{
while (--j);
} while (--i);
}
unsigned char Key()//按键处理函数
{
unsigned char Temp=0;//用于接收哪个按键按下
P1_7=0;P1_6=1;P1_5=1;P1_4=1;//控制P17一整行
if(P1_3==0){Delay15ms();while(P1_3==0);Delay15ms();Temp=1;}//检测S1
if(P1_2==0){Delay15ms();while(P1_2==0);Delay15ms();Temp=2;}//检测S2
if(P1_1==0){Delay15ms();while(P1_1==0);Delay15ms();Temp=3;}//检测S3
if(P1_0==0){Delay15ms();while(P1_0==0);Delay15ms();Temp=4;}//检测S4
P1_7=1;P1_6=0;P1_5=1;P1_4=1;//控制P16一整行
if(P1_3==0){Delay15ms();while(P1_3==0);Delay15ms();Temp=5;}//检测S5
if(P1_2==0){Delay15ms();while(P1_2==0);Delay15ms();Temp=6;}//检测S6
if(P1_1==0){Delay15ms();while(P1_1==0);Delay15ms();Temp=7;}//检测S7
if(P1_0==0){Delay15ms();while(P1_0==0);Delay15ms();Temp=8;}//检测S8
P1_7=1;P1_6=1;P1_5=0;P1_4=1;//控制P15一整行
if(P1_3==0){Delay15ms();while(P1_3==0);Delay15ms();Temp=9;}//检测S9
if(P1_2==0){Delay15ms();while(P1_2==0);Delay15ms();Temp=10;}//检测S10
if(P1_1==0){Delay15ms();while(P1_1==0);Delay15ms();Temp=11;}//检测S11
if(P1_0==0){Delay15ms();while(P1_0==0);Delay15ms();Temp=12;}//检测S12
P1_7=1;P1_6=1;P1_5=1;P1_4=0;//控制P14一整行
if(P1_3==0){Delay15ms();while(P1_3==0);Delay15ms();Temp=13;}//检测S13
if(P1_2==0){Delay15ms();while(P1_2==0);Delay15ms();Temp=14;}//检测S14
if(P1_1==0){Delay15ms();while(P1_1==0);Delay15ms();Temp=15;}//检测S15
if(P1_0==0){Delay15ms();while(P1_0==0);Delay15ms();Temp=16;}//检测S16
return Temp;//返回哪个按键按下
}
void main()
{
unsigned char Key_1=0;//用于接收按键上升沿
P2=0xff;//LED全关
while(1)
{
Key_1=Key();//接收按键上升沿
switch(Key_1)//有按键上升沿执行
{
case 1:
P2_0=0;
break;
case 2:
P2_1=0;
break;
case 3:
P2_2=0;
break;
case 4:
P2_3=0;
break;
case 5:
P2_4=0;
break;
case 6:
P2_5=0;
break;
case 7:
P2_6=0;
break;
case 8:
P2_7=0;
break;
case 9:
P2_0=1;
break;
case 10:
P2_1=1;
break;
case 11:
P2_2=1;
break;
case 12:
P2_3=1;
break;
case 13:
P2_4=1;
break;
case 14:
P2_5=1;
break;
case 15:
P2_6=1;
break;
case 16:
P2_7=1;
break;
}
}
}
- 整列控制
#include <REGX52.H>
void Delay15ms() //延时15ms,用于消抖
{
unsigned char i, j;
i = 30;
j = 43;
do
{
while (--j);
} while (--i);
}
unsigned char Key()//按键处理函数
{
unsigned char Temp=0;//用于接收哪个按键按下
P1_3=0;P1_2=1;P1_1=1;P1_0=1;//控制P13一整列
if(P1_7==0){Delay15ms();while(P1_7==0);Delay15ms();Temp=1;}//检测S1
if(P1_6==0){Delay15ms();while(P1_6==0);Delay15ms();Temp=5;}//检测S5
if(P1_5==0){Delay15ms();while(P1_5==0);Delay15ms();Temp=9;}//检测S9
if(P1_4==0){Delay15ms();while(P1_4==0);Delay15ms();Temp=13;}//检测S13
P1_3=1;P1_2=0;P1_1=1;P1_0=1;//控制P12一整列
if(P1_7==0){Delay15ms();while(P1_7==0);Delay15ms();Temp=2;}//检测S2
if(P1_6==0){Delay15ms();while(P1_6==0);Delay15ms();Temp=6;}//检测S6
if(P1_5==0){Delay15ms();while(P1_5==0);Delay15ms();Temp=10;}//检测S10
if(P1_4==0){Delay15ms();while(P1_4==0);Delay15ms();Temp=14;}//检测S14
P1_3=1;P1_2=1;P1_1=0;P1_0=1;//控制P11一整列
if(P1_7==0){Delay15ms();while(P1_7==0);Delay15ms();Temp=3;}//检测S3
if(P1_6==0){Delay15ms();while(P1_6==0);Delay15ms();Temp=7;}//检测S7
if(P1_5==0){Delay15ms();while(P1_5==0);Delay15ms();Temp=11;}//检测S11
if(P1_4==0){Delay15ms();while(P1_4==0);Delay15ms();Temp=15;}//检测S15
P1_3=1;P1_2=1;P1_1=1;P1_0=0;//控制P10一整列
if(P1_7==0){Delay15ms();while(P1_7==0);Delay15ms();Temp=4;}//检测S4
if(P1_6==0){Delay15ms();while(P1_6==0);Delay15ms();Temp=8;}//检测S8
if(P1_5==0){Delay15ms();while(P1_5==0);Delay15ms();Temp=12;}//检测S12
if(P1_4==0){Delay15ms();while(P1_4==0);Delay15ms();Temp=16;}//检测S16
return Temp;//返回哪个按键按下
}
void main()
{
unsigned char Key_1=0;//用于接收按键上升沿
P2=0xff;//LED全关
while(1)
{
Key_1=Key();//接收按键上升沿
switch(Key_1)//有按键上升沿执行
{
case 1:
P2_0=0;
break;
case 2:
P2_1=0;
break;
case 3:
P2_2=0;
break;
case 4:
P2_3=0;
break;
case 5:
P2_4=0;
break;
case 6:
P2_5=0;
break;
case 7:
P2_6=0;
break;
case 8:
P2_7=0;
break;
case 9:
P2_0=1;
break;
case 10:
P2_1=1;
break;
case 11:
P2_2=1;
break;
case 12:
P2_3=1;
break;
case 13:
P2_4=1;
break;
case 14:
P2_5=1;
break;
case 15:
P2_6=1;
break;
case 16:
P2_7=1;
break;
}
}
}
一般我们可以使用整行控制,这样来排按键的顺序就简单多了。
四、数码管的单独控制
首先还是来看一下数码管的原理图:
从图中可以看出,数码管的COM端连接着LED1~LED8,a~dp端连接着P00~P07。
然后,我们再看一下LED1~LED8又是由哪些引脚控制的。
这时候我们就要来学习一下这个74HC138译码器,74HC138译码器是由P22~P24引脚来控制LED1~LED8的,只要我们改变P22~P24引脚的信号,就可以改变LED1~LED8的值。
然后,我们再来看一下74HC138译码器是怎么控制的:
从上图可知,当P22~P24引脚的值改变,LED1~LED8其中一个会变成低电平,那么从中也可以得出,这个数码管的公共端是共阴极的。
所以,当我们要控制数码管时,首先是要进行位选,也就是选择第几位来显示,当我们进行位选时,只需要改变P22~P24的值,来控制哪个位是低电平,就可以进行位选了。然后,再进行段选,也就是控制a~dp端哪些要亮,只需要给高电平就可以亮了。
数码管的控制分为静态控制和动态控制,静态控制只能让单个数码管亮一个数字或者多个数码管亮同一个数字,而动态控制可以让多个数码管亮不同的数字,灵活性更高。
- 静态控制
#include <REGX52.H>
void Delay(unsigned int t) //延时tms
{
unsigned char i, j;
while(t--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
void main()
{
unsigned int Num[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//段选 0-9
unsigned int i;
P2=0xe3;//位选 选择第一个数码管
while(1)
{
for(i=0;i<=9;i++)//第一个数码管从0到9循环
{
P0=Num[i];//循环选择数字
Delay(1000);//延时1s
}
}
}
- 动态控制
#include <REGX52.H>
void Delay(unsigned int t) //延时tms
{
unsigned char i, j;
while(t--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
unsigned int Num[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//段选 0-9
void Seg(unsigned int a,b)//数码管处理函数 a-位选 b-段选
{
switch(a)
{
case 1://选择第一位数码管
P2=0xff;
break;
case 2://选择第二位数码管
P2=0xfb;
break;
case 3://选择第三位数码管
P2=0xf7;
break;
case 4://选择第四位数码管
P2=0xf3;
break;
case 5://选择第五位数码管
P2=0xef;
break;
case 6://选择第六位数码管
P2=0xeb;
break;
case 7://选择第七位数码管
P2=0xe7;
break;
case 8://选择第八位数码管
P2=0xe3;
break;
}
P0=Num[b];//显示 0-9
Delay(1);//消影
P0=0x00;//关闭段选
}
void main()
{
while(1)
{
Seg(1,1);//第一位显示1
Seg(2,2);//第二位显示2
Seg(3,3);//第三位显示3
Seg(4,4);//第四位显示4
Seg(5,5);//第五位显示5
Seg(6,6);//第六位显示6
Seg(7,7);//第七位显示7
Seg(8,8);//第八位显示8
}
}