51单片机——交通灯

原理图

在这里插入图片描述

功能描述

  1、基本功能就是如同红绿灯一般,不做赘述。
  2、红灯时长和绿灯时长可通过按键设置,即按键列中的上面4个,当这4个按键有一个按下后便进入时长设置功能,设置完成后按最下面两个按键(紧急控制按钮)任意一个便可退出该功能。
  3、有紧急控制功能,按下紧急控制按钮后,便进入该功能,保持红灯或绿灯常亮,且关闭数码管,当按下时长控制按钮即最上面的4个按钮便可退出该功能。

效果展示

在这里插入图片描述

代码

#include <reg52.h>

//数码管选择位
sbit EW_1=P1^0;		   
sbit EW_2=P1^1;
sbit NS_1=P1^2;		   
sbit NS_2=P1^3;

sbit add_red_time=P1^4;		   		//加红灯时间按钮
sbit add_green_time=P1^5;	   		//加绿灯时间按钮
sbit reduce_red_time=P1^6;		   	//减红灯时间按钮
sbit reduce_green_time=P1^7;	   	//减绿灯时间按钮
sbit NS_led=P2^6;	   				//南北向灯紧急控制按钮
sbit EW_led=P2^7;	   				//东西向灯紧急控制按钮

sbit EW_red=P2^0;	   				//东西向红灯
sbit EW_green=P2^1;	   				//东西向绿灯
sbit EW_yellow=P2^2;	   		    //东西向黄灯
sbit NS_red=P2^3;	   				//南北向红灯
sbit NS_green=P2^4;	   				//南北向绿灯
sbit NS_yellow=P2^5;	   			//南北向黄灯


char count=0;						//计数,count=20表示1s
char red_time=30;					//红灯停留时间
char green_time=25;					//绿灯停留时间
char yellow_time=0;					//黄灯停留时间
char NS_second=0;					//南北红绿灯秒计时
char EW_second=0;		    		//东西红绿灯秒计时
char code smgduan[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};		//共阳数码管段码表,仿真中用的是共阴数码管,所以使用时需对段码取反
char display_data[4]={0};			//show_data[0]显示南北计时十位,show_data[1]显示南北计时个位,show_data[2]显示东西计时十位,show_data[3]显示东西计时个位
char temp_data[4]={0};

char NS_R_G_mode=0;					//南北红绿灯亮模式  0  红灯  1  绿灯  2  黄灯
char EW_R_G_mode=1;					//东西红绿灯亮模式  0  红灯  1  绿灯  2  黄灯

bit NS_R_G_flag=0;					//南北红绿灯标识位  0  红灯  1  绿灯 
bit EW_R_G_flag=0;					//东西红绿灯标识位  0  红灯  1  绿灯 

void delay(unsigned int i)			//简单延时
{
	while(i--);
}

void Timer_init()					//定时器初始化
{
	//定时50ms
    TMOD = 0x01;					//定时器方式1
    TH0 = 0x3C;						//定时器赋初值
    TL0 = 0xB0;
    EA = 1;							//开启总中断
    ET0 = 1;						//开启定时器中断
    TR0 = 1;						//开启定时器
}

void Init()							//系统初始化
{
	P0=0x00;
	P2=0x00;
	EW_1=1;		   
	EW_2=1;
	NS_1=1;
	NS_2=1;
	NS_led=1;
	EW_led=1;
	NS_second=red_time;				//默认初始时南北向灯亮红灯,并赋红灯时长
	EW_second=green_time;			//默认初始时东西向灯亮绿灯,并赋绿灯时长
	yellow_time=red_time-green_time;//黄灯时间为红灯时间与绿灯时间差
	Timer_init();
}

void NS_SMG_drive(char *buff)		//南北向数码管驱动
{
	//显示十位
	NS_1=0;
	NS_2=1;
	P0=~smgduan[buff[0]];           //段码取反
	delay(5); 						//间隔一段时间扫描	
	P0=0x00;						//消隐

	//显示个位
	NS_1=1;
	NS_2=0;
	P0=~smgduan[buff[1]];
	delay(5); 
	P0=0x00;

	//关闭南北向数码管
	NS_1=1;
	NS_2=1;
}

void EW_SMG_drive(char *buff)		//东西向数码管驱动
{
	//显示十位
	EW_1=0;
	EW_2=1;
	P0=~smgduan[buff[2]];           //段码取反
	delay(5); 						//间隔一段时间扫描	
	P0=0x00;						//消隐

	//显示个位
	EW_1=1;
	EW_2=0;
	P0=~smgduan[buff[3]];
	delay(5); 	
	P0=0x00;

	//关闭东西向数码管
	EW_1=1;		   
	EW_2=1;
}

void data_del(char *buff,char data1,char data2)	//数据处理
{
	buff[0]=data1/10;							//取data1的十位
	buff[1]=data1%10;							//取data1的个位
	buff[2]=data2/10;							//取data2的十位
	buff[3]=data2%10;							//取data2的个位
}

void Time_del()									//计时处理
{
	if(count>=20)								//判断是否满1s
	{
		NS_second--;							//南北向灯计时自减
		EW_second--;							//东北向灯计时自减
		switch(NS_R_G_mode)						//南北向灯
		{
			case 0:								//红灯
			{
				if(NS_second<0)
				{
					NS_second=green_time;		//开始绿灯倒计时
					NS_R_G_mode=1;   			//红灯亮完绿灯亮
				}				
			}break;
			case 1:								//绿灯
			{
				if(NS_second<0)
				{
					NS_second=yellow_time;		//开始黄灯倒计时
					NS_R_G_mode=2;   			//绿灯亮完黄灯亮					
				}		
			}break;
			case 2:								//黄灯
			{
				if(NS_second<0)
				{
					NS_second=red_time;			//开始红灯到计时
					NS_R_G_mode=0;   			//黄灯亮完红灯亮					
				}		
			}break;
			default:break;
		}
		switch(EW_R_G_mode)						//东西向灯
		{
			case 0:								//红灯
			{
				if(EW_second<0)
				{
					EW_second=green_time;		//开始绿灯倒计时
					EW_R_G_mode=1;   			//红灯亮完绿灯亮
				}				
			}break;
			case 1:								//绿灯
			{
				if(EW_second<0)
				{
					EW_second=yellow_time;		//开始黄灯倒计时
					EW_R_G_mode=2;   			//绿灯亮完黄灯亮					
				}		
			}break;
			case 2:								//黄灯
			{
				if(EW_second<0)
				{
					EW_second=red_time;			//开始红灯倒计时
					EW_R_G_mode=0;   			//黄灯亮完红灯亮					
				}		
			}break;
			default:break;
		}
		count=0;								//计数值清零
	}
}

void R_G_Y_led()								//红绿灯驱动
{
	switch(NS_R_G_mode)							//南北向
	{
		case 0:									//红灯
		{
			NS_yellow=0;						//黄灯灭
			NS_red=1;							//红灯亮
		}break;
		case 1:									//绿灯
		{
			NS_red=0;							//红灯灭
			NS_green=1;							//绿灯亮
		}break;
		case 2:									//黄灯
		{
			NS_green=0;							//绿灯灭
			if(count<10)						//黄灯以1hz频率闪烁
				NS_yellow=1;
			else
				NS_yellow=0;			
		}break;
		default:break;		
	}
	switch(EW_R_G_mode)							//东西向
	{
		case 0:									//红灯
		{
			EW_yellow=0;						//黄灯灭
			EW_red=1;							//红灯亮
		}break;
		case 1:									//绿灯
		{
			EW_red=0;							//红灯灭
			EW_green=1;							//绿灯亮
		}break;
		case 2:									//黄灯
		{
			EW_green=0;							//绿灯灭
			if(count<10)						//黄灯以1hz频率闪烁
				EW_yellow=1;
			else
				EW_yellow=0;			
		}break;
		default:break;		
	}
}

void Set_time()									//设置红绿灯亮的时长
{
	if((add_red_time==0)||(add_green_time==0)||(reduce_red_time==0)||(reduce_green_time==0))//设置红绿灯时长时任一设置按钮都可触发
	{
		TR0 = 0;								//关闭定时器
		P2=0x00;								//清零P2寄存器
		EW_led=1;								//EW_led、NS_led引脚也在P2寄存器内,但是后面需要这两个按钮结束设置红绿灯时长任务,故而这两个引脚要拉高
		NS_led=1;
		while(1)
		{
			data_del(temp_data,red_time,green_time);//显示当前红绿灯时长
			NS_SMG_drive(temp_data);
			EW_SMG_drive(temp_data);
			if(add_red_time==0)					//判断加红灯时间按钮是否按下
			{
				delay(5);						//消抖
				if(add_red_time==0)
				{
					red_time++;					//红灯时间自加
					if(red_time>99)				//限制红灯时间最大值为99
						red_time=99;
					data_del(temp_data,red_time,red_time);//南北向数码管显示红灯时长
					NS_SMG_drive(temp_data);
				}while(!add_red_time);			//等待加红灯时间按钮弹起
			}
			if(add_green_time==0)				//判断加绿灯时间按钮是否按下
			{
				delay(5);
				if(add_green_time==0)
				{
					green_time++;				//绿灯时间自加
					if(green_time>95)			//限制绿灯时间最大值95
						green_time=95;
					data_del(temp_data,green_time,green_time);//东西向数码管显示绿灯时长
					EW_SMG_drive(temp_data);
				}while(!add_green_time);		//等待加绿灯时间按钮弹起
			}
			if(reduce_red_time==0)				//判断减红灯时间按钮是否按下
			{
				delay(5);
				if(reduce_red_time==0)
				{
					red_time--;					//红灯时间自减
					if(red_time<10)				//限制红灯时间最小值10
						red_time=10;
					data_del(temp_data,red_time,red_time);
					NS_SMG_drive(temp_data);
				}while(!reduce_red_time);		//等待减红灯时间按钮弹起
			}
			if(reduce_green_time==0)			//判断减绿灯时间按钮是否按下
			{
				delay(5);
				if(reduce_green_time==0)
				{
					green_time--;				//绿灯时间自减
					if(green_time<5)			//限制绿灯时间最小值5
						green_time=5;
					data_del(temp_data,green_time,green_time);
					EW_SMG_drive(temp_data);
				}while(!reduce_green_time);		//等待减绿灯时间按钮弹起
			}
			if((NS_led==0)||(EW_led==0))		//任一紧急控制按钮按下则结束设置红路灯时长任务
			{
				break;
			}
		}while((!NS_led)||(!EW_led));			//等待紧急控制按钮弹起
		TR0 = 1;								//开启定时器
		yellow_time=red_time-green_time;		//更新黄灯时间
	}
}

void Urgent()									//红绿灯紧急控制
{
	if((NS_led==0)||(EW_led==0))				//任一紧急控制按钮按下触发
	{
		TR0 = 0;								//关闭定时器
		P2=0x00;
		EW_led=1;
		NS_led=1;

		EW_1=1;									//关闭所有数码管
		EW_2=1;
		NS_1=1;
		NS_2=1;
		while((!NS_led)||(!EW_led));			//判断紧急控制按钮按是否弹起
		while(1)
		{
			if(NS_R_G_flag)						//根据NS_R_G_flag状态交替亮红灯或者绿灯
			{
				NS_green=1;	
				NS_red=0;					
			}	
			else
			{
				NS_red=1;	
				NS_green=0;				
			}
			if(EW_R_G_flag)						//根据EW_R_G_flag状态交替亮红灯或者绿灯
			{
				EW_green=1;	
				EW_red=0;									
			}
			else
			{
				EW_red=1;
				EW_green=0;						
			}	
			if(NS_led==0)						//判断南北向紧急控制按钮是否按下
			{
				delay(5);
				if(NS_led==0)
				{
					NS_R_G_flag=!NS_R_G_flag;	//NS_R_G_flag状态取反				
				}while(!NS_led);				//等待南北向紧急控制按钮弹起
			}
			if(EW_led==0)						//判断东西向紧急控制按钮是否按下
			{
				delay(5);
				if(EW_led==0)
				{
					EW_R_G_flag=!EW_R_G_flag;	//EW_R_G_flag状态取反						
				}while(!EW_led);				//等待东西向紧急控制按钮弹起
			}	
			if((add_red_time==0)||(add_green_time==0)||(reduce_red_time==0)||(reduce_green_time==0))//任一红绿灯设置时长按钮按下结束紧急控制人物
			{
				TR0 = 1;						//开启定时器
				break;
			}
		}while((!add_red_time)||(!add_green_time)||(!reduce_red_time)||(!reduce_green_time));//等待红绿灯时间设置按钮弹起	
		P2=0x00;
		EW_led=1;
		NS_led=1;
	}
}

void main()
{
	Init();
	while(1)
	{
		Time_del();								//时间处理
		data_del(display_data,NS_second,EW_second);//数据处理
		NS_SMG_drive(display_data);				//南北向数码管驱动
		EW_SMG_drive(display_data);				//东西向数码管驱动
		R_G_Y_led();							//红绿灯驱动
		Set_time();								//红绿灯时长设置
		Urgent();								//紧急控制
	}
}

void Timer0(void) interrupt 1					//定时器中断
{
    TH0 = 0x3C;
    TL0 = 0xB0;
	count++;									//触发中断后计数值自加,定时器中断每50ms触发一次
}


工程下载

链接:https://pan.baidu.com/s/1P0Gj6PmNfffJdsEe5j6JGA
提取码:0yzj

由于后续发现了些小问题,程序有所修改,网盘中的代码由于一些原因暂时不能更新,以本篇博客为准。

  • 65
    点赞
  • 537
    收藏
    觉得还不错? 一键收藏
  • 48
    评论
评论 48
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值