前言

单片机期末课程设计搞了一个最简单的交通灯,大概记录一下。

概要设计

交通灯系统设计

详细设计

初始化定时器&外部中断

  1. TMOD=0X11 定时器模式 1 ,组成一个 16 位的定时器

  2. 晶振频率为 11.0592M 时,定时 50ms 的初值为 0x4c、0x00 :

    1
    2
    3
    4
    TH1=0x4C;							//TH1重赋初值;11.0592m晶振50ms
    TL1=0X00;
    TH0=0x4C; //TH0重赋初值;11.0592m晶振50ms
    TL0=0X00;
  3. 启动定时0、定时1:TCON=0X50

  4. 开总中断;定时器1中断开启;开外部中断1;定时器0中断开启;开外部中断:IE=0x8F

  5. 初始化信号灯开关、时间:

    1
    2
    3
    P2=0Xc3;							//开始默认状态,东西绿灯,南北黄灯
    sec_nb=35; //南北方向初始时间
    sec_dx=30;

程序主循环

倒计时处理部分放在定时器中断服务中,buf 中的数字会不断刷新,主函数只需循环调用显示函数,显示倒计时。除此之外还判断是否进入特殊模式,特殊模式标志位是由外部中断修改:

1
2
3
4
5
6
7
8
9
void main()
{
init(); //初始化定时器、中断服务
while(1)
{
if(flag_option!=0)function(); //调用功能函数
display(); //调用显示函数
}
}

信号灯计时

使用定时器中断0循环计数 20 次记为 1 秒,当计数等于 20 时,进入处理倒计时数字处理逻辑部分。进入后首先重置计数器,然后各个方向时间 -1 :

1
2
3
4
5
if(countt0==20)	                  	//定时器中断次数=20时(即1秒时)
{
countt0=0; //清零计数器
sec_dx--; //东西时间减1
sec_nb--; //南北时间减1

时间 -1 后需要进一步判断是否需要切换状态。需要处理的有两种:

  1. 黄灯频闪:非通行方向时间剩余 5 秒,通行方向时间剩余 0 秒
  2. 切换通行方向:通行方向时间剩余 0 秒,非通行方向时间剩余 0 秒,且已经在黄灯频闪状态

黄灯频闪

进入条件:任意一个方向时间剩余 5 秒且另外一个方向时间剩余 0 秒,黄灯闪烁标志为 0 。

处理方法是将通行方向增加 5 秒用于黄灯频闪:

1
2
3
4
5
if(sec_nb==0&&sec_dx==5&&(Yellow_status==0))		//当南北倒计时到0时,重置5秒,用于黄灯闪烁时间
{
sec_nb=5;
Yellow_status=1;
}

进入黄灯闪烁模式后,判断通行方向,关闭通行方向绿灯、取反黄灯状态。

1
2
3
4
5
if((sec_dx<=5)&&(dx_nb==1)&&(Yellow_status==1))  		//南北黄灯闪
{
Green_nb=0;
Yellow_nb=~Yellow_nb;
}

另外一个方向的处理逻辑一致。

切换通行方向

进入条件:当非通行方向黄灯时间、通行方向倒计时到0时

1
2
3
4
5
6
7
8
9
10
if(dx_nb==1&&sec_dx==0&&sec_nb==0)			            //南北通行:当黄灯时间、通行方向倒计时到0时
{
P2=0X00; //重置东西南北的红绿灯状态
Green_dx=1; //东西绿灯亮
Red_nb=1; //南北红灯亮
dx_nb=!dx_nb; //交换通行方向
Yellow_status=0; //关闭黄灯闪烁
sec_dx=set_timedx; //重赋东西方向的起始值
sec_nb=set_timenb+5; //重赋南北方向的起始值
}

功能函数

紧急模式

全部方向红灯

用外部中断0改变全局标志位来进入函数:

1
2
3
4
5
void int0(void) interrupt 0 using 1	    //禁止通行
{
while(!key_EP)display();
flag_option=1;
}

处理逻辑:关交通灯定时器、灭显示、全部置红灯

1
2
3
4
TR0=0;						//关交通灯定时器
P2=0x00; //灭显示
Red_dx=1; //全部置红灯
Red_nb=1;

恢复逻辑:

1
2
3
4
5
6
TR0=1;						//开交通灯定时器
P2=0X00; //关全部信号灯
Green_dx=1; //东西绿灯亮
Red_nb=1; //南北红灯亮
sec_dx=set_timedx; //回到初值
sec_nb=sec_dx+5;

通过一个计数器实现进入与恢复:

1
2
3
conut_ER=1-conut_ER;
if(conut_ER==1){}
if(conut_ER==0){}

夜间模式

全部方向黄灯频闪

处理逻辑大致与上面一样,差异是控制频闪放在定时器中断1服务中。这里功能函数修改一个全局标志位,让定时器1启动输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
conut_night=1-conut_night;
if(conut_night==1)
{
TR0=0; //关交通灯定时器
P2=0x00; //关全部信号灯
flag_Night=1; //打开夜间模式
sec_dx=00; //四个方向的时间都为00
sec_nb=00;
}
if(conut_night==0)
{
TR0=1; //开交通灯定时器
P2=0x00; //关全部信号灯
Green_dx=1; //东西绿灯亮
Red_nb=1; //南北红灯亮
flag_Night=0; //打开夜间模式
sec_dx=set_timedx; //回到初值
sec_nb=sec_dx+5;
}
flag_option=0;
break;

定时器1中断服务,控制黄灯每 0.5 s 反转一次:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*用来处理夜间黄灯闪烁*/
void time1(void) interrupt 3 //定时中断子程序
{
TH1=0x4C; //重赋初值;11.0592m晶振50ms
TL1=0X00;
countt1++; //计数器数加1(用于计算时间)
if(countt1==10) //定时器中断次数=10时(即0.5秒)
{
if(flag_Night==1)
{
Yellow_nb=~Yellow_nb; //南北黄灯
Yellow_dx=~Yellow_nb; //东西黄灯
}
}
if(countt1==20) //定时器中断次数=20时(即1秒时)
{
countt1=0; //清零计数器
if(flag_Night==1)
{
Yellow_nb=~Yellow_nb; //南北黄灯
Yellow_dx=~Yellow_nb; //东西黄灯
}
}
}

演示截图

  1. 模拟电路电路图:

  2. 信号控制演示

  3. 紧急模式

  4. 夜间模式

Github 仓库

该项目托管在Github@skyedai910/traffic-lights-BC