一、 实验目的
1、 熟练掌握Keil和Proteus软件的C51设计与仿真操作
2、 熟悉C51编程的变量、存储等概念及使用方法
3、 掌握74LS245或74LS373的扩展方法
4、 熟悉定时计数器、外部中断的编程设计,初步掌握串行通信编程方法
二、 实验要求
1、 D级别要求:
a) 机制1:南北:红5秒,红 3 秒,绿5秒,黄闪3秒,在南北向亮灯的同时,东西方向的状态:绿5秒,黄闪3秒,红5秒,红 3 秒
b) 或机制2:可以按照四个路口方向逐次亮起红灯24秒,绿灯5秒,黄灯3秒。
c) 有一个外部中断,下降沿触发,触发后,四个方向全红,再次触发后,恢复正常
注:a) 和 b) 是两种不同的红绿灯控制机制,选择实现其一即可
2、 C级别要求:
a) 实现D级别的功能
b) 有设置键,可设置绿灯时间为其他秒数(红灯对应)
c) 可切换D级别的两种红绿灯机制,用设置键切换
3、 B级别要求:
a) 用2个单片机分别作为南北方向和东西方向的灯的控制
b) 2个单片机之间通过串行通信发送数据,实现同步,功能现象如D级要求
4、 A级别要求:
a) 实现B级别的功能
b) 某一个单片机上有设置键,能实现绿灯时间的设置(红灯对应)。
三、 实验实现的功能说明
在Proteus仿真软件中实现了A级别要求,主要完成了两个单片机分别控制两个方向的状态如D级别中的机制1状态,通过串行通讯用定时器1的方式二设置相同的波特率完成数据的收发,实现了红绿灯的正常同步,同时加入了两个数码管分别显示红绿灯的时间,并且可以通过四个按钮完成对红绿灯时间的加减,可以任意调节红绿灯的时间,同时代码也保持两个方向的红绿灯时间的对应。
四、 实验实现的原理及仿真电路设计
实验原理
为了便于观察与更好的模拟红绿灯将南北和东西方向的LED灯按照上北下南左西右东的方式排列,使之共地后分方向接入到主机与从机,将主机的RXD连接到从机的TXD,主机的TXD连接到从机的RXD,将主机与从机通过定时1的方式二设置相同的波特率后从而进行数据的收发。通过定时器0对红灯绿灯的时间进行控制,定义一个标志位,并且设置重装载值为50000us,即50毫秒溢出产生中断,当中断十次时候即0.5s时进行判断是否进行黄灯亮,在进入20次时即为1s时进行计时并通过算法判断各种灯的状态,黄灯比较特殊,我们在20次时将黄灯IO口置0并且抛出标志位,即可完成黄灯每过半秒闪烁,即可省略Delay延时函数在定时器中延时带来的干扰。
实验原理电路图:
主机代码:
#include <reg51.h>
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
u8 x;
u8 value;
u8 count0;//定时器0中断次数
sbit greenIO1=P1^0;
sbit yellowIO1=P1^1;
sbit redIO1=P1^2;
sbit k1=P3^4;
sbit k2=P3^5;
sbit k3=P3^6;
sbit k4=P3^7;
u8 red = 5;
u8 green = 5;
u8 Time1 = 7;
u8 Time_yello = 0;
u8 Time_green = 0;
u8 Time_yello1 = 0;
u8 change = 0;
u16 wei[4] = {0,0,0,0};
u16 wei2[4] = {0,0,0,0};
//共阴数码管编码表
u8 code numberList[] =
{0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71};
void delay(u16 i) // delay延时函数 i=1时,大约延时10us
{
while(i--);
}
void Time_arry(u16 count)
{
wei[0] = (count%1000)%100/10;
wei[1] = (count%1000)%100%10;
}
void Time_arry1(u16 count)
{
wei[2] = (count%1000)%100/10;
wei[3] = (count%1000)%100%10;
}
void Timer0Init() //定时器0初始化
{
TMOD|=0X21; //选择为定时器0模式,工作方式1,仅用TR0打开启动。
TH0 = (65536 - 50000)/ 256; //50000us定时,即50毫秒溢出产生中断
TL0 = (65536 - 50000)% 256; //50000us定时,即50毫秒溢出产生中断
ET0=1;//打开定时器0中断允许
EA=1;//打开总中断
TR0=1;//打开定时器
}
void TimerChange() //按键
{
if(k1==0)
{
delay(10); // 消抖
if(k1==0){
red += 1;
while(k1==0);
Time1 = 7 + red + green;
Time_yello = red+3;
Time_green = green+3;
Time_yello1 = Time_yello + 3;
}
}
if(k2==0)
{
delay(10); // 消抖
if(k2==0){
if(red >= 4)
{
red -= 1;
}
// change = 1;
while(k2==0);
Time1 = 7 + red + green;
Time_yello = red+3;
Time_green = green+3;
Time_yello1 = Time_yello + 3;
}
}
if(k3==0)
{
delay(10); // 消抖
if(k3==0){
green += 1;
// change = 1;
while(k3==0);
Time1 = 7 + red + green;
Time_yello = red+3;
Time_green = green+3;
Time_yello1 = Time_yello + 3;
}
}
if(k4==0)
{
delay(10); // 消抖
if(k4==0){
if(green>=4)
{
green -= 1;
}
//change = 1;
while(k4==0);
Time1 = 7 + red + green;
Time_yello = red+3;
Time_green = green+3;
Time_yello1 = Time_yello + 3;
}
}
}
void serial_initial(void) //串口初始化 //T1作为波特率发生器,使用工作模式1
{
TMOD=0x21; //T1工作方式2八位定时器自动重装
TH1=0xF3; //计数器初始值设置,注意波特率是4800
TL1=0xF3;
TR1=1; //启动定时器1
SM0=0;//设置串口工作方式
SM1=1;
EA=1;//打开总中断
ES=1;//打开串口中断
}
void ledShow()
{
u8 i;
for(i=0;i<=3;i++)
{
switch(i) //位选,选择点亮的数码管,
{
case(0):
P2 = 0xfe; break;//显示第0位
case(1):
P2 = 0Xfd; break;//显示第1位
case(2):
P2 = 0Xfb; break;//显示第0位
case(3):
P2 = 0Xf7; break;//显示第1位
}
P0=numberList[wei[i]];//发送段码
delay(200); //间隔一段时间扫描
P0=0x00;//消隐
}
}
void send_byte(u8 x) //串口发送一个字节
{
SBUF=x; //启动串口发送
while(TI==0);
TI=0; //发送完毕,置位TI=0
}
void main() //主函数
{
Time1 = Time1 + red + green;
Time_yello = red + 3;
Time_green = green+3;
Time_yello1 = Time_yello + 3;
serial_initial(); //串口初始化
Timer0Init(); //定时器0初始化
while(1)
{
Time_arry(Time_yello);
Time_arry1(Time_green);
ledShow();
TimerChange();
}
}
void runLight() interrupt 1 //定时中断程序
{
count0++; //软件计数加1
if(count0==10) //加到10也就是半秒
{
if(Time1<=4) //东西黄灯闪
{
greenIO1=0;
yellowIO1=0;
redIO1=0;
}
if((Time1<=Time_yello1)&&(Time1>Time_yello)) //南北黄闪
{
send_byte(2);
}
}
if(count0==20) // 定时器中断次数=20时(即1秒时)
{
serial_initial();
count0=0; //清零计数器
Time1--; //东西时间减1
if(Time1>Time_yello1) //东西红灯
{
redIO1=1;
yellowIO1=0;
greenIO1=0;
send_byte(1);
}
if((Time1<=Time_yello1)&&(Time1>Time_yello)) //东西红灯3s
{
redIO1=1;
yellowIO1=0;
greenIO1=0;
send_byte(2);
}
if((Time1<=Time_yello)&&(Time1>=3)) //东西绿灯
{
redIO1=0;
yellowIO1=0;
greenIO1=1;
send_byte(3);
}
if(Time1<=4) //东西黄灯闪
{
greenIO1=0;
redIO1=0;
yellowIO1=1;
send_byte(4);
}
if(Time1==0) //东西黄灯闪
{
yellowIO1=1;
delay(100);
P1=0X00; //重置状态
Time1=16;
redIO1=1;
send_byte(1);
}
}
TH0 = (65536 - 50000)/ 256; //50000us定时,即50毫秒溢出产生中断
TL0 = (65536 - 50000)% 256; //50000us定时,即50毫秒溢出产生中断
}
从机代码:
#include <reg51.h>
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
u8 x;
u8 value;
char count0;//定时器0中断次数
sbit greenIO2=P1^5;
sbit yellowIO2=P1^6;
sbit redIO2=P1^7;
char Time1=16;
int shan=0; //闪烁标志位
void delay(u16 i) // delay延时函数 i=1时,大约延时10us
{
while(i--);
}
void serial_initial(void) //串口初始化 //T1作为波特率发生器,使用工作模式1
{
TMOD=0X20;//定时器1方式2
TH1=0xF3; //计数器初始值设置,注意波特率是4800
TL1=0xF3;
TR1=1;//打开定时器
SM0=0;//设置串口工作方式
SM1=1;
REN=1;
EA=1;//打开总中断
ES=1;//打开串口中断
}
void send_byte(u8 x) //串口发送一个字节
{
SBUF=x; //启动串口发送
while(TI==0);
TI=0; //发送完毕,置位TI=0
delay(1);
}
void main() //主函数
{
serial_initial();
while(1);
}
//串口中断服务函数
void uart() interrupt 4
{
u8 Val;
value=SBUF;
switch(value)
{
case(1):
greenIO2=1;
yellowIO2=0;
redIO2=0;
break;
case(2):
greenIO2=0;
yellowIO2=!yellowIO2;
redIO2=0;
break;
case(3):
greenIO2=0;
yellowIO2=0;
redIO2=1;
break;
case(4):
greenIO2=0;
yellowIO2=0;
redIO2=1;
break;
}
Val = SBUF;
RI=0; //清除接收中断标志位
}
这是去年实验时的代码了,可能有些许不足,但是完成本次实验是完全没问题的,如果你发现了bug,请及时留言反应。