基于51单片机的交通灯控制系统的设计

        首先对系统的设计,自然想到软件部分与硬件部分,软件部分根据所要实现的功能进行编写,与硬件要进行对应。本文所介绍的是用keil进行c语言编写的,用STC-ISP进行烧录到板子上,在烧录之前,最好先用Proteus进行仿真,如果实现其所有功能后,再进行烧录,然后在板子上看实际实现的效果。以下我写的顺序是层层递进,展示如何一步步进行思考。可能有点长,但是包看懂的,无废话。

一、功能需求分析

1.基础功能

交通灯按以下时序循环:

交通信号灯变化表

东西

绿灯亮

黄灯闪烁

红灯亮

20S

5S

25S

南北

红灯亮

绿灯亮

黄灯闪烁

25S

20S

5S

2.扩展功能

a.手动调时:通过按键设置绿灯时长。

b.紧急模式:一键切换为全红/全黄闪烁/红绿灯的转换。

二、根据功能编写程序(keil中编写)

1.交通灯处理函数

思路:需要进行倒计时,所以1秒执行一次,然后每次都遵循红灯时间=绿灯时间+黄灯时间,当倒计时为0时候,则说明东西南北方向的红绿灯需要进行互换了。

void jiaotongdeng_dis()
{
	if (flag_1s == 1)	   //1秒执行一次里面的程序  
	{
		flag_1s = 0;
		if (flag_dx_nb == 0)	   //南北绿灯时间
		{
			dx_s--;		   //东西倒计时时间减1 
			nb_s--;		   //南北倒计时时间减1 
			if (dx_s == 5) //东西方向剩余5秒时,南北绿灯0
				nb_s = 5;  //南北置5秒进行黄灯闪烁
		}
		if (flag_dx_nb == 1)	   //东西绿灯时间
		{
			dx_s--;		   //南北倒计时时间减1
			nb_s--;		   //东西倒计时时间减1 
			if (nb_s == 5)
				dx_s = 5;
		}
		if (dx_s <= 0)		  //倒计时时间为0切换东西 南北绿灯时间 
		{
			flag_dx_nb = ~flag_dx_nb;
			if (flag_dx_nb == 1)	 //东西时间
			{
				dx_s = dx_time;
				nb_s = dx_time + 5;
			}
			else 				 //南北时间
			{
				dx_s = nb_time + 5;
				nb_s = nb_time;
			}
		}
	}
}

2.定时器初始化与中断服务程序

思路:由于1秒执行一次,所以就想到定时与中断

TH0:给定时器 0 的高 8 位赋初值。前面设置定时器 0 工作在方式 1,是 16 位定时器,计数值从初值开始向上累加,当计数值达到655362^{16}时产生溢出,触发中断。这里要实现 50ms 定时,已知单片机晶振频率(假设为 12MHz,机器周期为 1μs),定时器 0 每经过一个机器周期计数值加 1。50ms 内包含的机器周期数为50×1000=50000个。为了实现 50ms 定时,需要给定时器 0 设置一个初值,使得从该初值开始计数到65536溢出的时间为 50ms。65536−50000=15536,将这个值的高 8 位(15536÷256)赋给TH0。TL0同理。

/*********************定时器0初始化******************/
void time0_init()
{
	EA = 1;	 	  //开总中断
	TMOD = 0X01;	  //定时器0工作方式1,00000001
	ET0 = 1;		  //开定时器0中断 
	TR0 = 1;		  //允许定时器0定时
	TH0 = (65536 - 50000) / 256;
	TL0 = (65536 - 50000) % 256;     //50ms
}
/*********************定时器0中断服务程序************************/
void time0_int() interrupt 1
{
	static  uchar value;
	TH0 = (65536 - 50000) / 256;
	TL0 = (65536 - 50000) % 256;     //50ms
	value++;
	if (value % 10 == 0)
		flag_500ms = ~flag_500ms;	   //定时产生一个500毫秒的变量
	if (value >= 20)				//
	{
		value = 0;
		flag_1s = 1;	 //定时产生一个1秒的变量
	}
}

3.四个方向交通灯的亮灭

思路:在完成倒计时的设置后,接下来需要根据倒计时的时间来控制交通灯的亮灭和闪烁状态。

	/***********************南北时间绿灯时间*********************************/
	if (flag_dx_nb == 0)
	{
		if (dx_s > 5)       //当东西红灯倒计时大于5时
		{
			dx_red = 0;     //东西红灯亮
			dx_green = 1;	   //东西绿灯灭
			dx_yellow = 1;	   //东西黄灯灭
			nb_red = 1;     //南北红灯灭
			nb_green = 0;	   //南北绿灯亮
			nb_yellow = 1;	   //南北黄灯灭

		}
		else if (dx_s <= 5)		 //当小于5秒时  黄灯要闪了
		{
			dx_red = 0;     //东西红灯灭
			dx_green = 1;	   //东西绿灯灭
			dx_yellow = 1;	   //东西黄灯灭
			nb_red = 1;     //南北红灯灭
			nb_green = 1;	   //南北绿灯灭
			if (flag_500ms == 0)	   //黄灯闪烁
			{
				nb_yellow = 0;	//亮	
			}
			else
			{
				nb_yellow = 1;	//灭	
			}
		}
	}
	/***********************东西时间绿灯时间*********************************/
	if (flag_dx_nb == 1)
	{
		if (nb_s > 5)      //当南北红灯倒计时大于5时
		{
			dx_red = 1;  //东西红灯灭 
			dx_green = 0;	//东西绿灯亮
			dx_yellow = 1;	//东西黄灯灭
			nb_red = 0;  //南北红灯亮 
			nb_green = 1;	//南北绿灯灭
			nb_yellow = 1;	//南北黄灯灭
		}
		else if (nb_s <= 5)		 //当小于5秒时  黄灯要闪了
		{
			dx_red = 1;    //东西红灯灭
			dx_green = 1;	  //东西绿灯灭
			nb_red = 0;    //南北红灯亮
			nb_green = 1;	  //南北绿灯灭
			nb_yellow = 1;	  //南北黄灯灭
			if (flag_500ms == 0)	 //黄灯闪烁
			{
				dx_yellow = 0;//东西黄灯亮	
			}
			else
			{
				dx_yellow = 1;//东西黄灯灭	
			}
		}
	}
}

4.引脚的定义与数码管的显示

思路:由于需要观察到倒计时,所以想到需要进行显示出来。一想到显示,则需要进行接口的定义,然后设置高低电平进行显示。因此,先定义,再设置显示。

a.引脚的定义:数码管的编码有共阳极与共阴极,共阳极位选电平(即1)选中数码管,各段选电平(即0)选中各数码段,共阴极与此相反。在这里,我选用的是共阳极,例如用显示0来讲,oxC0(1100 0000)(小数点 g f e d c b a)。

由于要与引脚对应,所以这里最好结合仿真图(已放在第三部分),对于位选,就是选择个位还是十位,定义与仿真图结合看,有电源电路原因是需要进行驱动。由于需要进行控制方向的灯的亮灭,所以对于东西南北LED灯的显示进行定义。


//数码管段选定义       0   1   2    3    4    5 	   6	 7	  8	   9   A   B		
uchar smg_du[] = { 0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83 };	 //段码

uchar dis_smg[4] = { 0 };	   //数码管显示数组的缓冲区,初值为0确保启动时候都不亮

//数码管位选定义
sbit smg_we1 = P2 ^ 0;	    //数码管位选IO口定义
sbit smg_we2 = P2 ^ 1;		//数码管位选IO口定义
sbit smg_we3 = P3 ^ 6;		//数码管位选IO口定义
sbit smg_we4 = P3 ^ 7;		//数码管位选IO口定义

sbit dx_red = P2 ^ 7;	    //东西红灯IO口定义
sbit dx_green = P2 ^ 6;		//东西绿灯IO口定义
sbit dx_yellow = P2 ^ 5;		//东西黄灯IO口定义

sbit nb_red = P2 ^ 4;		//南北红灯IO口定义
sbit nb_green = P2 ^ 3;		//南北绿灯IO口定义
sbit nb_yellow = P2 ^ 2;		//南北黄灯IO口定义	

b.数码管显示:

void smg_we_switch(uchar i)//数码管位选函数
{
	switch (i)
	{
	case 0: smg_we1 = 0;  smg_we2 = 1; smg_we3 = 1;  smg_we4 = 1; break;
	case 1: smg_we1 = 1;  smg_we2 = 0; smg_we3 = 1;  smg_we4 = 1; break;
	case 2: smg_we1 = 1;  smg_we2 = 1; smg_we3 = 0;  smg_we4 = 1; break;
	case 3: smg_we1 = 1;  smg_we2 = 1; smg_we3 = 1;  smg_we4 = 0; break;
	}
}

void delay_1ms(uint q)//延时函数
{
	uint i, j;
	for (i = 0; i < q; i++)
		for (j = 0; j < 120; j++);
}

void display()//数码管显示函数
{
	for (i = 0; i < 4; i++)
	{
		P0 = 0xff;	          //消隐 
		smg_we_switch(i);  	  //位选
		P0 = dis_smg[i];	  //段选	  
		delay_1ms(3);		  //延时
	}
}

5.设置函数

思路:由于需要扩展的功能是可以自己设置时间,所以用按键进行控制时间的加与减,以及模式的切换(东西南北全部亮红灯,东西红灯南北绿灯,南北红灯东西绿灯,南北黄灯闪东西黄灯闪)。

/********************设置函数*****************/
void key_with()
{
	if (key_can == 4)   //交通管制按键 紧急模式 
	{
		flag_jdgz++;
		if (flag_jdgz > 4)
			flag_jdgz = 0;
		if (flag_jdgz == 1)	 //  全部亮红灯 
		{
			dx_red = 0;  //东西红灯亮 
			dx_green = 1;	//东西绿灯灭
			dx_yellow = 1;	//东西黄灯灭
			nb_red = 0;  //南北红灯亮 
			nb_green = 1;	//南北绿灯灭
			nb_yellow = 1;	//南北黄灯灭			
		}
		if (flag_jdgz == 2)	 //  东西红灯  南北绿灯
		{
			dx_red = 0;  //东西红灯亮 
			dx_green = 1;	//东西绿灯灭
			dx_yellow = 1;	//东西黄灯灭
			nb_red = 1;  //南北红灯灭 
			nb_green = 0;	//南北绿灯亮
			nb_yellow = 1;	//南北黄灯灭			
		}
		if (flag_jdgz == 3)	 //  南北红灯  东西绿灯
		{
			dx_red = 1;  //东西红灯灭 
			dx_green = 0;	//东西绿灯亮
			dx_yellow = 1;	//东西黄灯灭
			nb_red = 0;  //南北红灯亮 
			nb_green = 1;	//南北绿灯灭
			nb_yellow = 1;	//南北黄灯灭			
		}
		if (flag_jdgz == 4)	 //  南北黄灯闪  东西黄灯闪
		{
			dx_red = 1;  //东西红灯灭 
			dx_green = 1;	//东西绿灯灭
			nb_red = 1;  //南北红灯灭 
			nb_green = 1;	//南北绿灯灭
		}
	}
	if (key_can == 1)	  //设置键
	{
		menu_1++;
		if (menu_1 >= 3)
		{
			menu_1 = 0;  //menu_1 = 0 退出设置,是在正常显示界面下
		}
	}

	if (menu_1 == 1)	   //设置东西绿灯的时间
	{
		if (key_can == 2)  //加键 
		{
			dx_time++;		//设置东西绿灯的时间 加1
			if (dx_time > 99)
				dx_time = 99;
		}
		if (key_can == 3)  //减键 
		{
			dx_time--;		//设置东西绿灯的时间 减1
			if (dx_time <= 5)
				dx_time = 5;
		}
		dis_smg[0] = smg_du[dx_time % 10];		//显示设置的东西绿灯的时间 个位数
		dis_smg[1] = smg_du[dx_time / 10];	    //显示设置的东西绿灯的时间 十位数
		dis_smg[2] = smg_du[10];	//显示为A
		dis_smg[3] = smg_du[10];	//显示为A
	}
	if (menu_1 == 2)	   //设置南北绿灯的时间
	{
		if (key_can == 2)   //加键 
		{
			nb_time++;		//设置南北绿灯的时间 加1
			if (nb_time > 99)
				nb_time = 99;
		}
		if (key_can == 3)   //减键 
		{
			nb_time--;		//设置南北绿灯的时间 减1
			if (nb_time <= 5)
				nb_time = 5;
		}
		dis_smg[0] = smg_du[11];	//显示为B
		dis_smg[1] = smg_du[11];	//显示为B
		dis_smg[2] = smg_du[nb_time % 10];		//显示设置的南北绿灯的时间 个位数
		dis_smg[3] = smg_du[nb_time / 10];		//显示设置的南北绿灯的时间 十位数
	}
}

6.独立按键程序

 思路:前面第5步已经设好按键的功能,所以接下来就需要知道什么时候按键按下。

/********************独立按键程序*****************/
uchar key_can;	 //按键值

void key()	 //独立按键程序
{
	static uchar key_new;        //key_new  这个变量的功能是做按键松手检测的 
	key_can = 0;                //按键值还原成0
	if (key1 == 0 || key2 == 0 || key3 == 0 || key4 == 0)		//有按键按下 
	{
		delay_1ms(1);	     	//按键延时消抖动
		if (key_new == 1)
		{
			key_new = 0;		//key_new = 0   说明按键已按下
			if (key1 == 0)		//确认是按键按下
				key_can = 1; 	//得到按键值 
			if (key2 == 0)		//确认是按键按下
				key_can = 2; 	//得到按键值 
			if (key3 == 0)		//确认是按键按下
				key_can = 3; 	//得到按键值 
			if (key4 == 0)		//确认是按键按下
				key_can = 4; 	//得到按键值 
		}
	}
	else
		key_new = 1; 	//key_new = 1   说明按键已经松开了	
}

7.主函数

流程是先设定好初始时间,再检测是否有按键按下,有则进行对时间和模式的设置,没有则进行交通灯的处理,之后就是进行显示。

void main()
{
	time0_init();		  //定时器初始化程序
	dx_s = nb_time + 5;	  //南北时间
	nb_s = nb_time;	  //东西时间
	while (1)
	{
		key();			   //按键函数
		if (key_can > 0)	   //有按键按下 
			key_with();	   //按键处理设置函数
		if ((menu_1 == 0) && (flag_jdgz == 0))
			jiaotongdeng_dis();	   //交通灯处理函数

		if (flag_jdgz == 4)	//夜间模式
			if (flag_500ms == 1)		//  南北黄灯闪  东西黄灯闪
			{
				flag_500ms = 0;
				nb_yellow = ~nb_yellow;	   //  南北黄灯闪
				dx_yellow = ~dx_yellow;	   //  东西黄灯闪
			}
		display();	 //数码管显示函数 
	}
}

三、仿真(Proteus)

1.连接各个元器件

每个连接的都与程序代码中的定义相关联。

基于51单片机的交通灯控制系统的设计_第1张图片

2.导入代码

a.打开keil后,导出.hex文件。

基于51单片机的交通灯控制系统的设计_第2张图片

基于51单片机的交通灯控制系统的设计_第3张图片

b.打开Proteus,双击芯片(AT89C51),选择刚才导出的.hex文件,然后观察仿真效果。

基于51单片机的交通灯控制系统的设计_第4张图片

c.进行仿真验证

四、烧录(STC-ISP)

        在烧录之前,确保你的板子已经连接好(与仿真图对应)。具体操作如图。

基于51单片机的交通灯控制系统的设计_第5张图片

你可能感兴趣的:(51单片机,嵌入式硬件,单片机)