1 设计名称
基于51单片机的十字路口交通灯系统(C语言、Proteus开发环境)
2 设计要求
1、 基于51系列单片机设计一个十字路口交通灯控制系统,用于模拟道路十字路口交通灯实际运行的场景。
2、 东西向、南北向依次进行周期通行,默认设置为:东西向直行(绿灯)时间30秒,左转(蓝灯)时间15秒;南北向直行(绿灯)时间20秒,左转(蓝灯)时间10秒。
3、 每次直行(绿灯)变左转(蓝灯)和左转(蓝灯)变停车(红灯)前,黄灯需先闪烁3秒,此3秒时长包含在上述直行和左转时间内。
4、 可通过控制台按键实现对十字路口进行交通管制的功能。
5、 可通过控制台按键实现对十字路口各方向的通行时间进行设置的功能。
3 功能说明
整个十字路口由东西向道路和南北向道路垂直交叉形成,我们采用“上北下南,左西右东”的惯例进行构图。每个方向都有一组4色的LED灯来控制交通的流向和节奏。流向也即方向,通过不同LED灯颜色进行表征:红灯停,绿灯行,黄灯等一等,这里左转我们用蓝灯表示;节奏通过倒计时时间进行控制,该时间的时基由单片机的定时器提供。
十字路口的倒计时时间分别通过四个2位的七段共阴极数码管显示。绿灯点亮道路上的数码管显示的数字是该道路所剩余的可通行时间,红灯点亮道路上的数码管显示的数字是该道路所剩余的禁行时间,小伙伴需要注意区分两者不同的物理含义。
系统上电后,默认运行在“正常模式”下。此时南北向绿灯点亮,允许直行;东西向红灯点亮,禁止直行和左转。具体的状态迁移顺序可参照下面第 6 部分内容。
4 系统架构
整个架构非常简单,主要由四部分组成。分别是MCU单片机控制模块、按键输入扫描模块、数码管显示模块和交通灯显示模块。按键、数码管和LED灯都是典型的人机交互的元器件,按键用于接收命令,数码管和LED灯用于反馈信号和状态。数据流向也是单向的,由左至右。
我们现在将十字路口交通灯的运行场景抽象归纳一下,然后用下面的图表进行复盘。
5 运行模式
对照日常生活,我们来盘点一下交通灯运行的模式:
“正常模式”:模拟十字路口正常运行的场景,交通灯按正常的顺序进行周期循环;
“夜间模式”:模拟十字路口夜间运行的场景,东西向和南北向的黄灯同时闪烁;
“紧急模式”:模拟十字路口进入紧急情况下的场景(救护车、消防车通过等),东西向和南北向的红灯全部点亮;
“强制东西向通行模式”:强制东西向通行,南北向停车等待。
“强制南北向通行模式”:强制南北向通行,东西向停车等待。
6 ”正常模式“下状态迁移之状态机
不同国家、不同城市、不同十字路口都有不同的状态设置。有的路口是先直行再左转,有的路口是先允许左转再允许直行,有的路口压根就没有专门的左转灯,有的路口有人行道指示灯,而有的没有。所谓千人千面,五官各不相同,但确有一个共性,就是都有五官。状态机的思路可以很好的解决这个问题。
7 ”正常模式“下状态迁移之矩阵表
1 - 表征对应颜色交通灯点亮;
0 - 表征对应颜色交通灯熄灭;
1/0 - 表征对应颜色交通灯闪烁;
蓝色灯 - 表征对应道路左转状态;
8 仿真电路图
因为案例更侧重功能的实现,对于硬件电路的设计要求不是太高,达到能用就行的标准就行,所以这里只简要说一下重点吧。
1 单片机P0一定要加上拉电阻。为什么?因为P0口内部为开集电极输出,无法输出高电平,只有加了上拉电阻,P0口才能输出高电平。
2 数码管的驱动一定要加限流电阻。虽然在Proteus中仿真不加问题也不大,但是实际产品肯定是不允许的,而且也无法工作。
3 LED灯驱动一定要加限流电阻。如果不加限流电阻,单片机会工作异常,因为单片机引脚的驱动电流的能力是有限制的,超出则正常功能无法保证,甚至烧坏单片机。
4 LED灯不建议直接并联使用。虽然LED灯具有正向单调的伏安特性曲线,但是它的温度系数却是负向的,不利于均流。
5 按键模块采用的是普通的点对点的结构,但设计中也可采用矩阵式按键,小伙伴可以自行发挥。
6 当前的设计,单片机引脚基本被全部分配用完,没有冗余。对于实物产品的开发这是不被允许的,因为过于捉襟见肘的单片机资源完全不利于产品后期的功能升级和延展。如果单片机引脚紧张,除可以采用矩阵式键盘节省引脚外,也可以采用数字锁存器或者译码器对引脚进行扩展使用。
9 器件清单
其他未尽事宜,诸如导线啊,芯片底座啊,电源插座啊,电源啊等等,都是基本功,这里暂就不表了。
10 C 语言核心源代码
软件架构采用的是时间触发机制+状态机的设计模式,这种模式的好处是MCU执行效率高,实时性好,另外一点很重要的就是灵活性,代码容易扩展。时间片调度周期为20ms,用于按键的扫描识别和LED数码管的动态刷新。交通灯运转的状态机是通过switch来完成的。大侠看到网络上很多的案例代码用到了外部中断来实现按键扫描,这是完全没必要的,主函数轮询扫描的方式完全就够用了,51单片机的外部中断资源毕竟有限且珍贵,何以要用中断呢?难道江郎才尽了?此外,设计中按键扫描模块实现了去抖的功能。
因版面受限,这里只能贴出核心状态机的源代码供各位看管参考学习了。其他功能相对简单,大家可以适当脑补,或者也可划至文章最后寻找解决方案。
10.1 变量定义
sbit Gb_SN_LEDH_En = P2^0; // 南北向高位LED数码管使能控制位
sbit Gb_SN_LEDL_En = P2^1; // 南北向低位LED数码管使能控制位
sbit Gb_EW_LEDH_En = P2^2; // 东西向高位LED数码管使能控制位
sbit Gb_EW_LEDL_En = P2^3; // 东西向低位LED数码管使能控制位
sbit Gb_Debug_LEDH_En = P2^4; // 调试用高位LED数码管使能控制位
sbit Gb_Debug_LEDL_En = P2^5; // 调试用低位LED数码管使能控制位
sbit Gb_EW_Walk_Green_En = P2^6; // 东西向人行道绿灯使能控制位
sbit Gb_SN_Walk_Green_En = P2^7; // 南北向人行道绿灯使能控制位
sbit Gb_EW_Yellow_En = P1^2; // 东西向黄灯使能控制位
sbit Gb_SN_Yellow_En = P1^6; // 南北向黄灯使能控制位
sbit Gb_EW_Red_En = P1^3; // 东西向黄灯使能控制位
sbit Gb_SN_Red_En = P1^7; // 南北向黄灯使能控制位
#define SET 0 // 设置/确认按钮
#define SET_ADD 1 // 设置+
#define SET_SUB 2 //设置-
#define SET_HOME 3 // 正常模式
#define SET_YELLOW 4 // 夜间模式
#define SET_RED 5 // 紧急模式
#define SET_EW 6 // 强制东西向通行模式
#define SET_SN 7 //强制南北向通行模式
uint_8 Gb_Traffic_RunMode; // 红绿灯运行模式 0-正常 1-夜间 2-紧急 3-东西强制 4-南北强制
uint_8 Gb_SET_Active_Flag; // 使能设置功能标志位
uint_8 Gb_SET_LEDSEG_Blink_Flag; // 控制台数码管闪烁时基标志位
uint_8 Gb_LED_SEG_Update_Flag; // LED数码管刷新调度标志位
uint_8 Gb_LED_YellowControl_Flag; // LED黄灯闪烁调度标志位
uint_8 Gb_LED_Task1s_Flag; // 交通灯任务调度标志位
uint_8 Gb_EW_YellowLight_Flag; // 东西向黄灯闪烁标志位
uint_8 Gb_SN_YellowLight_Flag; // 南北向黄灯闪烁标志位
uint_8 Gb_EW_LED_RealTime; // 东西向LED数码管显示时间
uint_8 Gb_SN_LED_RealTime; // 南北向LED数码管显示时间
uint_8 Gb_SET_LED_RealTime; // 控制台LED数码管显示时间,默认无显示
uint_8 Gb_System_Step_Index = 0; // 正常模式下红绿灯运行进度标志
uint_8 Cc_EW_DirectPass_Time = 10; // 东西向道路直行时间参数(不含黄灯闪烁时间),单位秒
uint_8 Cc_SN_DirectPass_Time = 15; // 南北向道路直行时间参数(不含黄灯闪烁时间),单位秒
uint_8 Cc_EW_LeftPass_Time = 5; // 东西向道路左转时间参数(不含黄灯闪烁时间),单位秒
uint_8 Cc_SN_LeftPass_Time = 5; // 南北向道路左转时间参数(不含黄灯闪烁时间),单位秒
uint_8 Cc_YellowLight_Time = 3; // 黄灯闪烁时间参数,单位秒
uint_8 code LED_SEG_Table[11]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00}; //共阴数码管0-9段选码表,及无显示状态
uint_8 code LED_Light_Table[4]={0X28,0X18,0X82,0X81}; //交通信号灯控制代码,对应P1引脚
10.2 交通灯控制函数
/*************************************************
函数名:十字路口交通灯控制函数
参 数:无
返回值:无
结 果:管理红绿蓝交通灯,管理人行道交通灯
************************************************/
void Traffic_Control(void)
{
Gb_SN_LED_RealTime--; // 南北向时间倒计时
Gb_EW_LED_RealTime--; // 东西向时间倒计时
switch(Gb_System_Step_Index)
{
case 0: // 状态0 - 南北向绿灯,东西向红灯
if(Gb_SN_LED_RealTime <= Cc_YellowLight_Time)
{
P1 = P1&0x0F;
Gb_SN_YellowLight_Flag = 1;
Gb_System_Step_Index++;
}
break;
case 1: // 状态1 - 南北向黄灯,东西向红灯
if(Gb_SN_LED_RealTime <= 0)
{
P1 = LED_Light_Table[1];
Gb_SN_LED_RealTime = Cc_SN_LeftPass_Time + Cc_YellowLight_Time;
Gb_SN_YellowLight_Flag = 0;
Gb_System_Step_Index++;
}
break;
case 2: // 状态2 - 南北向左转灯亮,东西向红灯
if(Gb_SN_LED_RealTime <= Cc_YellowLight_Time)
{
P1 = P1&0x0F;
Gb_SN_YellowLight_Flag = 1;
Gb_System_Step_Index++;
}
break;
case 3: // 状态3 - 南北向黄灯,东西向红灯
if(Gb_SN_LED_RealTime <= 0)
{
P1 = LED_Light_Table[2];
Gb_SN_LED_RealTime = Cc_EW_DirectPass_Time + Cc_EW_LeftPass_Time + 2*Cc_YellowLight_Time;
Gb_EW_LED_RealTime = Cc_EW_DirectPass_Time + Cc_YellowLight_Time;
Gb_SN_Walk_Green_En = 0;
Gb_EW_Walk_Green_En = 1;
Gb_SN_YellowLight_Flag = 0;
Gb_System_Step_Index++;
}
break;
case 4: // 状态4 - 南北向红灯,东西向绿灯
if(Gb_EW_LED_RealTime <= Cc_YellowLight_Time)
{
P1 = P1&0xF0;
Gb_EW_YellowLight_Flag = 1;
Gb_System_Step_Index++;
}
break;
case 5: // 状态5 - 南北向红灯,东西向黄灯
if(Gb_EW_LED_RealTime <= 0)
{
P1 = LED_Light_Table[3];
Gb_EW_LED_RealTime = Cc_EW_LeftPass_Time + Cc_YellowLight_Time;
Gb_EW_YellowLight_Flag = 0;
Gb_System_Step_Index++;
}
break;
case 6: // 状态6 - 南北向红灯,东西向左转灯亮
if(Gb_EW_LED_RealTime <= Cc_YellowLight_Time)
{
P1 = P1&0xF0;
Gb_EW_YellowLight_Flag = 1;
Gb_System_Step_Index++;
}
break;
case 7: // 状态7 - 南北向红灯,东西向黄灯
if(Gb_SN_LED_RealTime <= 0)
{
Cc_EW_DirectPass_Time = Cc_EW_DirectPass_Time2;
Cc_SN_DirectPass_Time = Cc_SN_DirectPass_Time2;
Cc_EW_LeftPass_Time = Cc_EW_LeftPass_Time2;
Cc_SN_LeftPass_Time = Cc_SN_LeftPass_Time2;
Cc_YellowLight_Time = Cc_YellowLight_Time2;
Gb_SN_LED_RealTime = Cc_SN_DirectPass_Time + Cc_YellowLight_Time;
Gb_EW_LED_RealTime = Cc_SN_DirectPass_Time + Cc_SN_LeftPass_Time + 2*Cc_YellowLight_Time;
P1 = LED_Light_Table[0];
Gb_SN_Walk_Green_En = 1;
Gb_EW_Walk_Green_En = 0;
Gb_EW_YellowLight_Flag = 0;
Gb_System_Step_Index = 0;
}
break;
default:
break;
}
}
11 操作说明
从按键用途的角度进行划分,上图中的8个按键可以被归为两类。一类是用来设置时间参数的,另一类是用来切换交通灯系统运行模式的,我们就根据这2种大的分类分别进行详细的解释和说明。
11.1 “参数设置”按键操作说明
“参数设置”类的按键有三个,由上至下分别为“设置/确认“按钮、”加+“按钮和”减-“按钮。其主要功能是用来对十字路口东西向和南北向在各个阶段(东西向直行、东西向左转、南北向直行、南北向左转和黄灯等待)的通行时间参数进行设置和更新。
”设置/确认“按钮:共有三种功能:1 使系统进入时间参数设置状态;2 对设置参数进行顺序切换;3 完成参数设置并返回退出;
”加+“按钮:单击一下,对应时间参数加1。
” 减-“按钮:单击一下, 对应时间参数减1。
11.2 “模式切换”按键操作说明
“模式切换”类按键共有五个,由上至下分别为“正常模式“按钮、”夜间模式“按钮、”紧急模式“按钮、”强制东西向通行模式“按钮和”强制南北向通行模式“按钮。此处操作非常简单,不再赘述。交通灯在个模式下运行时对应的实际场景请参照上文说明。
以上为全部分享内容,如有任何技术疑问,可以在文章下方留言交流或关注下方公众号讨论。
- END -
如果你觉得我的分享对你有那么一点点启发和帮助,请帮忙点赞!
更多案例分享,请关注公众号【电控夜雨十年灯】,一个专注于分享硬件电路设计、单片机开发、C语言设计和Proteus案例的旮旯,保证干货满满!
本文为作者原创,未经允许,不得转载,谢谢支持!