基于8051单片机的交通灯设计方案

目录

一、功能要求

要求实现的功能

二、设计方案

1.系统时钟

2. 红绿灯时长及夜间模式时间段的存储

3.紧急情况设计

4.夜间模式设计

5.夜间行人过路设计

6.系统时钟及系统参数设置显示

7.数码管显示倒计时

8.LED显示红绿灯

三、系统硬件设计

1.数码管显示

2.矩阵键盘

3.蜂鸣器

4.PCF8591 八位AD/DA转换器(光——>光强)

5.AT24C02

6.DS1302实时时钟芯片

7.LED

四、系统软件设计

1.延时函数、选择锁存器模块

2.矩阵键盘模块

3.AT24C02存储数据模块

4.DS1302时钟模块

5.LED红绿灯模块

6.LCD1602显示模块

7.数码管显示模块

8.交通灯主模块

设计步骤

五、结论

参考文献

附录

(一)按键功能


在这篇博客中,我将分享自己用单片机8051设计的交通灯的相关内容。 以下我的博客内容仅可借鉴,严禁抄袭。

交通灯演示视频

一、功能要求

用8051单片机为核心控制部件,设计一个十字路口的红绿灯控制系统。

要求实现的功能

1.该交通灯控制器。用LED灯模拟指示东西和南北方向的通行控制,每个方向三种颜色,通行规则遵守一般规则,即“红灯停、绿灯行、黄灯齐止步”。

2.交通灯倒计时以秒为单位,两个方向倒计时需用数码管显示当前剩余秒数。

3.两个方向的每种颜色的灯的计时时长可以通过键盘设定,参数一旦设定在当前计时结束时生效。设定参数保存在EEPROM中,系统开机时,从EEPROM中读取参数配置系统,启动交通控制。

4.控制系统支持情急模式,即当情况发生时,按下紧急按钮,整个系统停止计时,两个方向红灯闪烁,数码管显示当时倒计时的剩余时间,再次按下紧急按钮时,系统从停止的计时时间继续计时。

5.系统实现时钟计时功能。系统时钟采用实时时钟DS1302实现,系统当前时间用LCD显示。

6.系统支持夜间模式,从夜里到凌晨某个时间段,系统工作在夜间模式,此时黄灯闪烁,数码管不显示以降低功耗。

7.在夜间模式下,配置行人过街按键,当行人通过按键触发系统,给行人需要通行的方向一定时长的绿灯倒计时,两个方向的LED和数码管同时工作,倒计时结束后,系统继续夜间模式工作。

8.夜间模式的时间段可以设定,参数保存在EEPROM中。

9.系统参数的设定通过LCD作为显示部件。

10.系统参数支持一键恢复出厂设置。

11. 夜间模式下行人过路配置的绿灯时长可调功能。

二、设计方案

为了完成上述的功能要求,我们需要分析实现各个要求所需要用到的单片机上的模块,思考如何实现功能要求。

1.系统时钟

选择DS1302实时时钟芯片来配置系统时钟。

图2.1.1 DS1302寄存器地址(实时时钟)

2. 红绿灯时长及夜间模式时间段的存储

       通过EEPROM芯片AT24C02来存储设置的东西南北方向红绿灯的时长以及夜间模式的时间段。AT24C02存储的内容具有掉电不丢失的特点。东西方向的绿黄红灯时长分别存储在0、2、4单元中,南北方向的绿黄红灯时长分别存储在1、3、5单元中。夜间模式的开始时间的时分存储在6、8单元,结束时间的时分存储在7、9单元。

图2.2.1 AT24C02器件地址

3.紧急情况设计

       通过按键S6来切换正常模式与紧急情况模式,当为紧急情况时,关闭中断,红灯闪烁,数码管显示当前倒计时。 

4.夜间模式设计

       当系统时钟处于夜间模式时,黄灯闪烁,只有液晶显示系统时间,数码管不显示。

5.夜间行人过路设计

       液晶显示系统时钟,led东西南北方向一直处于绿灯,数码管显示绿灯倒计时为20s。此时处于程序设计的状态s=6,一直到20s结束,恢复夜间模式。

6.系统时钟及系统参数设置显示

       通过LCD1602显示,主要对其进行写操作以及清屏操作。

图2.4.1 LCD1602显示屏

7.数码管显示倒计时

         前4个数码管显示东西方向倒计时,后4个显示南北方向倒计时。“A”代表红灯,“B”代表绿灯,“C”代表黄灯。

8.LED显示红绿灯

L1:东西方向红灯;L2:东西方向绿灯;L3:东西方向黄灯

L5:南北方向红灯;L6:南北方向绿灯;L7:南北方向黄灯

三、系统硬件设计

接下来,我将介绍一下单片机上所用到的各模块电路图原理,以及所集成的芯片说明。

1.数码管显示

图3.1 驱动数码管显示电路图

电路图说明:

       P2口的P2.7、P2.6、P2.5来作为138译码器的输入端,138译码器的输出端Y4、Y5、Y6、Y7分别用来作为4个74HC02的输入端,然后4个74HC02的输出端分别连接了4个HC573的LE端,用来控制P0口数据的输入直通与锁存 ;4个HC573分别控制LED、蜂鸣器、数码管的显示位和com公共端,通过P2口的数据改变来选通想要的HC573(如控制数码管com公共端的HC573),再改变P0口的数据来选通想要数码管并锁存数据,再改变P2口的数据来选通想要的HC573(如控制数码管显示位的HC573),再改变P0口的数据来让该数码管显示想要的数字,并锁存数据。然后重复上述步骤,依次改变P2口和P1口的数据来让八个数码管依次动态显示 。

2.矩阵键盘

图3.2 矩阵键盘电路图

3.蜂鸣器

图3.3 蜂鸣器相关电路图

4.PCF8591 八位AD/DA转换器(光——>光强)

图3.4 AD/DA转换器电路图

        PCF8591是一款单芯片、单电源、低功耗8位CMOS数据采集设备,具有四个模拟输入、一个模拟输出和一个串行IIC总线接口。

        三个地址引脚A0, A1和A2用于编程硬件地址,允许使用多达8个设备连接到IIC总线而不需要额外的硬件。

        该装置的功能包括模拟输入多路复用、片上跟踪和保持功能、8位模数转换和8位数模转换。最大转换速率由IIC总线的最大速度给出。

5.AT24C02

图3.5 AT24C02存储器电路图

        AT24C02为EEPROM,可以实现数据保存,且掉电不丢失。AT24C02的总容量是256个字节,该芯片通过 IIC 总线与外部连接,其有一个专门的写保护功能。该芯片5脚作为数据线,也就是IIC通信的SDA;6脚作为时钟线,也就是IIC通信的SCL。

       特点:工作电压:1.8V~5.5V;工作电流1mA,待机电流1uA;支持标准IIC通信协议;双向数据传输协议;支持硬件写保护功能;擦除次数可达10000次;存储数据时间超过100年。

6.DS1302实时时钟芯片

图3.6 DS1302时钟芯片

        DS1302时钟芯片可以对年、月、日、周、时、分、秒进行计时,且具有闰年补偿等多种功能。DS1302芯片包含一个用于存储实时时钟的 31字节的静态 RAM,可通过简单的串行接口与微处理器通讯,将当前时钟存于RAM。该芯片读写时钟或 RAM 数据时有单字节或多字节(脉冲串模式)数据传送方式;串行输入输出利于减少管脚数。 

7.LED

图3.7 LED电路图

8.LCD1602

图3.8 LCD1602电路图

       1602液晶也叫1602字符型液晶,它是一种专门用来显示字母、数字、符号等的点阵型液晶模块。1602LCD是指显示的内容16×2,即最多只能显示32个字符。 它由若干个8X5等点阵字符位组成,每个点阵字符位都可以显示一个字符,每位之间有一个点距的间隔,每行之间也有间隔,起到了字符间距和行间距的作用。

四、系统软件设计

(一)设计模块

接下来,我将分享我编写的模块代码以及对模块代码步骤的讲解。

本系统设计模块分为:延时函数、选择锁存器模块、矩阵键盘模块、AT24C02存储数据模块、DS1302时钟模块、LED显示模块、LCD1602显示模块以及交通灯主模块等。

1.延时函数、选择锁存器模块
#include "STC15.h"
#include "common.h"
void HC573_select(unsigned char channel)
{
  switch(channel)
  {
      case 0:P2=(P2&0x1f)|0x00;break;
	  case 4:P2=(P2&0x1f)|0x80;break;
	  case 5:P2=(P2&0x1f)|0xa0;break;
	  case 6:P2=(P2&0x1f)|0xc0;break;
	  case 7:P2=(P2&0x1f)|0xe0;break;
  }
}
void delay(unsigned int k)
{
   unsigned char i,j;
   while(k--)
   {
     for(i=0;i<50;i++)
	  for(j=0;j<10;j++)
	  ;
   };
}




图4.1.1 延时函数、选择锁存器模块代码

      HC573_select函数用来选通不同的锁存器,来选择对LED、数码管、蜂鸣器进行操作;

Delay函数用于对EEPROM写操作、矩阵键盘模块。

2.矩阵键盘模块
#include "STC15.h"
#include "key_scan.h"
#include "common.h"
unsigned char keynum;
sbit R1=P3^0;
sbit R2=P3^1;
sbit R3=P3^2;
sbit R4=P3^3;
sbit C1=P4^4;
sbit C2=P4^2;
sbit C3=P3^5;
sbit C4=P3^4;
unsigned char keycode[16]={0xee,0xde,0xbe,0x7e,0xed,0xdd,0xbd,0x7d,0xeb,0xdb,0xbb,0x7b,0xe7,0xd7,0xb7,0x77}; //用线反转法时各个按键对应的段码
unsigned char bdata key; //定义了一个可以位寻址的变量
sbit key0=key^0;
sbit key1=key^1;
sbit key2=key^2;
sbit key3=key^3;
sbit key4=key^4;
sbit key5=key^5;
sbit key6=key^6;
sbit key7=key^7;
void scan2()                  //线反转法
{
	unsigned char i=0;
	R1=R2=R3=R4=0;
	C1=C2=C3=C4=1;
	if(C1==0 || C2==0 | C3==0 | C4==0)	 //有按键被按下
	{
		delay(1);	  //消抖
		if(C1==0 || C2==0 | C3==0 | C4==0)
		{
			key7=C4;
			key6=C3;
			key5=C2;
			key4=C1;
			
			R1=R2=R3=R4=1;
			C1=C2=C3=C4=0;
			key3=R4;
			key2=R3;
			key1=R2;
			key0=R1;
		}
		while((P3 & 0x0f)!=0x0f); //等待键释放
		while(key!=keycode[i])
		{
			i++;
		}
		keynum=i;	 //得出该按键的序号(从右往左,从上往下编号)
	}
}

图4.2.1 矩阵键盘模块代码

该模块采用线反转法。

线反转法

       无论被按键是处于第一行或是最后一行,均只需经过两步便能获得此按键所在的行列值。用线反转法识别闭合键时,要将行线接一个并行口,列线也接到一个并行口(也可以合用一个口),先让行线工作在输出方式,列线工作在输入方式,即往输出端口各行线上全部送“0”,输入端口都送“1”,然后从输入端口读入列线的值。如果此时有某个键被按下,则必定会使某一列线值为“0”。然后,重新设置两个并行端口的工作方式,使其互换,将刚才读得的列线值从并行端口输出,再读取行线的输入值,那么,在闭合键所在的行线上的值必定为“0”,这样,被按下的键的行列值就可以获得了。

3.AT24C02存储数据模块
#include"STC15.h"
#include"iic.h"
#include"AT24C02.h"
void WR_AT24C02(unsigned char address,unsigned char dat)
{
   IIC_Start(); 
   IIC_SendByte(0xa0);
   IIC_WaitAck(); 
   IIC_SendByte(address);
   IIC_WaitAck();
   IIC_SendByte(dat);
   IIC_WaitAck();
   IIC_Stop();  
}

unsigned char RC_AT24C02(unsigned char address)
{
   unsigned char value;
   IIC_Start(); 
   IIC_SendByte(0xa0);	  //器件地址
   IIC_WaitAck(); 
   IIC_SendByte(address);
   IIC_WaitAck();
  
   
   IIC_Start(); 
   IIC_SendByte(0xa1);	 //读操作
   IIC_WaitAck(); 
   value=IIC_RecByte();
   IIC_SendAck(1); 
   IIC_Stop(); 

   return value;

}

图4.3.1 AT24C02存储数据模块代码

该模块负责向AT24C02 EEPROM选定存储单元写内容以及对选定存储单元读内容。

4.DS1302时钟模块
#include"STC15.h"
#include"ds1302.h"
#include"AT24C02.h"
#include"time.h"  
unsigned char year_0,month_0,day_0,hour_0,min_0,sec_0,week_0,min_1,hour_1;
void ds1302_init(unsigned int year,month,day,hour,minute,second,week)
{
   Write_Ds1302_Byte(0x8e,0x00);
   Write_Ds1302_Byte(0x8c,year);
   Write_Ds1302_Byte(0x88,month);
   Write_Ds1302_Byte(0x86,day);
   Write_Ds1302_Byte(0x84,hour);
   Write_Ds1302_Byte(0x82,minute);
   Write_Ds1302_Byte(0x80,second);
   Write_Ds1302_Byte(0x8a,week);
   Write_Ds1302_Byte(0x8e,0x80);
}
void timer_show(unsigned char *stuno,unsigned char *clock)
{
 
   year_0=Read_Ds1302_Byte(0x8d);
   month_0=Read_Ds1302_Byte(0x89);
   day_0=Read_Ds1302_Byte(0x87);
   hour_0=Read_Ds1302_Byte(0x85);
   min_0=Read_Ds1302_Byte(0x83);
   sec_0=Read_Ds1302_Byte(0x81);
   week_0=Read_Ds1302_Byte(0x8b);
   hour_1=hour_0/16*10+hour_0%16;
   min_1=min_0/16*10+min_0%16;
   stuno[0]=2+48;
   stuno[1]=0+48;
   stuno[2]=year_0/16%16+48;
   stuno[3]=year_0%16+48;

   stuno[4]=45;

   stuno[5]=month_0/16+48;
   stuno[6]=month_0%16+48;
   stuno[7]=45;

   stuno[8]=day_0/16+48;
   stuno[9]=day_0%16+48;
   stuno[10]=45; 

   stuno[11]=week_0/16+48;
   stuno[12]=week_0%16+48; 
   stuno[13]=0;

   clock[0]=hour_0/16+48;
   clock[1]=hour_0%16+48;
   clock[2]=45;

   clock[3]=min_0/16+48;
   clock[4]=min_0%16+48;
   clock[5]=45;

   clock[6]=sec_0/16+48;
   clock[7]=sec_0%16+48;
   clock[8]=0;
}

图4.4.1 DS1302时钟模块代码

       该模块中ds1302_init函数功能为将时钟芯片初始化为要求的系统时钟。对该芯片进行写操作时要先对地址0x8Eh写入0x00来关闭写保护,能对该芯片进行写操作,完成该操作后再对地址0x8Eh写入0x80来打开写保护。比如:对地址0x84、0x82、0x80进行写操作是分别对时分秒进行初始化操作。对地址0x85、0x83、0x81进行读操作,是读出此刻的时分秒。

      Timer_show函数功能为将此时的时分秒保存在clock数组中,年月日周保存在stuno数组中。

5.LED红绿灯模块
#include"STC15.h"
#include"display.h"
#include "common.h"
#include "led.h"
unsigned char led_table[6]={0xed,0xeb,0xde,0xbe,0xee,0xdd}; //数组红绿黄

void led_1(unsigned char i)
{
			P0=led_table[i];
			HC573_select(4);
            HC573_select(0);
}


图4.5 LED模块代码

6.LCD1602显示模块
#include"STC15.h"
#include"ds1302.h"
#include"AT24C02.h"
#include"time.h"  
unsigned char year_0,month_0,day_0,hour_0,min_0,sec_0,week_0,min_1,hour_1;
void ds1302_init(unsigned int year,month,day,hour,minute,second,week)
{
   Write_Ds1302_Byte(0x8e,0x00);
   Write_Ds1302_Byte(0x8c,year);
   Write_Ds1302_Byte(0x88,month);
   Write_Ds1302_Byte(0x86,day);
   Write_Ds1302_Byte(0x84,hour);
   Write_Ds1302_Byte(0x82,minute);
   Write_Ds1302_Byte(0x80,second);
   Write_Ds1302_Byte(0x8a,week);
   Write_Ds1302_Byte(0x8e,0x80);
}
void timer_show(unsigned char *stuno,unsigned char *clock)
{
 
   year_0=Read_Ds1302_Byte(0x8d);
   month_0=Read_Ds1302_Byte(0x89);
   day_0=Read_Ds1302_Byte(0x87);
   hour_0=Read_Ds1302_Byte(0x85);
   min_0=Read_Ds1302_Byte(0x83);
   sec_0=Read_Ds1302_Byte(0x81);
   week_0=Read_Ds1302_Byte(0x8b);
   hour_1=hour_0/16*10+hour_0%16;
   min_1=min_0/16*10+min_0%16;
   stuno[0]=2+48;
   stuno[1]=0+48;
   stuno[2]=year_0/16%16+48;
   stuno[3]=year_0%16+48;

   stuno[4]=45;

   stuno[5]=month_0/16+48;
   stuno[6]=month_0%16+48;
   stuno[7]=45;

   stuno[8]=day_0/16+48;
   stuno[9]=day_0%16+48;
   stuno[10]=45; 

   stuno[11]=week_0/16+48;
   stuno[12]=week_0%16+48; 
   stuno[13]=0;

   clock[0]=hour_0/16+48;
   clock[1]=hour_0%16+48;
   clock[2]=45;

   clock[3]=min_0/16+48;
   clock[4]=min_0%16+48;
   clock[5]=45;

   clock[6]=sec_0/16+48;
   clock[7]=sec_0%16+48;
   clock[8]=0;
}

图4.6.1 LCD1602显示模块代码

该模块代码主要负责向液晶进行写指令、写内容显示操作。

7.数码管显示模块
#include "STC15.h"
#include "display.h"
#include "common.h"
unsigned char tab[15]={0xc0,0xF9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf,0xff,0x88,0x83,0xc6};		//A-HONG,B-LV,C-HUANG
unsigned char cur_led;
unsigned char q;
void display(unsigned char *s,unsigned char n)
{ 
 for(cur_led=0;cur_led<n;cur_led++)
	{
	  q=s[cur_led];
	  P0=tab[q];
	  HC573_select(7);
	  HC573_select(0);

	  P0=0x01<<cur_led;
	  HC573_select(6);
	  HC573_select(0);
	  delay(3);	 
	  
	  P0=0x00;
	  HC573_select(6);
	  HC573_select(0);
	}
}

图4.7.1 数码管显示模块代码

8.交通灯主模块

在这里,我将只说明主模块的设计思路,而不提供完整代码。主模块主要是利用以上封装后的模块代码函数来进行编写。

设计步骤

1.首先将ds1302.c,iic.h底层模块调进工程中,然后编写好数码管显示、矩阵键盘、存储器读写、实时时钟、LED灯红绿灯、LCD1602显示、交通灯等模块。

2.在主模块中,在定时器0中断服务函数中,写一个状态机,来实现交通灯东西南北方向上状态的自动转换 。状态为:S0:东西绿、南北红;S1: 东西黄、南北红;S2: 东西红、南北绿;S3: 东西红、南北黄;S4: 将红绿灯时长重新赋值;S5: 夜间行人过路,给一定通行时间。

3.在while中,调用scan2()函数判断键值,timer_show读系统时钟,判断是否夜间时间到,若到则开启夜间模式,黄灯闪烁,数码管不显示,可按键触发行人过路模式。当按键S6按下,就开启紧急模式。按键S16对AT24C02重新写入内容并将数据重新读入系统中,为一键恢复出厂设置。设置红绿灯时长,夜间模式开启与结束时分,行人过路配置的绿灯时长的编程思想都类似,将修改的内容写入对应的AT24C02的存储单元中,并读入系统中,要有按键等待键释放的思想。

五、结论

       本设计的难点在于要通过定时器来进行红绿灯的倒计时,而紧急情况、夜间模式下又需要关闭定时器中断,还有在进行系统参数的设置时也需要定时器中断关闭。在设计程序中,要时时刻刻考虑定时器是否要关闭,以及系统状态s应该设置的值。本设计中不仅要数码管显示,还要LCD1602显示系统时钟,因此要时刻考虑LCD1602使能端的开和关。交通灯的状态切换以及其他功能的实现主要是通过状态机来判断,根据状态切换的条件来判断系统该处于何种状态。

参考文献

1.单片机原理与应用技术,高惠芳主编. 科学出版社, 2010年5月;

2. MCS-51系列单片机系统及其应用,蔡美琴主编,高等教育出版社,2012年1月第2版;

3. 单片机的C语言应用程序设计,马忠梅主编,北京航空航天大学出版社,2017年10月出版;

4. 蓝桥杯单片机开发板官方资料(电子文档);

5. 其他网络资源。

附录

(一)按键功能

S7:白天模式。

S11:可切换调整的内容“g-东西方向绿灯、G-南北方向绿灯、y-东西方向黄灯、Y-南北方向黄灯、r-东西方向红灯、R-南北方向红灯”。 

S15: 可对S11选择内容作加操作,用LCD1602显示。可将设置内容保存在EEPROM中,掉电不丢失。

S19: 可对S11选择内容作减操作,用LCD1602显示。 可将设置内容保存在EEPROM中,掉电不丢失。

S6: 紧急情况按键。

S10:可切换要调整的内容(夜间模式的时间段),用LCD1602显示。“h”“m”代表开始时间的时分,“H”“M”代表终止时间的时分。

S14: 可对S10选择内容作加操作,用LCD1602显示。可将设置内容保存在EEPROM中,掉电不丢失。

S18: 可对S10选择内容作加操作,用LCD1602显示。可将设置内容保存在EEPROM中,掉电不丢失。S5:调出EEPROM中保存的x号闹钟内容。

S8:夜间模式时间段调整结束后的确认按键,若调整了时间段,则该按键必须按下确认。

S5: 对夜间模式下行人过路时间的设置,进行加操作。可将设置内容保存在EEPROM中,掉电不丢失。

S9: 对夜间模式下行人过路时间的设置,进行减操作。可将设置内容保存在EEPROM中,掉电不丢失。

S13: 夜间模式下行人过路时间的设置结束后的确认按键,若调整了该内容,则该按键必须按下确认。

S12:开启夜间模式下行人过路模式。

S16:一键恢复出厂设置。

LED对应的红绿灯关系:

L1:东西方向红灯;L2:东西方向绿灯;L3:东西方向黄灯

L5:南北方向红灯;L6:南北方向绿灯;L7:南北方向黄灯

物联沃分享整理
物联沃-IOTWORD物联网 » 基于8051单片机的交通灯设计方案

发表评论