简介
1.本系统采用STC89C51单片机以及单片机最小系统和74HC245电路以及外围的按键和数码管显示等部件,设计一个基于单片机的交通灯设计。设计通过两位一体共阴极数码管显示,并能通过按键对定时进行设置,而且具有蜂鸣器提示。
2、本设计拟实现的性能指标如下:
设在十字路口,分为东西向和南北向,在任一时刻只有一个方向通行,另一方向禁行,持续一定时间,经过短暂的过渡时间,将通行禁行方向对换。
四个状态归纳如下:
(1)东西方向红灯灭,同时绿灯亮,南北方向黄灯灭,同时红灯亮,倒计时20秒。此状态下,东西向禁止通行,南北向允许通行。
(2)东西方向绿灯灭,同时黄灯亮,南北方向红灯亮,倒计时5秒。此状态下,除了已经正在通行中的其他所以车辆都需等待状态转换。
(3)南北方向红灯灭,同时绿灯亮,东西方向黄灯灭,同时红灯亮,倒计时30秒。此状态下,东西向允许通行,南北向禁止通行。
(4)南北方向绿灯灭,同时黄灯亮,东西方向红灯亮,倒计时5秒。此状态下,除了已经正在通行中的其他所以车辆都需等待状态转换。
STC89C51单片机
STC89C51是一种低功耗、高性能CMOS8位微控制器,具有 4K 在系统可编程Flash 存储器。在单芯片上,拥有灵巧的8 位CPU 和在系统可编程Flash,使得STC89C51为众多嵌入式控制应用系统提供高灵活、超有效的解决方案。 具有以下标准功能: 4k字节Flash,512字节RAM, 32 位I/O 口线,看门狗定时器,内置4KB EEPROM,MAX810复位电路,三个16 位 定时器/计数器,一个6向量2级中断结构,全双工串行口。
STC89C51单片机引脚图
(一)STC89C51主要功能、性能参数如下:
(1)内置标准51内核,机器周期:增强型为6时钟,普通型为12时钟;
(2)工作频率范围:040MHZ,相当于普通8051的080MHZ;
(3)STC89C51RC对应Flash空间:4KB;
(4)内部存储器(RAM):512B;
(5)定时器\计数器:3个16位;
(6)通用异步通信口(UART)1个;
(7)中断源:8个;
(8)有ISP(在系统可编程)\IAP(在应用可编程),无需专用编程器\仿真器;
(9)通用I\O口:32\36个;
(10)工作电压:3.8~5.5V;
(11)外形封装:40脚PDIP、44脚PLCC和PQFP等。
(二)STC89C51单片机的引脚说明:
(二)STC89C51单片机的引脚说明:
VCC:供电电压。
GND:接地。
P0口:P0口为一个8位漏级开路双向I/O口,每脚可吸收8TTL门电流。当P1口的管脚第一次写1时,被定义为高阻输入。P0能够用于外部程序数据存储器,它可以被定义为数据/地址的第八位。在FIASH编程时,P0 口作为原码输入口,当FIASH进行校验时,P0输出原码,此时P0外部必须被拉高。
P1口:P1口是一个内部提供上拉电阻的8位双向I/O口,P1口缓冲器能接收输出4TTL门电流。P1口管脚写入1后,被内部上拉为高,可用作输入,P1口被外部下拉为低电平时,将输出电流,这是由于内部上拉的缘故。在FLASH编程和校验时,P1口作为第八位地址接收。
P2口:P2口为一个内部上拉电阻的8位双向I/O口,P2口缓冲器可接收,输出4个TTL门电流,当P2口被写“1”时,其管脚被内部上拉电阻拉高,且作为输入。并因此作为输入时,P2口的管脚被外部拉低,将输出电流。这是由于内部上拉的缘故。P2口当用于外部程序存储器或16位地址外部数据存储器进行存取时,P2口输出地址的高八位。在给出地址“1”时,它利用内部上拉优势,当对外部八位地址数据存储器进行读写时,P2口输出其特殊功能寄存器的内容。P2口在FLASH编程和校验时接收高八位地址信号和控制信号。
P3口:P3口管脚是8个带内部上拉电阻的双向I/O口,可接收输出4个TTL门电流。当P3口写入“1”后,它们被内部上拉为高电平,并用作输入。作为输入,由于外部下拉为低电平,P3口将输出电流(ILL)这是由于上拉的缘故。
P3.0 RXD(串行输入口)
P3.1 TXD(串行输出口)
P3.2 /INT0(外部中断0)
P3.3 /INT1(外部中断1)
P3.4 T0(记时器0外部输入)
P3.5 T1(记时器1外部输入)
P3.6 /WR(外部数据存储器写选通)
P3.7 /RD(外部数据存储器读选通)
P3口同时为闪烁编程和编程校验接收一些控制信号。
I/O口作为输入口时有两种工作方式,即所谓的读端口与读引脚。读端口时实际上并不从外部读入数据,而是把端口锁存器的内容读入到内部总线,经过某种运算或变换后再写回到端口锁存器。只有读端口时才真正地把外部的数据读入到内部总线。上面图中的两个三角形表示的就是输入缓冲器CPU将根据不同的指令分别发出读端口或读引脚信号以完成不同的操作。这是由硬件自动完成的,不需要我们操心,1然后再实行读引脚操作,否则就可能读入出错,为什么看上面的图,如果不对端口置1端口锁存器原来的状态有可能为0Q端为0Q^为1加到场效应管栅极的信号为1,该场效应管就导通对地呈现低阻抗,此时即使引脚上输入的信号为1,也会因端口的低阻抗而使信号变低使得外加的1信号读入后不一定是1。若先执行置1操作,则可以使场效应管截止引脚信号直接加到三态缓冲器中实现正确的读入,由于在输入操作时还必须附加一个准备动作,所以这类I/O口被称为准双向口。89C51的P0/P1/P2/P3口作为输入时都是准双向口。
RST:复位输入。当振荡器复位器件时,要保持RST脚两个机器周期的高电平时间。
ALE/PROG:当访问外部存储器时,地址锁存允许的输出电平用于锁存地址的地位字节。在FLASH编程期间,此引脚用于输入编程脉冲。在平时,ALE端以不变的频率周期输出正脉冲信号,此频率为振荡器频率的1/6。因此它可用作对外部输出的脉冲或用于定时目的。然而要注意的是:每当用作外部数据存储器时,将跳过一个ALE脉冲。如想禁止ALE的输出可在SFR8EH地址上置0。此时, ALE只有在执行MOVX,MOVC指令是ALE才起作用。另外,该引脚被略微拉高。如果微处理器在外部执行状态ALE禁止,置位无效。
/PSEN:外部程序存储器的选通信号。在由外部程序存储器取指期间,每个机器周期两次/PSEN有效。但在访问外部数据存储器时,这两次有效的/PSEN信号将不出现。
/EA/VPP:当/EA保持低电平时,则在此期间外部程序存储器(0000H-FFFFH),不管是否有内部程序存储器。注意加密方式1时,/EA将内部锁定为RESET;当/EA端保持高电平时,此间内部程序存储器。在FLASH编程期间,此引脚也用于施加12V编程电源(VPP)。
XTAL1:反向振荡放大器的输入及内部时钟工作电路的输入。
XTAL2:来自反向振荡器的输出。
数码管显示
数码管是一种半导体发光器件,其基本单元是发光二极管。数码管按段数分为七段数码管和八段数码管,八段数码管比七段数码管多一个发光二极管单元(多一个小数点显示);按能显示多少个“8”可分为1位、2位、4位等等数码管:按发光二极管单元连接方式分为共阳极数码管和共阴极数码管。共阳数码管是指将所有发光二极管的阳极接到一起形成公共阳极(COM)的数码管,共阳数码管在应用时应将公共极COM接到+5V,当某一字段发光二极管的阴极为低电平时,相应字段就点亮,当某一字段的阴极为高电平时,相应字段就不亮。共阴数码管是指将所有发光二极管的阴极接到一起形成公共阴极(COM)的数码管,共阴数码管在应用时应将公共极COM接到地线GND上,当某一字段发光二极管的阳极为高电平时,相应字段就点亮,当某一字段的阳极为低电平时,相应字段就不亮。
硬件电路设计
1.信号显示驱动电路
74HC245译码器可接受3位二进制加权地址输入(A0, A1和A2),并当使能时,提供8个互斥的低有效输出(Y0至Y7)。74HC245特有3个使能输入端:两个低有效(E1和E2)和一个高有效(E3)。除非E1和E2置低且E3置高,否则74HC138将保持所有输出为高。利用这种复合使能特性,仅需4片74HC245芯片和1个反相器,即可轻松实现并行扩展,组合成为一个1-32(5线到32线)译码器。任选一个低有效使能输入端作为数据输入,而把其余的使能输入端作为选通端,则74HC245亦可充当一个8输出多路分配器,未使用的使能输入端必须保持绑定在各自合适的高有效或低有效状态。
2.键盘输入电路
单片机键盘有独立键盘和矩阵式键盘两种:独立键盘每一个I/O 口上只接一个按键,按键的另一端接电源或接地(一般接地),这种接法程序比较简单且系统更加稳定;而矩阵式键盘式接法程序比较复杂,但是占用的I/O少。根据本设计的需要这里选用了独立式键盘接法。
注意消抖
3.蜂鸣器驱动电路
蜂鸣器驱动电路一般都包含以下几个部分:一个三极管、一个蜂鸣器、一个限流电阻。
蜂鸣器为发声元件,在其两端施加直流电压(有源蜂鸣器)或者方波(无源蜂鸣器)就可以发声,其主要参数是外形尺寸、发声方向、工作电压、工作频率、工作电流、驱动方式(直流/方波)等。这些都可以根据需要来选择。本设计采用有源蜂鸣器。
三极管Q1起开关作用,其基极的低电平使三极管饱和导通,使蜂鸣器发声;而基极高电平则使三极管关闭,蜂鸣器停止发声。
源文件下载
链接:原文件资源下载
#include//头文件 #define uchar unsigned char #define uint unsigned int //宏定义 uchar data buf[4]; //秒显示的变量 uchar data sec_dx=20; //东西数默认 uchar data sec_nb=30; //南北默认值 uchar data set_timedx=20; //设置东西方向的时间 uchar data set_timenb=30; //设置南北方向的时间 int n; uchar data countt0,countt1;//定时器0中断次数 //定义6组开关 sbit k4=P3^7; //切换方向 sbit k1=P3^5; //时间加 sbit k2=P3^6; //时间减 sbit k3=P3^4; //确认 sbit k5=P3^1; //禁止 sbit k6=P1^5; //夜间模式 sbit Red_nb=P2^6; //南北红灯标志 sbit Yellow_nb=P2^5; //南北黄灯标志 sbit Green_nb=P2^4; //南北绿灯标志 sbit Red_dx=P2^3; //东西红灯标志 sbit Yellow_dx=P2^2; //东西黄灯标志 sbit Green_dx=P2^1; //东西绿灯标志 bit set=0; //调时方向切换键标志 =1时,南北,=0时,东西 bit dx_nb=0; //东西南北控制位 bit shanruo=0; //闪烁标志位 bit yejian=0; //夜间黄灯闪烁标志位 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 delay(int ms); //延时子程序 void key(); //按键扫描子程序 void display(); //显示子程序 void logo(); //开机LOGO void Buzzer(); //主程序 void main() { TMOD=0X11; //定时器设置 TH1=0X3C; TL1=0XB0; TH0=0X3C; //定时器0置初值 0.05S TL0=0XB0; EA=1; //开总中断 ET0=1; //定时器0中断开启 ET1=1; //定时器1中断开启 TR0=1; //启动定时0 TR1=0; //关闭定时1 logo(); //开机初始化 P2=0Xc3; // 开始默认状态,东西绿灯,南北黄灯 sec_nb=sec_dx+5; //默认南北通行时间比东西多5秒 while(1) //主循环 { key(); //调用按键扫描程序 display(); //调用显示程序 } } //函数的定义部分 void key(void) //按键扫描子程序 { if(k1!=1) //当K1(时间加)按下时 { display(); //调用显示,用于延时消抖 if(k1!=1) //如果确定按下 { TR0=0; //关定时器 shanruo=0; //闪烁标志位关 P2=0x00; //灭显示 TR1=0; //启动定时1 if(set==0) //设置键按下 set_timedx++; //南北加1S else set_timenb++; //东西加1S if(set_timenb==100) set_timenb=1; if( set_timedx==100) set_timedx=1; //加到100置1 sec_nb=set_timenb ; //设置的数值赋给东西南北 sec_dx=set_timedx; do { display(); //调用显示,用于延时 } while(k1!=1); //等待按键释放 } } if(k2!=1) //当K2(时间减)按键按下时 { display(); //调用显示,用于延时消抖 if(k2!=1) //如果确定按下 { TR0=0; //关定时器0 shanruo=0; //闪烁标志位关 P2=0x00; //灭显示 TR1=0; //关定时器1 if(set==0) set_timedx--; //南北减1S else set_timenb--; //东西减1S if(set_timenb==0) set_timenb=99; if( set_timedx==0 ) set_timedx=99; //减到1重置99 sec_nb=set_timenb ; //设置的数值赋给东西南北 sec_dx=set_timedx; do { display(); //调用显示,用于延时 } while(k2!=1); //等待按键释放 } } if(k3!=1) //当K3(确认)键按下时 { display(); //调用显示,用于延时消抖 if(k3!=1) //如果确定按下 { TR0=1; //启动定时器0 sec_nb=set_timenb; //从中断回复,仍显示设置过的数值 sec_dx=set_timedx; //显示设置过的时间 TR1=0; //关定时器1 if(set==0) //时间倒时到0时 { P2=0X00; //灭显示 Green_dx=1; //东西绿灯亮 Red_nb=1; //南北红灯亮 sec_nb=sec_dx+5; //回到初值 } else { P2=0x00; //南北绿灯,东西红灯 Green_nb=1; Red_dx=1; sec_dx=sec_nb+5; } } } if(k4!=1) //当K4(切换)键按下 { display(); //调用显示,用于延时消抖 if(k4!=1) //如果确定按下 { TR0=0; //关定时器0 set=!set; //取反set标志位,以切换调节方向 TR1=0; //关定时器1 dx_nb=set; do { display(); //调用显示,用于延时 } while(k4!=1); //等待按键释放 } } if(k5!=1) //当K5(禁止)键按下时 { display(); //调用显示,用于延时消抖 if(k5!=1) //如果确定按下 { TR0=0; //关定时器 P2=0x00; //灭显示 Red_dx=1; Red_nb=1; //全部置红灯 TR1=0; sec_dx=00; //四个方向的时间都为00 sec_nb=00; do { display(); //调用显示,用于延时 } while(k5!=1); //等待按键释放 } } if(k6!=1) //当K6(夜间模式)按下 { display(); //调用显示,用于延时消抖 if(k6!=1) //如果确定按下 { TR0=0; //关定时器 P2=0x00; TR1=1; sec_dx=00; //四个方向的时间都为00 sec_nb=00; do { display(); //调用显示,用于延时 } while(k6!=1); //等待按键释放 } } } void display(void) //显示子程序 { buf[1]=sec_nb/10; //第1位 东西秒十位 buf[2]=sec_nb%10; //第2位 东西秒个位 buf[3]=sec_dx/10; //第3位 南北秒十位 buf[0]=sec_dx%10; //第4位 南北秒个位 P1=0xff; // 初始灯为灭的 P0=0x00; 灭显示 P1=0xfe; //片选LED1 P0=table[buf[1]]; //送东西时间十位的数码管编码 delay(1); //延时 P1=0xff; //关显示 P0=0x00; //灭显示 P1=0xfd; //片选LED2 P0=table[buf[2]]; //送东西时间个位的数码管编码 delay(1); //延时 P1=0xff; //关显示 P0=0x00; //关显示 P1=0Xfb; //片选LED3 P0=table[buf[3]]; //送南北时间十位的数码管编码 delay(1); //延时 P1=0xff; //关显示 P0=0x00; //关显示 P1=0Xf7; //片选LED4 P0=table[buf[0]]; //送南北时间个位的数码管编码 delay(1); //延时 } void time0(void) interrupt 1 using 1 //定时中断子程序 { TH0=0X3C; //重赋初值 TL0=0XB0; //12m晶振50ms//重赋初值 TR0=1; //重新启动定时器 countt0++; //软件计数加1 if(countt0==10) //加到10也就是半秒 { if((sec_nb<=5)&&(dx_nb==0)&&(shanruo==1)) //东西黄灯闪 { Green_dx=0; Yellow_dx=0; } if((sec_dx<=5)&&(dx_nb==1)&&(shanruo==1)) //南北黄灯闪 { Green_nb=0; Yellow_nb=0; } } if(countt0==20) // 定时器中断次数=20时(即1秒时) { countt0=0; //清零计数器 sec_dx--; //东西时间减1 sec_nb--; //南北时间减1 if((sec_nb<=5)&&(dx_nb==0)&&(shanruo==1)) //东西黄灯闪 { Green_dx=0; Yellow_dx=1; } if((sec_dx<=5)&&(dx_nb==1)&&(shanruo==1)) //南北黄灯闪 { Green_nb=0; Yellow_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) //当黄灯闪烁时间倒计时到0时, { P2=0x00; //重置东西南背方向的红绿灯 Green_nb=1; Red_dx=1; dx_nb=!dx_nb; shanruo=0; sec_nb=set_timenb; //重赋南北方向的起始值 sec_dx=set_timenb+5; //重赋东西方向的起始值 } if(dx_nb==1&&sec_dx==0) //当黄灯闪烁时间到 { P2=0X00; //重置东西南北的红绿灯状态 Green_dx=1; //东西绿灯亮 Red_nb=1; //南北红灯亮 dx_nb=!dx_nb; //取反 shanruo=0; //闪烁 sec_dx=set_timedx; //重赋东西方向的起始值 sec_nb=set_timedx+5; //重赋南北方向的起始值 } } } void time1(void) interrupt 3 //定时中断子程序 { TH1=0X3C; //重赋初值 TL1=0XB0; //12m晶振50ms//重赋初值 countt1++; //软件计数加1 if(countt1==10) // 定时器中断次数=10时(即0.5秒) { Yellow_nb=0; //南北黄灯灭 Yellow_dx=0; //东西黄灯灭 } if(countt1==20) // 定时器中断次数=20时(即1秒时) { countt1=0; //清零计数器 Yellow_nb=1; //南北黄灯亮 Yellow_dx=1; //东西黄灯亮 } } void logo()//开机的Logo "- - - -" { for(n=0;n<50;n++) //循环显示----50次 { P0=0x40; //送形“-” P1=0xfe; //第一位显示 delay(1); //延时 P1=0xfd; //第二位显示 delay(1); //延时 P1=0Xfb; //第三位显示 delay(1); //延时 P1=0Xf7; //第四位显示 delay(1); //延时 P1 = 0xff; //灭显示 } } void delay(int ms) //延时子程序 { uint j,k; for(j=0;j