1. 项目概述
这个基于51单片机的交通信号灯控制系统是我在嵌入式开发学习过程中完成的一个典型实训项目。作为一名电子工程师,我经常需要设计类似的实时控制系统,而交通灯恰好涵盖了定时控制、状态切换、中断处理等嵌入式开发的核心知识点。
系统采用STC89C52单片机作为主控芯片,通过两组红绿黄LED模拟十字路口的双向交通信号,配合两位共阳数码管实现倒计时显示。当检测到红灯期间有车辆闯行时(通过3-5V模拟信号触发),系统会启动蜂鸣器报警。整个设计在Proteus 8.9环境下完成仿真验证,源代码使用Keil uVision5开发。
提示:选择STC89C52是因为其价格低廉且完全兼容传统8051架构,特别适合教学和入门级项目开发。实际工程中可根据需求升级到STC12/15系列增强型51单片机。
2. 硬件设计详解
2.1 核心元件选型
主控芯片:STC89C52RC
- 8KB Flash ROM
- 512B RAM
- 4个8位I/O口
- 3个定时器
- 最高35MHz工作频率
显示器件:
- 红绿黄LED各两支(共6个)
- 两位0.56寸共阳数码管(型号:5461AS)
- 74HC245总线驱动器(增强数码管驱动能力)
检测与报警:
- 10KΩ电位器(模拟车辆检测信号)
- 5V有源蜂鸣器(报警音源)
2.2 电路原理图设计
完整电路包含以下几个关键部分:
-
单片机最小系统:
- 11.0592MHz晶振(精确计时关键)
- 22pF起振电容×2
- 10KΩ上拉电阻
- 10μF电解电容+0.1μF瓷片电容组成的电源滤波电路
-
信号灯驱动电路:
c复制// P1口控制东西方向信号灯
sbit EW_RED = P1^0; // 东西红灯
sbit EW_GREEN = P1^1; // 东西绿灯
sbit EW_YELLOW = P1^2;// 东西黄灯
// P2口控制南北方向信号灯
sbit NS_RED = P2^0; // 南北红灯
sbit NS_GREEN = P2^1; // 南北绿灯
sbit NS_YELLOW = P2^2;// 南北黄灯
-
数码管显示电路:
- 采用动态扫描方式驱动
- P0口输出段选信号
- P3.0/P3.1控制位选
- 74HC245提高驱动电流
-
车辆检测电路:
- 电位器中间抽头接P3.2(INT0)
- 电压>3V时触发外部中断
2.3 Proteus仿真要点
在Proteus中搭建仿真模型时需注意:
- 数码管要设置为共阳类型
- 添加"DEBUG"虚拟终端监视程序运行
- 配置晶振频率为11.0592MHz
- 电位器属性中设置阻值范围为0-10KΩ
注意:Proteus中LED默认导通电压为1.8V,实际硬件中需根据LED规格添加限流电阻(通常220Ω-1KΩ)。
3. 软件设计实现
3.1 系统状态机设计
交通灯采用有限状态机(FSM)模型,定义以下状态:
c复制typedef enum {
STATE_EW_GREEN_NS_RED, // 东西绿灯,南北红灯
STATE_EW_YELLOW_NS_RED, // 东西黄灯,南北红灯
STATE_EW_RED_NS_GREEN, // 东西红灯,南北绿灯
STATE_EW_RED_NS_YELLOW // 东西红灯,南北黄灯
} TrafficState;
状态转换时序:
- 东西绿灯25秒 → 东西黄灯5秒 → 东西红灯30秒(南北绿灯25秒+黄灯5秒)
- 南北方向同理
3.2 定时器配置
使用Timer0产生1秒时基:
c复制void Timer0_Init() {
TMOD &= 0xF0; // 清除T0控制位
TMOD |= 0x01; // 设置T0为模式1(16位定时器)
TH0 = 0x3C; // 初始值15536(11.0592MHz下50ms)
TL0 = 0xB0;
ET0 = 1; // 允许T0中断
TR0 = 1; // 启动T0
}
void Timer0_ISR() interrupt 1 {
static unsigned int count = 0;
TH0 = 0x3C; // 重装初值
TL0 = 0xB0;
if(++count >= 20) { // 20×50ms=1秒
count = 0;
SystemTick(); // 系统时钟更新
}
}
3.3 数码管显示驱动
采用动态扫描方式显示倒计时:
c复制unsigned char code SEGMENT[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};
void Display_Update() {
static unsigned char pos = 0;
P0 = 0xFF; // 消隐
if(pos == 0) {
P3_0 = 1; P3_1 = 0; // 选中十位
P0 = SEGMENT[countdown/10];
} else {
P3_0 = 0; P3_1 = 1; // 选中个位
P0 = SEGMENT[countdown%10];
}
pos = !pos;
}
3.4 车辆检测与报警
通过外部中断检测闯红灯:
c复制void INT0_Init() {
IT0 = 1; // 下降沿触发
EX0 = 1; // 允许INT0中断
}
void INT0_ISR() interrupt 0 {
if((currentState == STATE_EW_RED_NS_GREEN && P3_2 == 0) ||
(currentState == STATE_EW_GREEN_NS_RED && P3_3 == 0)) {
Buzzer_Alarm(3); // 鸣响3次
}
}
4. 核心代码实现
4.1 主程序框架
c复制void main() {
System_Init(); // 初始化硬件
while(1) {
Traffic_Update(); // 交通灯状态更新
Display_Update(); // 数码管显示刷新
Watchdog_Feed(); // 喂看门狗(实际硬件需要)
}
}
4.2 状态切换逻辑
c复制void Traffic_Update() {
static unsigned char tick = 0;
if(++tick >= 10) { // 每10个系统周期(10秒)执行一次状态检查
tick = 0;
switch(currentState) {
case STATE_EW_GREEN_NS_RED:
if(--countdown == 0) {
currentState = STATE_EW_YELLOW_NS_RED;
countdown = YELLOW_TIME;
}
break;
// 其他状态转换类似...
}
}
}
4.3 报警功能实现
c复制void Buzzer_Alarm(unsigned char times) {
unsigned char i;
for(i=0; i<times; i++) {
Buzzer = 1;
Delay_ms(500);
Buzzer = 0;
Delay_ms(500);
}
}
5. 调试与优化
5.1 常见问题排查
-
数码管显示乱码:
- 检查共阳/共阴配置是否正确
- 测量段选线电压是否正常
- 确认动态扫描频率>50Hz(避免闪烁)
-
定时不准:
- 核对晶振频率设置
- 检查定时器初值计算
- 中断服务程序中是否及时重装初值
-
车辆检测不灵敏:
- 调整比较器参考电压
- 添加软件去抖(典型值10-20ms)
- 检查中断触发方式配置
5.2 性能优化建议
- 采用状态机+定时器中断的架构,避免使用delay()函数
- 数码管扫描放在定时器中断中,保证刷新率稳定
- 添加看门狗定时器防止程序跑飞
- 关键变量使用volatile修饰
- 空闲时让CPU进入IDLE模式降低功耗
5.3 功能扩展方向
- 增加手动控制模式(通过按键切换)
- 实现自适应配时(根据车流量调整)
- 添加无线通信模块远程监控
- 使用LCD屏替代数码管显示更多信息
- 增加环境光检测自动调节LED亮度
6. 项目总结
在实际开发过程中,有几个关键点需要特别注意:
-
定时精度问题:最初使用12MHz晶振导致秒计时误差较大,改为11.0592MHz后显著改善。这是因为11.0592MHz可以精确分频得到标准波特率和定时周期。
-
中断冲突处理:当定时器中断和外部中断同时发生时,可能出现显示异常。通过优化中断优先级(设置IP寄存器)和缩短中断服务程序解决。
-
抗干扰设计:仿真环境运行正常的产品,在实际硬件中可能出现复位现象。最终通过添加电源滤波电容、信号上拉电阻和软件看门狗增强了稳定性。
这个项目虽然基础,但完整涵盖了嵌入式系统开发的各个环节。建议初学者可以在此基础上逐步增加复杂度,比如引入RTOS进行任务调度,或者添加CAN总线实现多路口协同控制。