使用Proteus仿真实现51单片机控制的十字路口红绿灯
一.实验内容
1.需要仿真的元件:51单片机,一个动态显示的双数码管,六个LED灯。
2.实验要求:做一个十字路口红绿灯,每一向各一个,红绿灯要求能够按照通行的要求来亮灯。南北向红绿灯,红灯亮60秒,东西向的红绿灯亮,红灯亮30秒。
3.要求使用定时器中断来完成。
实验的内容跟我发的上一节是一样的,只不过这里用的不是真的51单片机和其他元器件了,而是仿真出来的。
二.步骤分析
1.首先我们得会初步使用preteus这个软件来实现我们电路的连接
2.了解动态数码管显示的原理
3.我们需要编写程序,让动态数码管显示倒计时,让led正确的亮灭
三.电路的仿真部分
我所绘制的电路图如下:
在这个电路图中,我们能够知道,我所设计的led都是共阴接法的,而且我所用到这个数码管也是共阴接法的(数码管可能看不出来是共阴的不过你知道就好了),而共阴接法和共阳接法让led和数码管显示是相反的。也就是说,共阴接法,你的引脚给1,led才会亮;而共阳接法,你的引脚给0,led会亮。所以你所写的程序是根据电路图来写的,如果电路图的接法不同,你的程序也得与之对应的发生改变。
我是把P2.0到P2.5的六给引脚分别给了六个红绿灯,分别代表是,南北路的绿红黄灯(上面的一组),和东西路的绿红黄灯(下面的一组)。这个灯的颜色是可以在它的元件库里挑的,可能当时嫌弃太麻烦就用了一个颜色的灯。
我把这个数码管的八个用来控制显示的引脚分别用p1.0和p1.7来控制,p1.7引脚控制的是数码管那个小数点的显示,因为用不到,所以我直接全部给的0。至于数码管显示的原理其实很简单,我就不过多阐述了,不知道可以去B站搜个视频看看。
至于数码管的片选的两个位,我给的是P0.0和P0.1两个引脚来控制,因为这个是两个数码管连在一起的,所以需要片选,什么意思呢,也就是说我们只能选择这两个数码管的其中一个亮,如果让这两个位同时给1,让他俩一起量,那么两个数码管就只能显示一样的数了,那么我们怎么实现让两个数码管一起亮,还能显示不同的数字呢。这就要用到数码管的动态显示了。
四.什么是数码管的动态显示 ,它是如何实现的呢?
我们都知道人眼识别是有一定范围的,如果我让两个数每一毫秒亮一次,并且让两个灯交替着亮,你看到的现象只会是两个灯一直在亮,而不会看到两个灯交替去亮。而动态数码管显示用的就是这个原理。
而用程序如何实现,如果我在程序里写让数码管一显示1,然后不加任何延时,接着写让数码管二显示2就可以实现吗?
其实不行,你还得加一个让数码管清除的语句,也就是说,让数码管一亮,然后让数码管一不亮,然后在让数码管二亮,让数码管二不亮,这样四个语句放在一起才能正常的显示。为什么非得加一个让它不亮的程序呢,在这里干说可能有些同学不理解,我建议你可以试试不加的,再试试加的,对比一下,你就明白了。
五.程序实现部分(切记,如果电路以及使用的电器件不一致,程序照搬照抄肯定是错的)
#include <REG52.H>
typedef unsigned int u16; //对系统默认数据类型进行重定义
typedef unsigned char u8;
typedef unsigned long u32;
//对南北东西两个方向的红绿灯的定义
sbit led1=P2^0;//南北路绿灯
sbit led2=P2^1;//南北路红灯
sbit led3=P2^2;//南北路黄灯
sbit led4=P2^3;//东西路绿灯
sbit led5=P2^4;//东西路红灯
sbit led6=P2^5;//东西路黄灯
void time0_init(void)//定时器0初始化
{
TMOD|=0X01;//选择为定时器0模式,工作方式1
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
ET0=1;//打开定时器0中断允许
EA=1;//打开总中断
TR0=1;//打开定时器
}
void shuma(int a,int b)//两个静态数码管的显示函数,其中a控制的是数码管片选,b控制显示什么数字
{
if(a==2){P0=0x02;}
if(a==1){P0=0x01;}
switch(b)//表示个位
{
case 0: P1=0x3f;break;//个位显示一个0
case 1: P1=0x06;break;
case 2: P1=0x5b;break;
case 3: P1=0x4f;break;
case 4: P1=0x66;break;
case 5: P1=0x6d;break;
case 6: P1=0x7d;break;
case 7: P1=0x07;break;
case 8: P1=0x7f;break;
case 9: P1=0x6f;break;
case 10: P1=0x00;break;
default: break;
}
}
int starttime=90,dtime=0;//定义两个用来计时的变量
int c,d;//定义两个变量分别用来储存十位数,和个位数
int sign;//定义一个标志位
void main()
{
time0_init();
led1=0;
led2=0;
led3=0;
led4=0;
led5=0;
led6=0;
while(1)
{
if(starttime==0)
{
sign=0;
starttime=90;//重置时间周期
}
//这些标志位的处理,主要是为了倒计时的正常显示
if(starttime==90) {sign=1;}
if(sign==1){dtime=starttime-30;}
if(dtime==0) {sign=2;}
if(sign==2) {dtime=starttime;}
if(starttime==90)//开始让南北的红灯亮,东西的绿灯亮
{
led2=1;//南北的红绿灯亮灭控制
led1=0;
led3=0;
led5=0;//东西的红绿黄灯亮灭控制
led4=1;
led6=0;
}
if(starttime==30)//让南北的绿灯亮,东西的红灯亮
{
led2=0;
led1=1;
led3=0;
led5=1;
led4=0;
led6=0;
}
if(starttime==33)//控制东北黄灯的亮,绿灯灭
{
led6=1;
led4=0;
led3=1;
led2=0;
}
if(starttime==3)//控制南北黄灯的灭,绿灯灭
{
led3=1;
led1=0;
led6=1;
led5=0;
}
c=dtime%10;//个位
d=dtime/10;//十位
//在数码管显示红绿灯倒计时
shuma(2,d); //让数码管2,显示十位数
shuma(2,10);//清除数码管二
shuma(1,c); //让数码管1,显示个位数
shuma(1,10);//清除数码管一
} //while
}//main
void time0() interrupt 1 //定时器0中断函数
{
static u16 i;//定义静态变量i
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
i++;
if(i==1000)//每过一秒钟让starttime减一
{
i=0;
starttime--;
}
}
上面的代码是完整的代码,上面的注释我写的十分详细 ,在这里就不过多阐述了,至于下一篇我打算出一篇使用51单片机控制步进电机的文章,希望大家多多支持,如果哪里有问题可以在评论区留言或者直接私信我即可。