基于8051与8255A的智能交通灯控制器设计:从固定配时到车流量检测与闯红灯报警
简介:这份毕业设计论文面向电子信息、自动化等专业学生及单片机初学者,围绕基于MSC-51系列ATSC51单片机与8255A可编程并行I/O接口芯片的交通灯控制系统展开,解决传统固定配时信号灯难以适应动态车流的问题。压缩包内为1个doc文档,约257KB,完整收录论文正文,涵盖交通灯发展沿革、8051内部结构与引脚说明、硬件原理图与接口电路分析、初始化与状态切换等源程序代码,以及系统测试与结论。论文实现了红绿灯循环点亮、倒计时剩5秒黄灯闪烁警示、闯红灯报警和绿灯时段车流量检测显示等功能,软硬件结合、层次清晰。目前已有1011人学习,可作为毕业设计选题范本、课程设计参考或单片机控制类项目的入门实践材料,帮助读者理解从需求分析、方案设计到编程调试的完整流程。
1. 从一次答辩被问懵说起:这套交通灯方案到底解决什么
答辩现场最容易被问倒的不是代码写没写出来,而是“你这个东西和市面上几十块的定时器有什么区别”。很多同学做单片机交通灯,最后交上去的就是一个固定 60 秒红灯、80 秒绿灯的循环,导师一句“车流量变化了怎么办”就答不上来。这套基于 MCS-51 系列单片机(AT89S51/8031 一类)加 8255A 可编程并行接口芯片的交通灯控制器,核心价值恰恰在于它不是一个死循环定时器:红绿灯时长可通过 P1 口现场设定,绿灯期间能检测车流量并数码管显示,倒计时剩 5 秒黄灯闪烁,车辆闯红灯还能触发蜂鸣报警。它适合正在做单片机课程设计、毕业设计的学生,也适合需要一份可交付的十字路口信号控制原型的从业者,把“能跑”和“能讲清楚为什么这么设计”两件事一起解决。
2. 需求与方案论证:十字路口两干道四相位怎么定
2.1 从路口实际通行矛盾倒推功能清单
先把场景钉死:东西、南北两条干道交叉,各有一组红黄绿三色灯,行人随对应方向绿灯通行。原料里给了一个很关键的假设——东西道车流量大于南北道,所以东西绿灯 80 秒、南北绿灯 60 秒,中间各插 5 秒黄灯作为公共停车缓冲。这个不对称配时就是整个方案的需求起点,而不是随便拍脑袋定 60/60。
由此倒推出的功能清单有四条硬需求:第一,红、绿、黄三色按固定相位顺序循环,黄灯在切换前闪烁警示;第二,红绿灯初始时长可由键盘现场修改,不能烧死在程序里;第三,绿灯时段要能统计通过车辆数并用双位数码管显示,为后续动态配时留接口;第四,红灯期间若检测到车辆越线(闯红灯),蜂鸣器报警约 3 秒后恢复。这四条对应了原料摘要里“实用性强、操作简单、扩展功能强”的定位,也是答辩时能展开讲的技术点。
2.2 为什么选 8051 + 8255A 而不是纯单片机口
这是方案论证里必须讲清楚的一步,也是很多设计文档最薄弱的地方。8051 本身有 P0、P1、P2、P3 四组 8 位 I/O,但 P0 要分时复用做地址/数据总线,P2 出高 8 位地址,P3 又承担中断、串口、定时器外部输入等第二功能,真正能干净借用的只剩 P1。而本系统要同时驱动:两路红黄绿共 6 个灯、两组双位数码管(段选加位选)、开关键盘输入、车流量检测输入、蜂鸣器,端口数量明显不够。
扩展有两条路:借用外部 RAM 地址空间做 I/O,或者用专用接口芯片。前者省成本但地址译码和时序要自己兜,调试时容易和存储器访问打架;后者用 8255A,A 口出信号灯、C 口出数码管段码、B 口做位选和辅助输出,控制字一次写清楚,端口职责分明。我一般会跟学生讲:课程设计阶段,可读性和可调试性比省一片芯片重要,8255A 的代价是多几根连线和一次初始化,换来的是“哪个口干什么”一眼能说清。原料里 74LS373 用来锁存 P0 送出的低 8 位地址,MAX692 看门狗负责程序跑飞复位,7805 提供 5V 稳压,这套器件组合是那个年代教学板的经典配置,今天用仿真或打样都还能复现。
2.3 相位与时序表:把配时写成可查的表
方案论证的落点是时序表。按原料表 2 的逻辑,一个完整循环是:东西红灯 60 秒(南北绿灯)→ 黄灯闪 5 秒 → 东西绿灯 80 秒(南北红灯)→ 黄灯闪 5 秒 → 回到起点。注意黄灯是两干道公共停车时间,两个方向此时都该停,所以黄灯阶段信号灯输出要同时点亮两侧黄灯。
| 阶段 | 东西方向 | 南北方向 | 时长 | 说明 |
|---|---|---|---|---|
| 1 | 红灯 | 绿灯 | 60s | 南北车流通行,东西行人可过 |
| 2 | 黄灯闪 | 黄灯闪 | 5s | 公共缓冲,全向停车 |
| 3 | 绿灯 | 红灯 | 80s | 东西车流大,配时更长 |
| 4 | 黄灯闪 | 黄灯闪 | 5s | 公共缓冲,回到阶段 1 |
这张表就是软件状态机的骨架,后面所有代码都是围绕它展开。把表先定下来,硬件接线和程序分支才有依据,答辩时被问“为什么黄灯 5 秒”也能答:它是切换缓冲,取值要大于车辆制动反应时间,教学场景取 5 秒便于观察。
3. 硬件总体设计:从总框图到 8255A 端口分配
3.1 系统总框图与信号流向
硬件按“单片机核心 + 并行扩展 + 驱动输出 + 人机输入”四块组织。8051 通过 P0 口分时向 8255A 送数据和控制字,74LS373 锁存低 8 位地址完成片选与端口选择;8255A 的 PA 口经驱动后接 6 只交通灯,PC 口输出七段段码、PB 口输出位选,驱动两组共阴数码管;P1 口接开关键盘用于设定红绿初始时间;P2.0 接车流量检测开关,P3.0 控制工作/设置状态并驱动蜂鸣器报警;MAX692 看门狗监控程序运行,7805 给整板供 5V。
信号流向一句话概括:输入走 P1/P2/P3,输出全压到 8255A 三个口上,单片机只负责算时间和写控制字。这样划分的好处是输出侧完全由 8255A 承担,单片机端口压力小,程序里对灯的操作用一条MOVX写外部地址就能完成,逻辑干净。
3.2 8255A 控制字与端口地址译码
8255A 用方式 0(基本输入/输出)就够了,因为信号灯和数码管都是单向输出,键盘和检测开关是单向输入,不需要选通和中断握手。控制字按原料表 1 的格式:D7=1 标志有效,D6D5=00 选 A 口方式 0,D4=0 令 A 口输出,D3=0 令 C 口高 4 位输出,D2=0 选 B 口方式 0,D1=0 令 B 口输出,D0=0 令 C 口低 4 位输出,合起来就是0x80。
端口地址用 P0.7 做片选,P0.1、P0.0 选口:P0.7=0 时片选有效,00H为 PA、01H为 PB、02H为 PC、03H为控制口。原料里写的是 P0.7=1 时片选有效、地址写成 0003H 一类,实际接线以片选逻辑为准,写代码时把基址和偏移对齐即可。下面这段初始化是整板能出灯的前提:
// 8255A 初始化:方式0,PA/PB/PC 全部输出 // 端口基址按片选译码确定,此处以 0x0000 为例 #define P8255_A 0x0000 #define P8255_B 0x0001 #define P8255_C 0x0002 #define P8255_CTL 0x0003 void init_8255(void) { unsigned char ctrl = 0x80; // D7=1, 方式0, 三口全输出 XBYTE[P8255_CTL] = ctrl; // 写控制寄存器 XBYTE[P8255_A] = 0xFF; // 上电先全灭(共阳低电平点亮) XBYTE[P8255_B] = 0xFF; XBYTE[P8255_C] = 0xFF; }XBYTE是 Keil C51 里访问外部数据空间的宏,等价于原料汇编里的MOVX @DPTR,A。控制字0x80的含义要能背下来:最高位是方式标志,低六位逐位对应三个口的高低半字节方向。初始化后先写全 1 灭灯,避免上电瞬间乱亮。
3.3 信号灯与数码管的驱动细节
信号灯接 PA 口,发光二极管共阳接法,端口输出低电平对应灯亮。按相位表,阶段 1 东西红、南北绿,PA 输出要同时拉低对应位;阶段 3 反过来。原料里给的0xDD、0xEB、0xD4、0xE2就是这些组合的十六进制编码,实际接线顺序不同编码要重算,不能照抄。
数码管用共阴接法,PC 口出段码、PB 口出位选。段码表按dp g f e d c b a排列,0 到 9 对应3FH、06H、5BH、4FH、66H、6DH、7DH、07H、7FH、6FH。双位数码管要动态扫描:先送十位段码、开十位位选、延时 1 到 2 毫秒,再送个位段码、开个位位选、延时,循环刷新。扫描频率太低会闪,太高亮度不够,1 毫秒左右是教学板上比较稳的取值。
提示:共阳灯和共阴数码管的电平逻辑相反,接线前先把“低电平点亮”还是“高电平点亮”确认一遍,这是后面调试阶段最常见的返工点。
4. 软件设计:定时、状态机与车流量检测怎么落地
4.1 1 秒基准:定时器 50ms 加软件计数
8051 在 12MHz 晶振下机器周期 1 微秒,方式 1 的 16 位定时器最大定时 65.536 毫秒,直接定 1 秒做不到。原料给的思路是定时器 T0 定 50 毫秒,主程序里设一个初值 20 的软件计数器,中断里减到 0 就是 1 秒。初值计算:TC = 65536 - 50000 = 15536 = 0x3CB0,所以TH0=0x3C、TL0=0xB0。
// T0 方式1,50ms 中断一次,20 次凑 1 秒 volatile unsigned char tick_50ms = 20; volatile unsigned char flag_1s = 0; void timer0_isr(void) interrupt 1 { TH0 = 0x3C; // 重装 50ms 初值 TL0 = 0xB0; if (--tick_50ms == 0) { tick_50ms = 20; flag_1s = 1; // 置 1 秒标志,主循环消费 } display_scan(); // 借中断顺带刷新数码管 }这里有个设计取舍:数码管扫描放在定时中断里,保证刷新节奏稳定,主循环只处理秒级逻辑。中断服务要短,所以display_scan()里只做段码送出和位选切换,不做除法运算。flag_1s用标志位而不是在中断里直接跑状态机,是为了避免中断嵌套和长耗时操作。
4.2 相位状态机:四个状态加倒计时变量
把第 2 章的时序表翻译成状态机,四个状态循环:S_EW_RED(东西红、南北绿,60 秒)、S_YELLOW1(双黄闪 5 秒)、S_EW_GREEN(东西绿、南北红,80 秒)、S_YELLOW2(双黄闪 5 秒)。每个状态维护一个倒计时变量countdown,每秒减 1,减到 0 切下一状态并重装时长。
typedef enum { S_EW_RED, S_YELLOW1, S_EW_GREEN, S_YELLOW2 } phase_t; phase_t phase = S_EW_RED; unsigned char countdown = 60; unsigned char t_red = 60, t_green = 80; // 可由键盘修改 void traffic_fsm(void) { if (!flag_1s) return; flag_1s = 0; if (countdown > 0) countdown--; if (countdown == 0) { switch (phase) { case S_EW_RED: phase = S_YELLOW1; countdown = 5; break; case S_YELLOW1: phase = S_EW_GREEN; countdown = t_green; break; case S_EW_GREEN: phase = S_YELLOW2; countdown = 5; break; case S_YELLOW2: phase = S_EW_RED; countdown = t_red; break; } } output_lights(phase); // 按状态写 PA 口 output_countdown(countdown); // 倒计时送数码管 }状态机的好处是加相位、改配时只动switch分支和时长变量,不用在汇编里到处跳转。原料的汇编用N00/N1/N2/N3标号实现同样的循环,C 语言版本更利于答辩时讲清楚“状态迁移条件是什么”。
4.3 黄灯闪烁与闯红灯报警的时序处理
黄灯闪烁不是简单常亮,而是亮 0.5 秒、灭 0.5 秒,持续 5 秒。实现上在黄灯状态里用一个 0.5 秒计数器翻转输出位,5 秒内翻转 10 次。原料用CJNE R4,#7DH,$这类空循环凑 0.5 秒,C 版本可以复用 50 毫秒中断计数,每 10 次翻转一次,时序更准。
闯红灯报警逻辑:红灯相位期间,若检测到越线信号(P2.0 或独立检测输入为有效电平),拉低蜂鸣器控制位 P3.0,同时启动 3 秒报警计时,计时到就恢复。这里要注意报警不能打断正常倒计时,所以报警计时和相位倒计时用两个独立变量,互不覆盖。原料里B0/B01段就是这个思路,判断倒计时是否归零再决定是否继续报警,避免绿灯切换后还在响。
4.4 车流量检测与键盘设定初值
车流量检测放在绿灯相位:每检测到一个有效脉冲,计数器加 1,超过 99 回绕(双位数码管只能显示两位),并把当前值送数码管显示,直到下一次绿灯重新计数。检测用边沿触发比电平触发可靠,否则开关按住不放会一直累加。
键盘设定初值走 P1 口:P3.1 为 0 进入设置态,读 P1 低 7 位作为时长值,P1.7 区分设红灯还是绿灯,设完写回t_red或t_green并刷新显示。这里有个细节:键盘是机械按键,必须做 10 到 20 毫秒软件消抖,否则一次按下会被读成多次,时长直接跳变。原料的汇编里用LCALL DELAY做延时消抖,C 版本同样保留这个习惯。
5. 避坑与排查:调试阶段最容易翻车的五个点
5.1 灯全亮或全灭,先查共阳共阴和驱动
现象:上电后 6 只灯同时亮或同时不亮,改程序也没反应。原因多半是发光二极管接法判断错,共阳接法要低电平点亮,程序里却按高电平写;或者 PA 口直接驱动电流不够,灯偏暗甚至不亮。解决:先用万用表量 PA 口对应位在“该亮”时是不是低电平,再确认每路是否加了限流电阻或驱动三极管(原料用 74LS07 做驱动就是干这个的)。这一步不确认,后面所有时序调试都是白费。
5.2 数码管显示乱码或重影
现象:倒计时数字缺段、两位数字叠在一起、或者亮度忽明忽暗。原因通常是段码表和实际接线顺序对不上,或者动态扫描没有消隐——换位选前没先关段码,上一位的残影留到了下一位。解决:先静态点亮一位验证段码表,再开动态扫描;扫描函数里按“关段码→换位选→送新段码→延时”的顺序写,延时控制在 1 到 2 毫秒。原料段码表是按dp g f e d c b a排的,接线换了顺序就要重算,不能直接抄。
5.3 倒计时不准,越走越偏
现象:跑十分钟后倒计时和手机秒表差出好几秒。原因一般是中断里做了耗时操作(比如在中断里做除法转十进制、长延时),导致 50 毫秒中断被拉长;或者重装初值时漏了TH0/TL0的补偿。解决:中断里只置标志、做最短的扫描,进制转换和状态判断放主循环;重装初值用“当前值加 50 毫秒”而不是固定写死,减少累积误差。教学场景对精度要求不高,但偏差大到肉眼可见就说明中断设计有问题。
5.4 8255A 写不进去,灯和数码管都没反应
现象:初始化代码跑了,但 PA 口电平不变。原因常见于片选地址算错、XBYTE基址和实际译码不符,或者控制字写成了输入方向。解决:用示波器或逻辑笔看写控制口时 CS、WR、A0、A1 的时序,确认片选有效;把控制字0x80单独写一个小测试程序验证;再检查 74LS373 的 LE 和 OE 接法,锁存没打开时地址根本送不到 8255A。这类问题属于“黑匣子”式故障,分段隔离比盯着代码猜快得多。
5.5 闯红灯报警误触发或一直响
现象:没车闯红灯也报警,或者报警响起来停不下来。原因通常是检测输入没有消抖,电平抖动被当成多次触发;或者报警计时变量和倒计时变量共用,切换相位时把报警计时清零逻辑写乱了。解决:检测输入加 RC 滤波或软件确认连续多次采样有效;报警计时独立成一个变量,只在报警触发时装载 3 秒,倒计时归零只影响相位不影响报警复位。血泪经验是:报警和正常时序一定要解耦,否则一个边界条件就能让蜂鸣器响一整晚。
6. 验收与答辩:怎么证明这套交通灯真的能交付
验收分三层,按顺序走,别跳。第一层是静态检查:断电用万用表测电源对地无短路,5V 稳压输出正常,8255A 控制字写入后 PA/PB/PC 三口电平可控,这一步过了才敢上电。第二层是功能逐项验收,对着第 2 章的需求清单打勾,建议做成下面这张表,答辩时直接展示。
| 验收项 | 操作 | 合格判据 |
|---|---|---|
| 相位循环 | 上电观察一个完整周期 | 红60s→黄闪5s→绿80s→黄闪5s,顺序正确 |
| 黄灯闪烁 | 秒表卡黄灯段 | 亮灭各约0.5s,总时长约5s |
| 倒计时显示 | 对照秒表 | 每秒减1,两位数字无缺段无重影 |
| 键盘设时 | 进入设置态改红灯为30s | 下一周期红灯段变为30s |
| 车流量检测 | 绿灯期按检测开关若干次 | 数码管计数递增,下次绿灯重新计数 |
| 闯红灯报警 | 红灯期触发检测输入 | 蜂鸣器响约3s后自动恢复,不干扰倒计时 |
第三层是边界与异常验收:把红灯设成 5 秒、绿灯设成 5 秒看会不会和黄灯逻辑打架;按住检测开关不放看计数会不会失控;拔掉看门狗看复位后能否回到初始相位。这些边界条件答辩时主动讲出来,比被问倒强。
答辩陈述有个我常用的节奏:先用一句话说清“这不是固定配时,是可通过键盘改时长、能检测车流量、能报警的控制器”,再拿时序表讲相位设计,接着用状态机代码讲软件结构,最后主动说一个自己踩过的坑(比如数码管重影怎么解决的)。导师最想听的是你的设计决策依据,而不是把论文目录念一遍。被问到“为什么不用更高级的芯片”时,答“教学场景下 8051 加 8255A 的端口扩展路径清晰、成本低、可调试性好,动态配时和检测接口已经留出,换平台只需改驱动层”,这个回答既守住方案又留了扩展余地。
从那以后我每次带这类单片机设计,都强制先画时序表再动烙铁,先验证 8255A 三口电平再写业务逻辑,验收表不填满不算完成。这套顺序看着笨,但能省掉大量返工。希望帮到你。
本文还有配套的精品资源,点击获取