简介:这份毕业设计论文面向电子信息、自动化等专业的学生与单片机初学者,围绕城市路口车流动态变化下传统定时交通灯难以适配的问题,给出以MSC-51系列ATSC51单片机配合8255A可编程并行I/O接口芯片实现交通灯智能控制的完整方案。压缩包内为1个doc文档,约257KB,涵盖交通灯发展沿革、8051内部结构与引脚说明、硬件原理图与接口电路分析、初始化与状态切换等源程序代码,以及系统测试与结论,结构完整、层次清晰。已有1007人学习下载,可作为毕业设计选题参考与课程设计范本。读者可从中获取从理论分析、软硬件设计到实测验证的系统化流程,理解红绿灯时长按车流量编程调整、倒计时剩5秒黄灯闪烁、闯红灯报警及绿灯车流量检测显示等功能的实现思路,并借鉴论文的章节组织与论述方式,用于自己的课题写作与单片机控制类项目开发。
1. 从一块 8031 最小系统板说起:这套交通灯控制器到底能跑出什么效果
去年帮一个做实训室改造的朋友收拾柜子,翻出一块落灰的 8031 最小系统板,旁边还压着两片 8255 和一堆共阴数码管。他随口说了句“这玩意儿当年做过交通灯”,我顺手把板子接上 5V,烧了个测试程序,数码管亮起来那一刻,突然就理解了为什么这类单片机交通灯设计年年有人做、年年有人卡在仿真和实物对不上。这套基于 MCS-51 系列单片机加 8255A 可编程并行接口的交通灯控制器,核心就三件事:红绿灯按东西 60 秒、南北 80 秒循环点亮,倒计时剩 5 秒黄灯闪烁警示,以及绿灯期间能检测车流量、红灯期间能对闯红灯车辆报警。它适合谁?适合正在做单片机课程设计、毕业设计,或者想拿一块 51 板子把并行接口、定时器中断、数码管动态扫描这几样东西一次性串起来练手的开发者。下面我按“这东西怎么搭起来 → 怎么跑起来 → 怎么改”的顺序,把源码和硬件连接拆开讲。
2. 系统架构与硬件选型:8031、8255A、74LS373 各自扛什么活
2.1 为什么是 8031 + 8255A 这套组合
先把这个项目的硬件骨架说清楚。主控用的是 MCS-51 系列的 8031(论文里也写成 ATSC51,实际就是 8051 内核、片内无 ROM 的那一档),它负责定时、状态机切换和中断响应。问题在于 8031 的 I/O 口不够用:P0 和 P2 通常要拿去做外部地址和数据总线,P3 又有第二功能(串口、中断、定时器输入),真正能自由支配的只剩 P1。而系统要同时驱动 6 只交通灯(东西南北各红黄绿)、2 位共阴数码管、一个开关键盘、一个车流量检测开关,还要留出闯红灯报警的蜂鸣器控制线,端口数量明显吃紧。
所以这里引入了 8255A 可编程并行接口芯片做端口扩展。8255A 有三个 8 位端口 PA、PB、PC 和一个控制寄存器,工作方式 0 下三个端口都能做基本输入输出。项目里的分工是:PA 口输出交通灯信号(低电平点亮,共阳接法),PC 口输出数码管的段码,PB 口配合 PC 口做位选,控制口用来写方式控制字。这样 8031 只需要通过 P0 口分时复用地址/数据总线去访问 8255A 的四个寄存器地址,就把外设全挂上去了。
地址译码这块用的是线选法,P0.7 接 8255A 的片选 CS,P0.1、P0.0 接 A1、A0 选端口。于是得到四个地址:00H 是 PA 口、01H 是 PB 口、02H 是 PC 口、03H 是控制口。这个映射在源码里反复出现,比如MOV DPTR,#0000H后跟MOVX @DPTR,A就是往 PA 口写灯的状态。74LS373 则负责锁存 P0 口送出的低 8 位地址,因为 P0 是地址/数据分时复用的,不锁存的话地址会丢。
2.2 硬件连接清单与关键引脚
把论文里的器件清单和接线逻辑整理成一张表,方便对着焊板子或者画原理图时核对:
| 器件 | 数量 | 作用 | 关键连接 |
|---|---|---|---|
| 8031/8051 单片机 | 1 | 主控,定时与状态切换 | P0 接 8255 数据/地址,P1 接时间设定键盘,P2.0 接车流量检测,P3.0 接报警/工作模式 |
| 8255A | 1 | 并行 I/O 扩展 | PA 出灯信号,PC 出段码,PB 出位选,CS 接 P0.7 |
| 74LS373 | 1 | 地址锁存 | 锁存 P0 低 8 位地址送 8255 |
| 74LS07 | 2 | 驱动缓冲 | 增强数码管与灯驱动能力 |
| MAX692 | 1 | 看门狗 | 防止程序跑飞 |
| 共阴七段数码管 | 2 | 倒计时/车流量显示 | 段码接 PC 口,位选接 PB 口 |
| 红黄绿 LED | 各 2 | 交通信号 | 共阳接法,接 PA 口 |
| 7805 | 1 | 稳压 | 5V 供电 |
这里有个容易忽略的点:论文写的是“共阳极接法,输出低电平点亮”,但数码管又写“共阴级连接”。这两者不矛盾——交通灯 LED 是共阳(PA 口输出 0 点亮),数码管是共阴(PC 口输出高电平点亮段)。焊板子时如果搞反,现象就是灯全亮或者数码管显示乱码,后面避坑章节会细说。
2.3 8255A 方式控制字的计算
8255A 上电后必须先写方式控制字,否则端口行为不确定。按项目需求:PA 输出、PB 输出、PC 输出,全部工作方式 0。控制字格式是 D7=1(方式标志)、D6D5=00(A 口方式 0)、D4=0(A 口输出)、D3=0(C 口高四位输出)、D2=0(B 口方式 0)、D1=0(B 口输出)、D0=0(C 口低四位输出),合起来就是 10000000B,即 80H。源码里MOV A,#80H后MOVX @DPTR,A(DPTR=0003H)就是干这个的。这一步没做或者写错,后面所有灯和数码管都不会有反应,是排查时第一个要确认的地方。
3. 核心代码拆解:定时器、状态机与显示子程序怎么咬合
3.1 定时器 T0 产生 1 秒基准
整个系统的时间基准来自 T0。8031 主频按 6MHz 算,机器周期是 2us,方式 1 是 16 位定时器,最大定时 65.536ms,定不了 1 秒,所以采用“定时器 + 软件计数器”的组合:T0 每 50ms 溢出一次,中断里对一个软件计数器累加,满 20 次就是 1 秒。源码里 T0 初值给的是TH0=#3CH、TL0=#B0H,即 3CB0H=15536,65536-15536=50000 个机器周期,乘 2us 正好 100ms?这里论文正文写的是 50ms,但按 6MHz 算是 100ms,属于论文里主频和初值没对齐的典型问题,实际调试时以你板子的晶振为准重新算初值。中断服务程序骨架如下:
ORG 000BH ; T0 中断入口 LJMP T0_INT T0_INT: MOV TL0,#9AH ; 重装初值(论文后段用的 10ms 版本) MOV TH0,#0F1H INC R4 INC R5 CJNE R5,#0FAH,T01 ; 累计到 250 次认为 1 秒到 MOV R5,#00H DEC R3 ; 倒计时减一 DEC R2 ; 报警计时减一 T01: ACALL DISP ; 每次中断都刷新显示 RETI逻辑说明:R3 存当前相位剩余秒数,R2 存报警剩余秒数,R5 做秒内计数。CJNE R5,#0FAH,T01里的 0FAH=250,配合 10ms 定时正好凑 2.5 秒?这里又是论文前后参数不一致的地方——前段说 50ms×20=1 秒,后段中断里用 10ms×250。我一般会统一成“T0 定 50ms,R5 计到 20”,这样 R3 每减一就是实打实 1 秒,数码管倒计时才不会跑快或跑慢。参数上你只需要改两个地方:T0 初值(按晶振算)和 R5 的比较值(1 秒除以定时周期)。
3.2 主循环里的四相位状态机
交通灯的核心是一个四状态循环:东西红/南北绿(60s)→ 全黄闪(5s)→ 东西绿/南北红(80s)→ 全黄闪(5s)→ 回到起点。源码用标号 N00、N1、N2、N3 把四个相位串起来,每个相位先往 PA 口写一个灯码,再等 R3 倒计时归零。灯码是位运算的结果,比如东西红南北绿对应 PA 口输出 0DDH,东西绿南北红对应 0EBH,全黄对应 0D4H/0E2H。截取东西红相位:
N00: MOV A,76H ; 76H 存东西红灯时间 MOV R3,A ; 装入倒计时 MOV DPTR,#0000H ; 选 PA 口 MOV A,#0DDH ; 东西红、南北绿 MOVX @DPTR,A N01: JB P2.0,B0 ; P2.0 为高则跳去闯红灯检测 N02: SETB P3.0 CJNE R3,#00H,N01 ; 倒计时没到 0 就继续等逻辑说明:MOVX @DPTR,A是 8031 访问外部扩展口的固定写法,DPTR 指向 8255A 的端口地址。CJNE R3,#00H,N01是轮询等待,R3 由 T0 中断递减,主循环和中断通过这个共享变量协作。参数上,60 和 80 这两个时间分别存在 R7 和 R0 里(由 P1 口键盘设定),改时间就是改这两个寄存器的初值,不用动状态机结构。
3.3 数码管动态扫描与段码表
两位数码管显示倒计时,用的是动态扫描:先送十位段码、开十位位选,延时,再送个位段码、开个位位选。段码表 TAB 里存的是共阴 0-9 的编码(3FH、06H、5BH…)。显示子程序先把 R3 里的二进制数除以 10 拆成十位和个位,再查表输出:
DISP1: MOV B,#0AH MOV A,R3 DIV AB ; A=十位, B=个位 MOV 79H,A MOV 7AH,B DIS: MOV A,79H MOV DPTR,#TAB MOVC A,@A+DPTR ; 查十位段码 MOV DPTR,#0002H ; PC 口 MOVX @DPTR,A MOV DPTR,#0001H ; PB 口位选 MOV A,#0F7H MOVX @DPTR,A LCALL DELAY DS2: MOV A,7AH MOV DPTR,#TAB MOVC A,@A+DPTR ; 查个位段码 MOV DPTR,#0002H MOVX @DPTR,A MOV DPTR,#0001H MOV A,#0FBH MOVX @DPTR,A RET逻辑说明:DIV AB是 51 汇编里做 BCD 拆位的常用手法,商在 A、余数在 B。位选码 0F7H 和 0FBH 分别对应十位和个位有效(低电平选通),这个值取决于你的位选译码电路,换板子要重算。段码表 TAB 必须和数码管共阴/共阳类型匹配,共阴用 3FH 起头,共阳要取反成 C0H 起头,这是新手最容易翻车的一处。
3.4 闯红灯报警与车流量检测
报警逻辑挂在东西红相位里:P2.0 接的是一个模拟“车辆越线”的开关,红灯期间检测到 P2.0 为高就跳到 B0 标号,拉低 P3.0 驱动蜂鸣器,持续 3 秒(R2 计数)后恢复。车流量检测则在绿灯相位:每按一次 P2.0 开关,R7 加一,计到 64H(100)归零,同时把值送去数码管显示。这两段本质都是“在特定相位里轮询一个 IO 电平”,实现不难,难的是别让报警和倒计时互相打断——源码里用JZ N1判断倒计时是否结束来避免报警卡死,这个细节值得保留。
4. 从源码到实物:编译、烧录与仿真验证的完整步骤
4.1 开发环境与工程建立
这套代码是标准 8051 汇编,工具链用 Keil C51(或 Keil uVision 的汇编工程)最省事。新建工程选 8031/8051 器件,添加一个 .asm 源文件,把论文里的源码贴进去。注意源码里ORG 0000H、ORG 000BH这些是绝对地址定位,中断向量表不能挪。编译前确认两件事:一是MOV DPTR,#TAB引用的 TAB 标号在文件里真实存在(论文只给了段码表数据,没给标号定义,需要自己补TAB: DB 3FH,06H,5BH,4FH,66H,6DH,7DH,07H,7FH,6FH);二是DELAY子程序论文给了两个版本,保留一个即可,重复定义会报错。
4.2 用 Proteus 先跑仿真再上实物
直接焊板子之前,强烈建议先在 Proteus 里把 8031、8255A、74LS373、数码管和 LED 搭出来跑一遍。仿真里能快速验证三件事:8255A 控制字 80H 写进去后 PA 口是否按预期输出灯码、数码管段码和位选是否匹配、T0 中断周期是否和倒计时对得上。仿真通过再烧实物,能省掉大量“灯不亮到底是程序错还是接线错”的扯皮。烧录用通用 8051 编程器或支持 8031 的下载线,hex 文件由 Keil 生成。
4.3 上电后的自检顺序
实物第一次上电,按这个顺序看现象,能快速定位问题层:先看数码管有没有显示(没有→查 8255 控制字和 PC 口接线);再看交通灯是否按 60/80 秒切换(不切换→查 T0 中断是否使能、R3 是否在减);再看黄灯是否在剩 5 秒时闪烁(不闪→查 N1/N3 段落的 R3 初值 05H 和黄灯码);最后测 P2.0 开关能否触发报警和车流量计数(不响应→查 P2.0 上拉和边沿逻辑)。这个顺序对应代码的执行链,从显示到状态机到外设输入,一层层往下排。
5. 避坑与排查:这类 51 交通灯设计最容易翻车的五个地方
5.1 数码管显示乱码或全亮
现象:上电后两位数码管显示的不是倒计时,而是乱码或者八段全亮。原因通常是段码表共阴/共阳搞反,或者位选码和实际译码电路对不上。论文里段码表按共阴给(3FH 起),但如果你买的是共阳数码管,3FH 会让所有段反着亮。解决:确认数码管型号,共阳就把段码表全部按位取反(3FH→C0H),位选码也按实际选通电平重算,别照抄 0F7H/0FBH。
5.2 倒计时走得比秒表快或慢
现象:数码管每秒减 2 或者每 2 秒才减 1。原因就是前面提过的定时参数不一致——论文前段按 50ms×20 算 1 秒,后段中断里却用 10ms×250,两套参数混用。解决:先确认晶振频率(6MHz 还是 11.0592MHz),按初值 = 65536 - 定时时间/机器周期重算 TH0/TL0,再把 R5 的比较值改成1秒/定时周期,两个数必须来自同一套假设。
5.3 8255A 完全不响应
现象:灯不亮、数码管不显示,像没上电。原因多半是控制字没写对或者片选地址错。检查MOV DPTR,#0003H后写的值是不是 80H,以及 P0.7 是否真的接到了 8255 的 CS、A1/A0 是否接到 P0.1/P0.0。线选法下地址高位是“无关位”,但 CS 必须为低才选中,接反或者虚焊都会导致所有 MOVX 写操作落空。
5.4 黄灯不闪或闪个不停
现象:进入黄灯相位后灯常亮不闪,或者闪烁频率明显不对。原因在 N1/N3 段落里用CJNE R4,#7DH,$做软件延时,这个值是按某个主频估的,换晶振后闪烁周期就变了。解决:把黄灯闪烁改成基于 T0 的 0.5 秒节拍翻转,而不是裸软件延时,这样主频变了也不影响闪烁节奏。
5.5 报警触发后倒计时卡死
现象:按下闯红灯开关后蜂鸣器响,但倒计时停住不动。原因是报警循环 B01 里没有正确退出条件,或者 R2 递减和 R3 递减在中断里互相干扰。解决:报警持续时间用独立计数器,并且在报警循环里保留对 R3 是否为 0 的判断(源码里JZ N1就是干这个的),确保倒计时结束能强制退出报警。
6. 二次开发怎么改:把固定配时升级成可配置、可扩展的控制器
这套代码最大的改造价值在于它把“时间参数”和“状态机结构”分离得还算干净,改起来有抓手。我一般按三个层次动刀。第一层是配时参数外置:现在 60 和 80 秒是键盘通过 P1 口写进 R7/R0 的,可以进一步把四个相位的时长做成一张表存在片内 RAM 或 EEPROM 里,上电读表初始化,这样不用每次上电重新按键设定。第二层是车流量真正参与配时:目前车流量检测只是计数显示,没有反馈到绿灯时长,可以在绿灯相位结束时读一次 R7,按“车多延长、车少缩短”的简单规则动态调整下一轮绿灯时间,规则写成查表或分段函数都行,改动集中在 N2 相位入口。第三层是通信与远程监控:8031 的串口(P3.0/P3.1)目前没被占用,可以加一个简单的 UART 上报协议,把当前相位、剩余时间、车流量打包发出去,上位机用任意串口工具就能看,这一步对毕业设计的“扩展功能”章节很加分。
验证改造是否成功,我习惯用一张对照表逐项过:
| 改造点 | 验证方法 | 通过标准 |
|---|---|---|
| 配时表初始化 | 断电重启后不按键 | 倒计时从表里设定值开始 |
| 车流量动态配时 | 绿灯期间连续触发 P2.0 | 下一轮绿灯时长有可见变化 |
| 串口上报 | 串口助手接 P3.0/P3.1 | 每秒收到一帧相位+时间数据 |
| 报警不卡死 | 红灯期间触发报警并等倒计时归零 | 报警 3 秒后正常进入下一相位 |
最后说个血泪经验:这类 51 交通灯项目,代码本身很少出错,翻车基本都在“论文参数和实物参数对不上”上——晶振频率、共阴共阳、位选电平、8255 地址,这四样任何一样照抄论文都可能不亮。从那以后我每次拿到这类设计,都强制先把这四项在纸上列出来、对着实物核一遍再烧程序,省下的调试时间比写代码多得多。希望帮到你。
本文还有配套的精品资源,点击获取