基於51單片機交通燈程序設計與仿真

基於51單片機交通燈程序設計與仿真
摘要

交通信號燈是城市交通安全、有序、快速運行的重要保障。本文提出一種基於單片機的交通信號燈控制系統的設計方案。該系統模擬現實中十字路口的通行指示,倒計時、緊急車輛通行、強制東西/南北通行、夜間模式燈、轉向等功能。並結合交通燈,設計了人行道燈。採用STC89C51芯片作爲中心期間來設計交通燈來控制電路,結合七段共陰極數碼管顯示時間的模塊、交通燈顯示模塊、按鍵電路、IO口擴展電路等組成,通過程序設計和軟件仿真來實現紅、黃、綠、藍燃亮時間以及雙位數碼管顯示倒計時。

關鍵字:交通信號燈、人行道燈、STC89C51單片機、緊急通行、轉向功能

51單片機交通燈Proteus仿真

一、工程介紹

1.1、實現要求

  1. 控制交通燈實現車輛東西通行後,南北通行功能
  2. 控制轉向燈實現交通燈東西通行後左轉,南北通行後左轉功能
  3. 控制人行道燈實現交通燈東西通行的同時,同時南北人行道開啓,同理南北通行亦如此
  4. 可以控制按鈕實現東西時間的調整、夜間模式、緊急模式、強制東西/南北模式、查看時間

二、仿真電路設計

2.1環境介紹

2.2、電路設計

2.2.1、最小系統的實現

關於51單片機最小系統可以查看我以前博客,2.2.1、最小系統實現參考鏈接
src="51%E5%8D%95%E7%89%87%E6%9C%BA%E8%BD%A6%E6%B5%81%E9%87%8F%E4%BA%A4%E9%80%9A%E7%81%AF+%E4%BA%BA%E8%A1%8C%E9%81%93%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E4%B8%8E%E4%BB%BF%E7%9C%9F.assets/%E5%8E%9F%E4%BB%B6%E9%80%89%E6%8B%A9.png" align = "left" style="width:500px;height:300px" />
從Proteus中選取元件有:74HC595芯片、74HC245芯片、AT89C51單片機、BUTTON開關、BUZZER蜂鳴器、CAP電容、CAP-ELEC電解電容、CRYSTAL晶振、LED-BLUE藍色LED、LED-GREEN綠色LED、LED-RED紅色LED、LED-YELLOW黃色LED、PNP三極管、RES電阻、RESPACK-8排阻、TRAFFICLIGHTS交通燈、7SEG-MPX2-CC兩位八段共陰極數碼管

最小系統如下:

2.2.2、數碼管的使用

數碼管按段數可分爲七段數碼管和八段數碼管,八段數碼管比七段數碼管多一個發光二極管單元(多一個小數點顯示);按能顯示多少個“8”可分爲1位、2位、3位、4位、5位、6位、7位等數碼管。按發光二極管單元連接方式可分爲共陽極數碼管和共陰極數碼管。共陽數碼管是指將所有發光二極管的陽極接到一起形成公共陽極(COM)的數碼管,共陽數碼管在應用時應將公共極COM接到+5V,當某一字段發光二極管的陰極爲低電平時,相應字段就點亮,當某一字段的陰極爲高電平時,相應字段就不亮。共陰數碼管是指將所有發光二極管的陰極接到一起形成公共陰極(COM)的數碼管,共陰數碼管在應用時應將公共極COM接到地線GND上,當某一字段發光二極管的陽極爲高電平時,相應字段就點亮,當某一字段的陽極爲低電平時,相應字段就不亮。

一位數碼管內部原圖如圖所示:

在實際電路中數碼管是要加驅動芯片的,因爲單靠51單片機管腳輸出電流是不夠的,這裏通過添加74HC245驅動芯片驅動各個數碼管,用51單片機P0口傳送數據給數碼管,一定要加上拉電阻,並通過八同相三態總線收發器74HC245直接連接數碼管的八個LED。

74HC245芯片內部結構如下:

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

由於單片機或CPU的數據地址控制總線端口都有一定的負載能力,如果負載超過其負載能力,一般應加驅動器。主要應用於大屏顯示,以及其它的消費類電子產品中增加驅動。

仿真電路圖如下:

在這裏插入圖片描述
在這裏插入圖片描述

2.2.3、交通燈、人行燈及轉向燈

通過Proteus繪製路線以及人行道,可以畫出如下電路圖:

2.2.4、74HC595芯片的使用

74HC595是串口轉並口芯片,可輸出三種狀態:高電平、低電平和高組態。一片74HC595芯片可實現3根口線擴展爲8根口線.也可採用多片74HC595進行級聯的方式擴展輸出口線。控制可以採取如下控制:

仿真電路圖如下:

2.2.6、蜂鳴器電路的設計

當IO輸出低電平時,三極管導通,蜂鳴器發出聲音。

2.2.7、按鍵電路的設計

2.3、總體仿真電路圖

三、程序設計

3.1、環境介紹

3.2、定時器的使用

89C51單片機內部有兩個16位的定時/計數器,即定時器T0和定時器T1,這次仿真是要控制數碼管每隔1S變化一次,所以應把兩個定時器配置50MS的中斷,響應10次即完成0.5S,響應20次完成1S。由於51單片機1個機器週期=12個時鐘週期,系統時鐘頻率爲12MHZ,則一個機器週期爲1US,$ Sysclk = \frac{12MHZ}{12}=1MHZ$。下面是涉及定時器的相關寄存器:

工作模式寄存器TMOD是用於控制定時器0/1的工作模式,通過對TMOD進行賦值,則可以改變定時器的工作模式具體各位的定義如下:

定時器T0/T1有四種工作模式:模式0(13位定時器/計數器),模式1(16位定時器/計數器模式),模式2(8位自動重重裝載模式),模式3(兩個8位定時器/計數器)。T1除模式3外,其他工作模式與定時器/計數器0相同,T1載模式3無效,停止計數。

定時器的控制寄存器TCON爲定時器/計數器T0、T1的控制寄存器,同時也鎖存T0、T1溢出中斷源和外部請求中斷源等,格式如下:

配置T0和T1定時器程序如下:

void TIMER_init(void)     // 定時器初始化配置
{
	TMOD=0X11;            // 設置T0和T1爲模式1(16位定時器)
	TH1=0X3C;		     // 定時器1置初值 0.05S
	TL1=0XB0;
	TH0=0X3C;		     // 定時器0置初值 0.05S
	TL0=0XB0;
	EA=1;				 // 開總中斷
	ET0=1;				 // 定時器0中斷開啓
	ET1=1;				 // 定時器1中斷開啓
	TR0=1;				 // 啓動定時0
	TR1=0;				 // 關閉定時1
}

定時器T0實現交通燈和轉向燈程序流程:

T0開始計時
當東西/南北倒計時到0重置5秒
開啓黃燈閃爍和蜂鳴器
黃燈閃爍完開啓轉向燈10秒
當東西/南北轉向燈倒計時爲5S開啓閃爍

黃燈和轉向燈閃爍程序如下:

void Yellow_Flicker(void)
{
	if (countt0 == 10)						                    // 加到10也就是半秒
	{
		if ((sec_nb <= 5) && (dx_nb == 0) && (shanruo == 1))  	// 東西黃燈閃		
		{
			Green_dx = 0;
			Yellow_dx = 0;
			if (DX_LL == 1)
				SendTo595(0XF7);
			Buzz = 0;						                        // 蜂鳴器關
		}
		if ((sec_dx <= 5) && (dx_nb == 1) && (shanruo == 1)) // 南北黃燈閃		
		{
			Green_nb = 0;
			Yellow_nb = 0;
			if (NB_LL == 1)
				SendTo595(0XFB);
			Buzz = 0;					                          // 蜂鳴器關
		}
	}
	if (countt0 == 20)	      // 定時器中斷次數=20時(即1秒時)
	{
		if ((sec_nb <= 5) && (dx_nb == 0) && (shanruo == 1))  	// 打開交通燈		
		{
			Green_dx = 0;
			Buzz = 1;						                        // 蜂鳴器關
			if (DX_LL == 1)
				SendTo595(DX_L);
			else
				Yellow_dx = 1;
		}
		if ((sec_dx <= 5) && (dx_nb == 1) && (shanruo == 1))  // 南北黃燈閃		
		{
			Green_nb = 0;
			Buzz = 1;						                       // 蜂鳴器關
			if (NB_LL == 1)
				SendTo595(NB_L);
			else
				Yellow_nb = 1;
		}
	}
}

定時器0中斷程序如下:

void time0(void) interrupt 1 using 1  	 // 定時中斷子程序
{
	TH0=0X3C;							                 // 重賦初值
	TL0=0XB0;							                 // 12m晶振50ms//重賦初值
	TR0=1;								                 // 重新啓動定時器
	countt0++;							               // 軟件計數加1
	Yellow_Flicker();
		
	if(countt0==20)	      // 定時器中斷次數=20時(即1秒時)
	{	countt0=0;					// 清零計數器
		sec_dx--;						// 東西時間減1
		sec_nb--;						// 南北時間減1
				
		if(sec_dx==0&&sec_nb==5) 		//當東西倒計時到0時,重置5秒,用於黃燈閃爍時間   
		{
			sec_dx=5;
			shanruo=1;
		}
		if(sec_nb==0&&sec_dx==5)		//當南北倒計時到0時,重置5秒,用於黃燈閃爍時間   
		{
			sec_nb=5;
			shanruo=1;
		}
		if(dx_nb==0&&sec_nb==0&&DX_LL==0)			//當東西通行時間完畢,開始東西左轉
		{
			sec_nb = 10;
			sec_dx = 10;
			SendTo595(DX_L);		
			DX_LL=1;
			Yellow_dx = 0;
		}
		if(dx_nb==0&&sec_nb==0&&DX_LL==1)			//當黃燈閃爍時間倒計時到0時,
		{
 	    	Buzz=1;						//蜂鳴器開
			P2 &=0x81;					//重置東西南背方向的紅綠燈
			Green_nb=1;
			Red_dx=1;
			dx_nb=!dx_nb;
			shanruo=0;
			if(num_che_nb>set_timenb/2)//如果此時南北通行的車輛數大於預設通行量
				set_timenb=set_timenb+5;	
			if(num_che_nb==0)//如果南北方向無車輛通行,每次遞減5秒
				set_timenb=set_timenb-5;
			if(set_timenb<=15)
				set_timenb=15;
			sec_nb=set_timenb;			//重賦南北方向的起始值
			sec_dx=set_timenb+5;		//重賦東西方向的起始值
			num_che_nb=0;//清零
		  SendTo595(NB_R);	
      DX_LL=0;	
		}	
		if(dx_nb==1&&sec_dx==0&&NB_LL==0)			//當南北通行時間完畢,開始南北左轉
		{
			sec_nb = 10;
			sec_dx = 10;
			SendTo595(NB_L);		
			NB_LL=1;
			Yellow_nb = 0;	
		}
		if(dx_nb==1&&sec_dx==0&&NB_LL==1)			//當黃燈閃爍時間到
		{
			P2&=0X81;						//重置東西南北的紅綠燈狀態
			Green_dx=1;					 //東西綠燈亮
			Red_nb=1;					 //南北紅燈亮
			dx_nb=!dx_nb;				 //取反
			shanruo=0;					//閃爍
			if(num_che_dx>set_timedx/2)//如果此時南北通行的車輛數大於預設通行量
				set_timedx=set_timedx+5;	
			if(num_che_dx==0)//如果東西方向無車輛通行,每次遞減5秒
				set_timedx=set_timedx-5;
			if(set_timedx<=15)
				set_timedx=15;
			sec_dx=set_timedx;			//重賦東西方向的起始值
			sec_nb=set_timedx+5;		//重賦南北方向的起始值
			num_che_dx=0;//清零
			SendTo595(DX_R);		
			NB_LL=0;
		}

	}	
}

3.3、外部中斷的使用

程序使用兩個外部中斷實現東西/南北強制轉換,分別使用了外部中斷0和外部中斷1,且中斷結構如下。

通過結構圖可以看出使用外部中斷步驟如下:

  1. 設置電平觸發方式
  2. 開啓EX0/1
  3. 開啓總中斷

初始化外部中斷程序如下:

	IT0 = 1;//設置爲下降沿觸發
	EX0 = 1;//使能外部中斷
	IT1 = 1;
	EX1 = 1;

兩個外部中斷具體使用的功能:

//外部中斷0
void int0(void) interrupt 0 using 1	   //只允許東西通行
{
	TR0=0;								//關定時器0
	TR1=0;								//關定時器1
	P2=0x00;							//滅顯示
	Green_dx=1;							//東西方向置綠燈
	Red_nb=1;							//南北方向爲紅燈
	sec_dx=00;							//四個方向的時間都爲00
	sec_nb=00;	
	SendTo595(null);	
}
//外部中斷1
void int1(void) interrupt 2 using 1	  	 //只允許南北通行 
{
	TR0=0;								//關定時器0
	TR1=0;							   //關定時器1
	P2=0x00;							//滅顯示
	Green_nb=1;							//置南北方向爲綠燈
	Red_dx=1;							//東西方向爲紅燈
	sec_nb=00;							//四個方向的時間都爲00
	sec_dx=00;
	SendTo595(null);	
}

3.4、控制595芯片

我們都知道通信從大的方面有兩種:串行和並行。串行的最大優點是佔用總線少,但是傳輸速率低;並行恰恰相反,佔用總線多,傳輸速率高。市面上有很多這樣的芯片,有串入並出的(通俗講就是 一個一個進,最後一塊出來),有併入串出的(相對前者而言)。具體用哪種類型要根據我們得實際情況。比如利用單片機顯示數碼管單純的顯示一個數碼管如果僅僅是爲了顯示 那麼動用單片機一個端口(如P0或P1/P2/P3)那沒有什麼,當然這裏我說的數碼管是8段的(如果利用BCD類型 16進制數碼管那麼只需四個即可)就拿51類型的單片機來說,總共32個I/O口,一般如果不是做太大的工程是完全夠用的,但有些時候你會恨單片機怎麼不多長几條“腿”,怎麼省還是不夠用。這個時候就需要用到並轉串或者串轉並芯片來進行IO口的擴展,74HC595就是一種串行轉並行的芯片。參考鏈接

程序如下:

#define NB_L 0XFE
#define DX_L 0XFD
#define NB_G 0XF7
#define NB_R 0XF7
#define DX_G 5
#define DX_R 0XFB
#define null 0XF3
sbit DS = P2^0;
sbit SH_CP = P2^7;
sbit ST_CP = P1^4;
void SendTo595(uchar Data)//發送一個字節數據給595再並行輸出
{
   char i=0;
	ST_CP = 0;
   for(i;i<8;i++)
   {
      SH_CP = 0;  
      DS=0x80&Data;//&爲按位運算符,即全1爲1,有0爲0,上式也就是 (1000 0000)&(1111 1111)=1000 0000,若高位爲1則是1高位爲0則這個式子爲0 
      Data=_crol_(Data,1); //左移一位 將高位補給低位,如果二進制數爲01010101 那麼_crol_(1) 爲10101010
      SH_CP = 1;           //上升沿讓串行輸入時鐘變成高電平 並延時一個時鐘週期
      _nop_(); 
   }    
                            /*位移寄存器完畢,轉移到存儲寄存器*/
   ST_CP = 1;               //上升沿,存儲寄存器變爲高電平 延遲兩個時鐘週期
   _nop_();
   _nop_();
 }

3.5、數碼管程序設計

uchar code table[11]={	//共陰極字型碼
	0x3f,  //--0
	0x06,  //--1
	0x5b,  //--2
	0x4f,  //--3
	0x66,  //--4
	0x6d,  //--5
	0x7d,  //--6
	0x07,  //--7
	0x7f,  //--8
	0x6f,  //--9
	0x00   //--NULL
};
void display(void) //顯示子程序
{	

	if(xianshi_fx==0)//正常顯示
	{
	buf[1]=sec_nb/10; 		//第1位 東西顯示秒十位
	buf[2]=sec_nb%10; 		//第2位 東西顯示秒個位
	buf[3]=sec_dx/10; 		//第3位 南北顯示秒十位
	buf[0]=sec_dx%10; 		//第4位 南北顯示秒個位	
	}
	if(xianshi_fx==1)//查看通行時間
	{
	buf[1]=set_timenb/10; 		//第1位 東西通行秒十位
	buf[2]=set_timenb%10; 		//第2位 東西通行秒個位
	buf[3]=set_timedx/10; 		//第3位 南北通行秒十位
	buf[0]=set_timedx%10; 		//第4位 南北通行秒個位	
	}

	if(xianshi_fx==2)//查看紅外計數值
	{
	buf[1]=num_che_nb/10; 		//第1位 東西紅外計數值十位
	buf[2]=num_che_nb%10; 		//第2位 東西紅外計數值個位
	buf[3]=num_che_dx/10; 		//第3位 南北紅外計數值十位
	buf[0]=num_che_dx%10; 		//第4位 南北紅外計數值個位	
	}
	s1 = 1;s2 = 1;s3 = 1;s4 = 1;
	P0=0x00;				 ////滅顯示
	s1 = 0;s2 = 1;s3 = 1;s4 = 1;
	P0=table[buf[1]];		//送東西時間十位的數碼管編碼			
	delay(1);				//延時
	s1 = 1;s2 = 1;s3 = 1;s4 = 1;
	P0=0x00;				//滅顯示						   
	s1 = 1;s2 = 0;s3 = 1;s4 = 1;
	P0=table[buf[2]];		 //送東西時間個位的數碼管編碼
	delay(1);				 //延時
	s1 = 1;s2 = 1;s3 = 1;s4 = 1;
	P0=0x00;				//關顯示
	s1 = 1;s2 = 1;s3 = 0;s4 = 1;
	P0=table[buf[3]];		//送南北時間十位的數碼管編碼
	delay(1);				//延時
	s1 = 1;s2 = 1;s3 = 1;s4 = 1;	
	P0=0x00;				 //關顯示
	s1 = 1;s2 = 1;s3 = 1;s4 = 0;	
	P0=table[buf[0]];		//送南北時間個位的數碼管編碼
	delay(1);				//延時
}

五、結果分析與改進

  1. 問題:仿真結果表明,當增加時間或者減小時間那麼東西和南北的倒計時會同時增加
  2. 解決:通過設置獨立按鍵把東西/南北倒計時時間分開即可解決

參考資料:

  1. 51單片機 74hc595使用用法(利用proteus仿真)
  2. 74HC245 內部結構及作用
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章