下面我将为你提供一个完整、详细、可执行的交通灯课程设计方案,包括设计要求、方案论证、硬件设计、软件设计(含C51代码)、系统调试和扩展思考。
基于51单片机的十字路口交通灯控制系统
设计要求
-
基本功能:

- 模拟一个十字路口的交通灯控制。
- 东西方向和南北方向交替放行。
- 每个方向的灯有红、黄、绿三种状态。
- 东西方向绿灯亮,南北方向红灯亮。
- 东西方向黄灯亮,南北方向红灯亮。
- 南北方向绿灯亮,东西方向红灯亮。
- 南北方向黄灯亮,东西方向红灯亮。
- 如此循环。
-
时间要求:
- 东西绿灯亮 30 秒。
- 东西黄灯亮 3 秒。
- 南北绿灯亮 30 秒。
- 南北黄灯亮 3 秒。
- 总周期为 66 秒。
-
显示要求:
- 使用LED灯模拟红、黄、绿灯。
- 使用数码管或LCD显示屏实时显示当前方向的剩余时间(倒计时)。
-
扩展功能(可选,用于拿高分):
- 紧急模式: 当按下某个按键(如K1)时,所有方向都变为红灯,用于紧急情况(如救护车通过)。
- 夜间模式: 当按下另一个按键(如K2)时,所有方向黄灯闪烁,表示提醒车辆慢行。
- 车流量检测: 模拟通过红外对管检测车流量,根据车流量动态调整绿灯时间(东西方向车多,则延长其绿灯时间)。
方案论证
-
核心控制器选型:

- 方案A:51系列单片机(如AT89C51/52或STC89C52)
- 优点: 经典入门级单片机,资料丰富,价格低廉,I/O口足够,定时器/计数器资源满足需求,非常适合课程设计。
- 缺点: 性能相对较弱,处理复杂逻辑能力有限。
- 方案B:STM32系列单片机
- 优点: 性能强大,资源丰富,适合更复杂的控制和算法。
- 缺点: 学习曲线较陡,对于本项目来说“杀鸡用牛刀”,成本也稍高。
- 本课程设计选用方案A,STC89C52单片机,它性价比最高,足以完美实现所有要求。
- 方案A:51系列单片机(如AT89C51/52或STC89C52)
-
显示方案选型:
- 方案A:LED数码管
- 优点: 亮度高,视角大,驱动简单,成本低。
- 缺点: 显示内容有限,只能显示数字。
- 方案B:LCD1602液晶显示屏
- 优点: 显示信息丰富,可以显示文字和数字,界面友好。
- 缺点: 驱动相对复杂,需要占用更多I/O口(通常需要用到数据总线和控制线)。
- 为简化设计,同时保证清晰显示,本设计选用方案A,共阴极或共阳极数码管,倒计时显示可以共用一组数码管,通过切换显示内容来节省I/O口。
- 方案A:LED数码管
-
时间方案选型:
- 方案A:软件延时
- 优点: 编程简单,无需额外硬件。
- 缺点: CPU被占用,无法执行其他任务(如按键检测),精度低。
- 方案B:单片机定时器中断
- 优点: 精度高,不占用CPU主循环,CPU可以在中断间隙处理其他任务(如刷新显示、检测按键),这是专业和高效的实现方式。
- 本设计采用方案B,使用STC89C52的定时器0(Timer0)来产生精确的1秒基准时间。
- 方案A:软件延时
硬件设计
-
系统硬件框图
-
主要元器件清单

- 主控芯片:STC89C52RC (或AT89C52) 1片
- 晶振:12MHz 1个
- 电容:30pF (晶振匹配) 2个,10uF (电源滤波) 1个,22pF (按键去抖) 若干
- 电阻:1kΩ (LED限流) 12个,10kΩ (上拉电阻) 1个,200Ω (数码管限流/位选) 4个
- 数码管:4位一体共阴极数码管 1个
- LED灯:红、黄、绿各4个 (东西南北各一组)
- 按键:轻触开关 2个 (用于紧急模式和夜间模式)
- 电源:5V直流电源
- 面包板或洞洞板、杜邦线若干
-
电路连接说明
- 交通灯连接:
- 东西绿灯 -> P1.0
- 东西黄灯 -> P1.1
- 东西红灯 -> P1.2
- 南北绿灯 -> P1.3
- 南北黄灯 -> P1.4
- 南北红灯 -> P1.5
- (注意:每个LED串联一个1kΩ电阻到GND)
- 数码管连接(以共阴极为例):
- 段选 a, b, c, d, e, f, g, dp -> P0口 (P0.0-P0.7)。P0口需要外接10kΩ上拉电阻。
- 位选 (从左到右) -> P2.0, P2.1, P2.2, P2.3,每个位选引脚串联一个200Ω电阻。
- 按键连接:
- 紧急模式按键 K1 -> P3.2 (外部中断0)
- 夜间模式按键 K2 -> P3.3 (外部中断1)
- 按键另一端接地,引脚通过10kΩ电阻上拉到VCC。
- 晶振电路: 12MHz晶振接XTAL1和XTAL2,两端分别接30pF电容到地。
- 复位电路: RST引脚接一个10uF电容到VCC,一个10kΩ电阻到GND(上电复位)。
- 交通灯连接:
软件设计
-
软件流程图
-
核心思想:状态机
- 将交通灯的运行过程看作4个状态:
- S0: 东西绿灯,南北红灯 (30s)
- S1: 东西黄灯,南北红灯 (3s)
- S2: 南北绿灯,东西红灯 (30s)
- S3: 南北黄灯,东西红灯 (3s)
- 程序通过一个状态变量(如
unsigned char state;)来记录当前处于哪个状态,定时器每1秒中断一次,更新倒计时,并在倒计时为0时,切换到下一个状态。
- 将交通灯的运行过程看作4个状态:
-
C51代码实现
#include <reg52.h>
#include <intrins.h>
// 定义交通灯引脚
sbit EW_GREEN = P1^0; // 东西绿灯
sbit EW_YELLOW = P1^1; // 东西黄灯
sbit EW_RED = P1^2; // 东西红灯
sbit SN_GREEN = P1^3; // 南北绿灯
sbit SN_YELLOW = P1^4; // 南北黄灯
sbit SN_RED = P1^5; // 南北红灯
// 定义按键引脚
sbit KEY_EMERGENCY = P3^2; // 紧急模式按键
sbit KEY_NIGHT = P3^3; // 夜间模式按键
// 定义数码管段选码 (共阴极, 0-9)
unsigned char code SegCode[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};
// 定义数码管位选码 (从左到右)
unsigned char code BitCode[] = {0xFE, 0xFD, 0xFB, 0xF7};
// 全局变量
unsigned char state = 0; // 状态机变量: 0,1,2,3
unsigned int counter = 30; // 倒计时计数器
unsigned char flag_1s = 0; // 1秒到标志位
unsigned char display_buf[4]; // 数码管显示缓冲区
// 函数声明
void Timer0_Init();
void Key_Scan();
void State_Set();
void Display();
void Emergency_Mode();
void Night_Mode();
void main() {
// 初始化
Timer0_Init();
EA = 1; // 开启总中断
while (1) {
// 主循环中处理按键和显示
Key_Scan();
// 根据状态设置交通灯
State_Set();
// 更新显示缓冲区
display_buf[0] = counter / 10;
display_buf[1] = counter % 10;
display_buf[2] = 0x10; // 显示横杆 '-'
display_buf[3] = 0x10; // 显示横杆 '-'
// 显示
Display();
}
}
// 定时器0中断服务函数
void Timer0_ISR() interrupt 1 {
static unsigned int count_50ms = 0;
TH0 = (65536 - 50000) / 256; // 重新装载初值 (50ms)
TL0 = (65536 - 50000) % 256;
count_50ms++;
if (count_50ms >= 20) { // 50ms * 20 = 1000ms = 1s
count_50ms = 0;
flag_1s = 1; // 置1秒标志位
}
}
// 状态设置函数
void State_Set() {
if (flag_1s) {
flag_1s = 0; // 清除标志位
if (counter > 0) {
counter--;
} else {
// 时间到,切换到下一个状态
state++;
if (state > 3) {
state = 0;
}
// 根据新状态设置倒计时
if (state == 0 || state == 2) {
counter = 30; // 绿灯30秒
} else {
counter = 3; // 黄灯3秒
}
}
}
// 根据当前状态设置交通灯
switch (state) {
case 0: // 东西绿灯,南北红灯
EW_GREEN = 1; EW_YELLOW = 0; EW_RED = 0;
SN_GREEN = 0; SN_YELLOW = 0; SN_RED = 1;
break;
case 1: // 东西黄灯,南北红灯
EW_GREEN = 0; EW_YELLOW = 1; EW_RED = 0;
SN_GREEN = 0; SN_YELLOW = 0; SN_RED = 1;
break;
case 2: // 南北绿灯,东西红灯
EW_GREEN = 0; EW_YELLOW = 0; EW_RED = 1;
SN_GREEN = 1; SN_YELLOW = 0; SN_RED = 0;
break;
case 3: // 南北黄灯,东西红灯
EW_GREEN = 0; EW_YELLOW = 0; EW_RED = 1;
SN_GREEN = 0; SN_YELLOW = 1; SN_RED = 0;
break;
}
}
// 数码管动态扫描显示函数
void Display() {
static unsigned char index = 0;
P2 = 0xFF; // 消隐,关闭所有位选
P0 = SegCode[display_buf[index]]; // 送段选码
P2 = BitCode[index]; // 送位选码
index++;
if (index >= 4) {
index = 0;
}
}
// 按键扫描函数 (此处简化,实际应用中可用外部中断)
void Key_Scan() {
// 紧急模式
if (KEY_EMERGENCY == 0) {
Delay_ms(10); // 消抖
if (KEY_EMERGENCY == 0) {
Emergency_Mode();
while (!KEY_EMERGENCY); // 等待按键释放
}
}
// 夜间模式
if (KEY_NIGHT == 0) {
Delay_ms(10);
if (KEY_NIGHT == 0) {
Night_Mode();
while (!KEY_NIGHT);
}
}
}
// 紧急模式函数
void Emergency_Mode() {
// 所有灯亮
EW_GREEN = 1; EW_YELLOW = 1; EW_RED = 1;
SN_GREEN = 1; SN_YELLOW = 1; SN_RED = 1;
}
// 夜间模式函数
void Night_Mode() {
// 所有黄灯闪烁
while(1) {
EW_YELLOW = !EW_YELLOW;
SN_YELLOW = !SN_YELLOW;
Delay_ms(500);
// 可以加入按键检测来退出此模式
}
}
// 简单延时函数 (ms级,不精确)
void Delay_ms(unsigned int ms) {
unsigned int i, j;
for (i = ms; i > 0; i--)
for (j = 110; j > 0; j--);
}
系统调试与问题解决
-
硬件调试:
- 电源检查: 用万用表测量VCC和GND之间是否为5V,确保电路没有短路。
- LED检查: 逐个检查LED的连接和极性是否正确,上电后是否能被点亮。
- 数码管检查: 检查段选和位选的连接,特别是P0口的上拉电阻,可以先写一个简单的程序,让数码管显示"8888",检查每个段和位是否正常。
- 按键检查: 按下按键时,用万用表测量对应引脚是否被拉低。
-
软件调试:
- 下载与运行: 将编译好的
.hex文件烧录到单片机中,观察交通灯是否按预期规律变化。 - 逻辑错误: 如果灯的顺序不对,检查
State_Set()函数中的switch-case逻辑。 - 时间不准: 如果倒计时不准,检查定时器的初值计算是否正确,晶振频率是否与程序中设定的12MHz一致。
- 显示问题: 如果数码管显示乱码或闪烁,检查动态扫描的频率是否太慢(
Delay_ms函数的参数)或太快,以及段选、位选码是否正确。
- 下载与运行: 将编译好的
扩展思考与优化
-
车流量检测:
- 硬件: 在每个方向的红绿灯前安装一对红外对管(发射和接收)。
- 软件: 将红外接收管的输出接到单片机的某个I/O口(如P3.4),平时,有车挡住时,I/O口为高电平;无车时为低电平,在程序中,可以统计一个绿灯周期内被触发的次数,从而判断车流量,动态调整下一个绿灯周期的时长。
-
实现人行横道灯:
在东西和南北方向各增加一组红绿灯(人行道),连接到单片机的剩余I/O口,在主干道绿灯亮时,人行道红灯亮;主干道红灯亮时,人行道绿灯亮,这需要更精细的状态控制。
-
使用LCD1602替代数码管:
可以更直观地显示"东西方向:30秒"这样的文字信息,提升用户体验。
-
代码优化:
- 使用结构体来封装交通灯的状态,使代码更清晰。
- 将所有延时函数替换为基于定时器的精确延时,提高系统效率。
- 将按键检测改为外部中断方式,响应更迅速,不占用主循环时间。
这个方案为你提供了一个从理论到实践的完整路径,你可以根据自己的实际情况(如手头的元件、老师的具体要求)进行调整,祝你课程设计顺利成功!
版权声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。