基于51单片机的交通灯(含源码与proteus仿真,具备按键切换夜间模式、禁行模式、加减时间操作,平滑切换无bug)

    在我们学习51单片机过程中,模拟交通灯都是一个经典的设计任务。交通灯模拟涉及到的硬件都是比较简单的,LED灯、主控芯片、两位数码管,这些东西依靠普通51单片机就可以完成驱动,部分数码管需要电流大的话可以适当加一些驱动芯片以达到更好的显示效果,如74HC245芯片。这个设计的难度往往在于南北、东西两路的配合以及按键改变工作模式如何完善切换而不冲突。

        下面我将就本次设计的硬件与编程思想进行介绍。

文末附Proteus仿真与keil源工程以及测试视频,欢迎大家下载测试。

注:本次仿真基于proteus8.15,低于本版本将无法打开仿真。

一、硬件

(1)双位七段数码管

        本次设计采用共阴七段数码管,A-H分别对应其中七段显示和小数点,因交通灯设计不涉及小数点,H口未接,其余接至主控芯片51单片机P0口。所谓共阴数码管就是共地,所以A-H接口给1就亮了。共阴段码如下:

u8 code SMG_NODOT[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//无小数点

另外,二位数码管有两个控制位,1拉低即第一位有效,2拉低即第二位有效。

(2)LED

        我们都知道51单片机上电端口默认高电平,因此我们可以将LED灯进行共阳接法。

                33e3d3ff108d4212bacb60e99b350fc4.png

(3)按键

        普通按键,共阴接法。

                                        24d3802ed72a4233b5be09d3f4673914.png

图比较小,建议下载proteus工程看。

总体设计图如下:

529d44972195496da369ad8fba012db6.png

 二、软件

1.设计思路

        这个是整个设计最有意思的地方,我们想下日常生活中见到的交通灯,一侧红灯,一侧就为绿灯,绿灯切红灯中间有短暂黄灯。我第一想法是设置三个变量,红灯时间、绿灯时间、黄灯时间,按照顺序进行控制就OK,可惜想法美好但现实残酷,这实现普通的可以,但实现调节时间时出bug了,增加红灯时间后,有一部分时间南北、东西都是红灯状态,不太合理啊,而且这样的话调节时间的意义就没了,因此我最终设计了六个变量,分别为南北红灯、绿灯、黄灯时间和东西红灯、绿灯、黄灯时间。

        关于顺序控制,学过PLC的话,应该很清楚,关注切换条件就好,下面我们借助一个图更好的理解思路。

63af386160334096ac560b4120dbb4a0.png

图中NS表示南北方向、WE表示东西方向。我们可以将交通灯运行分为四个阶段:

  (1)南北红东西绿

  (2)南北红东西黄

  (3)南北绿东西红

  (4)南北黄东西红

阶段一切阶段二标志,东西绿灯时间为0。阶段二切阶段三,东西黄灯时间为0。阶段三切阶段四,南北绿灯为0,阶段四结束,重装值,进行循环。

本次我定义了一个状态变量,采用switch结构选择

程序设计如下:

1)数码管变量

void isr_timer0() interrupt 1
{
	count_time++;
	TL0 = 0xc8;				//设置定时0初始值
	TH0 = 0x38;				//设置定时0初始值
	if(count_time == 20)
	{
		count_time = 0;//1s时间到
		switch(state)
		{
			case 0://阶段一:南北红,东西绿
			{
				ns_red_time --;
				we_green_time --;
				ns_time = ns_red_time;
				we_time = we_green_time;
				if(we_green_time == 0&&ns_red_time>=0)//切换条件
				{
					state = 1;
				}
				break;
			}
			case 1://阶段二:南北红,东西黄
			{
				ns_red_time --;
				ns_time = ns_red_time;
				we_yellow_time --;
				we_time = we_yellow_time;
				if(we_yellow_time==0)//切换条件
				{
					state = 2;
				}
				break;
			}
			case 2://阶段三:南北绿,东西红
			{
				ns_green_time --;
				we_red_time --;
				we_time = we_red_time;
				ns_time = ns_green_time;
				if(ns_green_time == 0&&we_red_time>=0)//切换条件
				{
					state = 3;
				}
				break;
			}
			case 3://阶段四:南北黄,东西红
			{
				we_red_time --;
				we_time = we_red_time;		
				ns_yellow_time --;
				ns_time = ns_yellow_time;
				if(ns_yellow_time == 0)//切换条件
				{
					state = 0;
					we_red_time = we_red_param;
					we_green_time = we_green_param;
					we_yellow_time = 3;
					ns_red_time = ns_red_param;
					ns_green_time = ns_green_param;
					we_yellow_time = 3;
				}
				break;
			}
		}
	}
}

2)LED灯

/*********************************************************
函数名: dis_led
功能:正常模式下LED显示
形参:无
返回值:无
*********************************************************/
void dis_led()
{
	if(!mode)
	{
		switch(state)
		{
				case 0://阶段一:南北红,东西绿
			{
				P2 = 0xeb;
				break;
			}
			
				case 1://阶段二:南北红,东西黄
			{
				P2 = 0xf3;
				break;
			}
			
				case 2://阶段三:南北绿,东西红
			{
				P2 = 0xdd;
				break;
			}
			
				case 3://阶段四:南北黄,东西红
			{
				P2 = 0xbe;
				break;
			}
				default:break;
		}
	}
}

正常模式的讲完了,下面我们进入按键部分,这里写的比较繁琐,因为要保证其按下后连续执行,所以定义了一个变量mode,对应夜间模式、禁行模式、加时间、减时间等操作,根据不同mode值执行不同操作,确保了响应的及时性,同时任意情况下都可以丝滑切换,设计如下:

void key_scan()
{
	if(!K1)//夜间模式
	{
		delay(10);
		if(!K1)
		{
			while(!K1);
			mode = 1;
		}
	}

	if(!K2 )//南北禁行
	{
		delay(10);
		if(!K2)
		{
			while(!K2);
			mode = 2;
		}
	}
	
	if(!K3 )//东西禁行
	{
		delay(10);
		if(!K3)
		{
			while(!K3);
			mode = 3;
		}
	}
	
	if(!K4)//加时间
	{
		delay(10);
		if(!K4)
		{
			while(!K4);
			mode = 4;
			ns_red_param++;
			we_green_param++;
			ns_green_param--;
			we_red_param--;
			if(ns_red_param==55)
			{
				ns_red_param = 30;
				ns_green_param = 27;
				we_red_param = 30;
				we_green_param = 27;
			}
		}
	}
	
	if(!K5)//减时间
	{
		delay(10);
		if(!K5)
		{
			while(!K5);
			mode = 5;
			ns_red_param--;
			ns_green_param++;
			we_green_param--;
			we_red_param++;
			if(we_red_param==55)
			{
				ns_red_param = 30;
				ns_green_param = 27;
				we_red_param = 30;
				we_green_param = 27;
			}
		}
	}
	
	if(!K6)//确认
	{
		delay(10);
		if(!K6)
		{
			while(!K6);
			mode = 6;
		}
	}
}
/*********************************************************
函数名:key_pron
功能:按键处理函数,根据mode值调用函数
形参:无
返回值:无
*********************************************************/
void key_pron()
{
	switch(mode)
	{
		case 1:
		{
			K1_pron();
			break;
		}
		
		case 2:
		{
			K2_pron();
			break;
		}
		
		case 3:
		{
			K3_pron();
			break;
		}
		
		case 4:
		{
			K4_pron();
			break;
		}
		
		case 5:
		{
			K5_pron();
			break;
		}
		
		case 6:
		{
			K6_pron();
			break;
		}
	}
}

OK,本次设计大体已经结束。

下面是完整代码

(1)main.c

/*********************************************
Author:sakura
Time:2023/5/16
funcation:模拟日常生活中的交通灯,拥有正常模式
			、夜间模式、禁行模式、调节时间灯等
			功能,通过按键进行切换
copyright:版权所有,借鉴请注明
*********************************************/
#include "main.h"//系统头文件
#include "seg.h"
#include "led.h"
#include "key.h"
/*********************************************************
函数名:main
功能:主函数入口
形参:无
返回值:无
*********************************************************/
void main()
{
	sys_Init();
	while(1)
	{
		dis_seg();
		dis_led();
		key_pron();
		key_scan();
	}
}

  (2)main.h

#ifndef __MAIN_H
#define __MAIN_H

#define u8 unsigned char
#define u16 unsigned int
	
#include <reg51.h>

#endif

  (3)seg.c

#include "seg.h"
//段码0-9
u8 code SMG_NODOT[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//无小数点

u8 ns_red_time = 0;//南北红灯时间
u8 ns_green_time = 0;//南北绿灯时间
u8 ns_yellow_time = 3;//南北黄灯时间

u8 we_red_time = 0;//东西红灯时间
u8 we_green_time = 0;//东西绿灯时间
u8 we_yellow_time = 3;//东西黄灯时间

u8 ns_red_param = 30;//南北红灯时间参数,后期修改时间使用
u8 ns_green_param = 27;//南北绿灯时间参数,后期修改时间使用
u8 we_red_param = 30;//东西红灯时间参数,后期修改时间使用
u8 we_green_param = 27;//南北绿灯时间参数,后期修改时间使用

u8 ns_time = 30;//数码管南北向变量
u8 we_time = 27;//数码管东西向变量

u8 count_time = 0;//定时器计数变量
u8 state = 0;//状态变量
/*********************************************************
函数名:delay
功能:延时函数,延时n*1ms
形参:n,范围0-255
返回值:无
*********************************************************/
void delay(u8 n)//1ms
{
	while(n--)
	{
		unsigned char i, j;

		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);

	}
}
/*********************************************************
函数名:seg_Init
功能:数码管显示初始化
形参:pos:0-3,显示位   value:段码
返回值:无
*********************************************************/
void seg_Init(u8 pos,u8 value)
{
	P3 = ~(0x01 << pos);//选择位
	P0 = value;//数据
	delay(5);
}
/*********************************************************
函数名:dis_seg
功能:数码管显示函数
形参:无
返回值:无
*********************************************************/
void dis_seg()
{
	if(mode == 0)//正常模式
	{
		seg_Init(0,SMG_NODOT[ns_time/10]);
		seg_Init(1,SMG_NODOT[ns_time%10]);
		
		seg_Init(2,SMG_NODOT[we_time/10]);
		seg_Init(3,SMG_NODOT[we_time%10]);
	}
}
/*********************************************************
函数名:sys_Init
功能:系统初始化,定时器初值,数码管数值重装
形参:无
返回值:无
*********************************************************/
void sys_Init()
{
	TMOD = 0x01;

	TL0 = 0xc8;				//设置定时0初始值50ms
	TH0 = 0x38;				//设置定时0初始值
	
	ET0 = 1;
	EA = 1;
	
	ns_red_time = ns_red_param;//数值重装(初始化)
	ns_green_time = ns_green_param;
	we_red_time = we_red_param;
	we_green_time =we_green_param;
	
	TR0 = 1;
}
/*********************************************************
函数名:isr_timer0
功能:定时器0中断处理函数,每过1s,数码管值减一
形参:无
返回值:无
*********************************************************/
void isr_timer0() interrupt 1
{
	count_time++;
	TL0 = 0xc8;				//设置定时0初始值
	TH0 = 0x38;				//设置定时0初始值
	if(count_time == 20)
	{
		count_time = 0;//1s时间到
		switch(state)
		{
			case 0://阶段一:南北红,东西绿
			{
				ns_red_time --;
				we_green_time --;
				ns_time = ns_red_time;
				we_time = we_green_time;
				if(we_green_time == 0&&ns_red_time>=0)//切换条件
				{
					state = 1;
				}
				break;
			}
			case 1://阶段二:南北红,东西黄
			{
				ns_red_time --;
				ns_time = ns_red_time;
				we_yellow_time --;
				we_time = we_yellow_time;
				if(we_yellow_time==0)//切换条件
				{
					state = 2;
				}
				break;
			}
			case 2://阶段三:南北绿,东西红
			{
				ns_green_time --;
				we_red_time --;
				we_time = we_red_time;
				ns_time = ns_green_time;
				if(ns_green_time == 0&&we_red_time>=0)//切换条件
				{
					state = 3;
				}
				break;
			}
			case 3://阶段四:南北黄,东西红
			{
				we_red_time --;
				we_time = we_red_time;		
				ns_yellow_time --;
				ns_time = ns_yellow_time;
				if(ns_yellow_time == 0)//切换条件
				{
					state = 0;
					we_red_time = we_red_param;
					we_green_time = we_green_param;
					we_yellow_time = 3;
					ns_red_time = ns_red_param;
					ns_green_time = ns_green_param;
					ns_yellow_time = 3;
				}
				break;
			}
		}
	}
}

  (4)seg.h

#ifndef __SEG_H
#define __SEG_H

#include "main.h"

void delay(u8 n);//1ms
void seg_Init(u8 pos,u8 value);
void dis_seg();
void sys_Init();
extern u8 mode;
#endif

  (5)led.c

#include "led.h"
extern u8 state;
extern u8 mode;
/*********************************************************
函数名: dis_led
功能:正常模式下LED显示
形参:无
返回值:无
*********************************************************/
void dis_led()
{
	if(!mode)
	{
		switch(state)
		{
				case 0://阶段一:南北红,东西绿
			{
				P2 = 0xeb;
				break;
			}
			
				case 1://阶段二:南北红,东西黄
			{
				P2 = 0xf3;
				break;
			}
			
				case 2://阶段三:南北绿,东西红
			{
				P2 = 0xdd;
				break;
			}
			
				case 3://阶段四:南北黄,东西红
			{
				P2 = 0xbe;
				break;
			}
				default:break;
		}
	}
}

  (6)led.h

#ifndef __LED_H
#define __LED_H

#include "main.h"
void dis_led();

#endif

 (7)key.c

#include "key.h"
u8 mode = 0;
/*********************************************************
函数名:key_scan
功能:按键扫描函数,mode表示按键切换的功能
形参:无
返回值:无
*********************************************************/
void key_scan()
{
	if(!K1)//夜间模式
	{
		delay(10);
		if(!K1)
		{
			while(!K1);
			mode = 1;
		}
	}

	if(!K2 )//南北禁行
	{
		delay(10);
		if(!K2)
		{
			while(!K2);
			mode = 2;
		}
	}
	
	if(!K3 )//东西禁行
	{
		delay(10);
		if(!K3)
		{
			while(!K3);
			mode = 3;
		}
	}
	
	if(!K4)//加时间
	{
		delay(10);
		if(!K4)
		{
			while(!K4);
			mode = 4;
			ns_red_param++;
			we_green_param++;
			ns_green_param--;
			we_red_param--;
			if(ns_red_param==55)
			{
				ns_red_param = 30;
				ns_green_param = 27;
				we_red_param = 30;
				we_green_param = 27;
			}
		}
	}
	
	if(!K5)//减时间
	{
		delay(10);
		if(!K5)
		{
			while(!K5);
			mode = 5;
			ns_red_param--;
			ns_green_param++;
			we_green_param--;
			we_red_param++;
			if(we_red_param==55)
			{
				ns_red_param = 30;
				ns_green_param = 27;
				we_red_param = 30;
				we_green_param = 27;
			}
		}
	}
	
	if(!K6)//确认
	{
		delay(10);
		if(!K6)
		{
			while(!K6);
			mode = 6;
		}
	}
}
/*********************************************************
函数名:key_pron
功能:按键处理函数,根据mode值调用函数
形参:无
返回值:无
*********************************************************/
void key_pron()
{
	switch(mode)
	{
		case 1:
		{
			K1_pron();
			break;
		}
		
		case 2:
		{
			K2_pron();
			break;
		}
		
		case 3:
		{
			K3_pron();
			break;
		}
		
		case 4:
		{
			K4_pron();
			break;
		}
		
		case 5:
		{
			K5_pron();
			break;
		}
		
		case 6:
		{
			K6_pron();
			break;
		}
	}
}
/*********************************************************
函数名:K1_pron
功能:夜间模式函数
形参:无
返回值:无
*********************************************************/
void K1_pron()//夜间
{
	TR0 = 0;
	seg_Init(0,SMG_NODOT[0]);
	seg_Init(1,SMG_NODOT[0]);
	seg_Init(2,SMG_NODOT[0]);
	seg_Init(3,SMG_NODOT[0]);
	P2 = 0xf6;
	delay(10);
	P2 = 0xff;
	delay(10);
}
/*********************************************************
函数名:K2_pron
功能:南北禁行处理函数
形参:无
返回值:无
*********************************************************/
void K2_pron()//南北禁行
{
	TR0 = 0;
	seg_Init(0,SMG_NODOT[9]);
	seg_Init(1,SMG_NODOT[9]);
	
	seg_Init(2,SMG_NODOT[0]);
	seg_Init(3,SMG_NODOT[0]);
	P2 = 0xeb;
}
/*********************************************************
函数名:K3_pron
功能:东西禁行处理函数
形参:无
返回值:无
*********************************************************/
void K3_pron()//东西禁行
{
	TR0 = 0;
	seg_Init(0,SMG_NODOT[0]);
	seg_Init(1,SMG_NODOT[0]);
	
	seg_Init(2,SMG_NODOT[9]);
	seg_Init(3,SMG_NODOT[9]);
	P2 = 0xdd;
}
/*********************************************************
函数名: K4_pron
功能:加时间处理函数
形参:无
返回值:无
*********************************************************/
void K4_pron()//加时间
{
	TR0 = 0;
	seg_Init(0,SMG_NODOT[ns_red_param/10]);
	seg_Init(1,SMG_NODOT[ns_red_param%10]);
	
	seg_Init(2,SMG_NODOT[we_green_param/10]);
	seg_Init(3,SMG_NODOT[we_green_param%10]);
}
/*********************************************************
函数名: K5_pron
功能:减时间处理函数
形参:无
返回值:无
*********************************************************/
void K5_pron()//减时间
{
	TR0 = 0;
	
	seg_Init(0,SMG_NODOT[ns_red_param/10]);
	seg_Init(1,SMG_NODOT[ns_red_param%10]);
	
	seg_Init(2,SMG_NODOT[we_green_param/10]);
	seg_Init(3,SMG_NODOT[we_green_param%10]);
}
/*********************************************************
函数名:K6_pron
功能:确认时间调整函数
形参:无
返回值:无
*********************************************************/
void K6_pron()//确认/退出
{
	TR0 = 1;
	mode = 0;
	ns_red_time = ns_red_param;
	ns_green_time = ns_green_param;
	we_red_time = we_red_param;
	we_green_time =we_green_param;
	ns_time = ns_red_time;
	we_time = we_green_time;
}

 (8)key.h

#ifndef __KEY_H
#define __KEY_H

#include "main.h"
sbit K1 = P1^0;
sbit K2 = P1^1;
sbit K3 = P1^2;
sbit K4 = P1^3;
sbit K5 = P1^4;
sbit K6 = P1^5;

extern void delay(u8 n);//1ms
extern void seg_Init(u8 pos,u8 value);
extern u8 code SMG_NODOT[10];
extern u8 ns_red_time;//南北红灯
extern u8 ns_green_time;

extern u8 we_red_time ;
extern u8 we_green_time;

extern u8 ns_red_param;
extern u8 ns_green_param;
extern u8 we_red_param;
extern u8 we_green_param;

extern u8 ns_time;
extern u8 we_time;

void key_scan();
void key_pron();
void K1_pron();//夜间
void K2_pron();//南北禁行
void K3_pron();//东西禁行
void K4_pron();//加时间
void K5_pron();//减时间
void K6_pron();//确认/退出	

#endif

5.测试视频

交通灯运行(源码与仿真搜作者:sakura(划水))

6.资源

链接:https://pan.baidu.com/s/1uqDNAAb8k1cpAkT_IHSAfw?pwd=nazb 
提取码:nazb

  • 24
    点赞
  • 165
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 24
    评论
### 回答1: 基于51单片机的数控可调稳压电源是一种能够根据负载的电压需求调整输出电压的电源系统。Proteus是一种电子电路仿真软件,可以帮助我们进行电路的设计和仿真。 在Proteus中,我们可以使用51单片机的模型来设计数控可调稳压电源的电路。首先,我们需要绘制一个适合的电路图,包括51单片机、电源电路和稳压电路。 在电源电路中,我们可以选择使用变压器、整流电路和滤波电路来将交流电转换为直流电。然后,将直流电输入稳压电路中,稳压电路可以采用反馈控制的方式来控制输出电压的稳定性。在稳压电路中,我们可以使用稳压芯片或者自己设计的反馈电路。 51单片机作为主控制器,可以通过检测输出电压和负载电流来动态地调整稳压电源的输出电压。通过编程,我们可以实现根据负载的电压需求进行动态调整。例如,当负载电压下降时,51单片机可以检测到并通过反馈信号调整稳压电流的输出,使其恢复到设定的目标电压。 除了电路设计,Proteus还可以进行电路的仿真,我们可以通过添加合适的信号源、示波器等元件来模拟电路的输入和输出情况。通过仿真,我们可以验证电路的性能和稳定性,优化电路设计。 总之,基于51单片机的数控可调稳压电源可以通过Proteus进行电路设计和仿真。通过设计合适的电路图和使用51单片机进行动态控制,我们可以实现电源输出电压的稳定调节,并通过Proteus进行仿真验证电路的性能。 ### 回答2: 基于51单片机的数控可调稳压电源proteus仿真,主要是通过使用Proteus软件来模拟51单片机及其外围电路,实现数控可调稳压电源的功能。 首先,在Proteus中创建一个新的项目,选择适合的51单片机型号并添加到项目中。然后,通过连线连接所需的电路元件,包括电源输入、电路滤波、稳压电路、AD转换器和显示装置等。 其次,根据设计要求,编写51单片机的程序代码,其中包了控制稳压电源输出电压的关键算法。代码中通过AD转换获取输入电压,并通过调节PWM信号来控制稳压电路的输出电压,从而实现可调稳压的功能。 接着,在Proteus中进行仿真。利用Proteus仿真功能,可以模拟稳压电源输出电压的变化、数码显示装置的显示效果等。可以设置输入电压的变化曲线,观察稳压电源在不同输入情况下的输出电压是否符合要求。 最后,分析仿真结果。根据仿真结果评估电路设计的稳定性和可靠性。如果稳压电源输出电压在设定范围内,且能够随输入变化而调整,则说明设计成功;反之则需要进行修改和优化。 总结来说,通过Proteus仿真,可以帮助我们验证基于51单片机的数控可调稳压电源的设计方案,减少不必要的实验和调试,节约时间和成本。同时,也能够提供电路稳定性和可靠性方面的信息,为后续的实际制作和使用提供参考。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sakura(划水)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值