最近老师让我们使用51单片机做一个十字路口红绿灯实验,正好发表一篇文章,希望能给有需要的同学们带来一些帮助,如果有不合理的地方,还请大家帮忙指正。
一. 首先要利用51做一个红绿灯我们需要知道这个红绿灯有哪些要求,下面是题目要求:
1.实验硬件:红绿黄三种颜色的LED灯每种两个,杜邦线若干,面包板一个,51单片机开发板一个。
2.实验要求:做一个十字路口红绿灯,每一向各一个,红绿灯要求能够按照通行的要求来亮灯。南北向红绿灯,红灯亮60秒,东西向的红绿灯亮,红灯亮30秒。
3.要求用LED灯和定时器中断来完成。
二.下面说一下我所用的单片机以及其图片:
我所用的是STC89C52系列的51单片机,这个是学习版的,在淘宝和京东上都能搜到,大部分初学者都是学的这个型号的51单片机。
我本来是想直接用这个板子上的数码管和它右边mo的LED模块来做这个实验,但是数码管控制的引脚和这个LED模块是冲突的,两个共用一个引脚,所以不能同时使用。
数码管需要用到P0和P2的引脚,而这个LED模块就是用的P2的8个引脚控制。
三.程序部分
1.头文件以及子程序的说明
/
void time0_init(void)
{
TMOD|=0x01;//选择为定时器0模式,工作方式1
TH0=0xFC; //给定时器赋初值,定时1ms
TL0=0x18;
ET0=1;//打开定时器0中断允许
EA=1;//打开总中断
TR0=1;//打开定时器
}
extern int ss=90;
void time0() interrupt 1 //定时器0中断函数
{
static u16 i;//定义静态变量i
TH0=0xFC; //给定时器赋初值,定时1ms
TL0=0x18;
i++;
if(i==1000)
{
i=0;
ss--;
}
}
这个定时器中断配置的是每一毫秒溢出一次,也就是说每一毫秒进入一次中断,而中断服务函数里面的SS--就会每一秒钟进行一次,也就是说SS每一秒钟会减一,这样也是为了符合红绿灯的读数显示,我们都知道红绿灯是倒计时显示的。
2.主程序的代码以及相关说明
//对红绿灯的引脚定义
sbit NBred=P1^0;//南北方向红绿黄灯引脚定义
sbit NBgreen=P1^1;
sbit NByellow=P1^2;
sbit DXred=P1^5;//东西方向红绿黄灯引脚定义
sbit DXgreen=P1^6;
sbit DXyellow=P1^7;
//**********************************************
//*************************************************数码管段码表
unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
void Nixie(unsigned char Location,Number)//数码管显示子函数
{
switch(Location) //位码输出
{
case 1:P2_4=1;P2_3=1;P2_2=1;break;
case 2:P2_4=1;P2_3=1;P2_2=0;break;
case 3:P2_4=1;P2_3=0;P2_2=1;break;
case 4:P2_4=1;P2_3=0;P2_2=0;break;
case 5:P2_4=0;P2_3=1;P2_2=1;break;
case 6:P2_4=0;P2_3=1;P2_2=0;break;
case 7:P2_4=0;P2_3=0;P2_2=1;break;
case 8:P2_4=0;P2_3=0;P2_2=0;break;
}
P0=NixieTable[Number]; //段码输出
Delay(1); //显示一段时间
P0=0x00; //段码清0,消影
}
//**************************************
//**************************主函数变量定义
extern int ss;
int a=0,b=0,sign=0,cc=0;
//**************************
//********************主函数部分,使用定时器0中断计时,中断函数在interrupt文件中
void main()
{
time0_init();
while(1)
{
if(ss==0)
{
sign=0;
ss=90;//如果等于ss=0,让ss=90,重置ss的时间周期
}
//这些标志位的处理,主要是为了倒计时的正常显示
if(ss==90) {sign=1;}
if(sign==1){cc=ss-30;}
if(cc==0) {sign=2;}
if(sign==2) {cc=ss;}
if(ss==90)//开始让南北的红灯亮,东西的绿灯亮
{
NBred=0;//南北的红绿灯亮灭控制
NBgreen=1;
NByellow=1;
DXred=1;//东西的红绿黄灯亮灭控制
DXgreen=0;
}
if(ss==30)//让南北的绿灯亮,东西的红灯亮
{
NBred=1;
NBgreen=0;
DXred=0;
DXgreen=1;
DXyellow=1;
}
if(ss==33)//控制东北黄灯的亮,绿灯灭
{
DXyellow=0;
DXgreen=1;
}
if(ss<=3&ss>=0)//控制南北黄灯的灭,绿灯灭
{
NByellow=0;
NBgreen=1;
}
a=cc%10;//个位
b=cc/10;//十位
Nixie(1,b); //在数码管的第1位置显示十位b
Nixie(2,a); //在数码管的第2位置显示个位a
}
}
主程序大体的思路:
这里我是直接把SS的初值给90,因为南北和东西两向的灯的红绿灯时间不一样,一个是红灯60秒,一个是红灯30秒,所以说两个红绿灯的总周期是90秒,把SS设为90,来做总周期的计时。但是红绿灯的倒计时不能显示总周期,而是分成两段的。所以我设了一个CC来进行外部实际的倒计时显示。可能有同学不理解为什么要这样设,我在这里简单说明一下,拿南北方向红绿灯来说,我设计的是南北方向先进入红灯,那么南北方向红绿灯就会有一个60秒的红灯倒计时。倒计时结束后,南北方向是红绿灯就会进入30秒的绿灯,而东西方向的红绿灯正好相反。至于黄灯单独拿出来处理即可。
而数码管显示的时候要注意把倒计时数字,拆成个位和十位然后进行动态显示即可。
这里还要说的一点是,如果是用仿真的话,根据电路的不同,程序也是不一样的,但是设计的思路是类似的,大家要灵活变通



