ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于8051与8255A的交通灯控制系统设计:硬件接线、定时逻辑与避坑指南

基于8051与8255A的交通灯控制系统设计:硬件接线、定时逻辑与避坑指南

简介:这份毕业设计论文面向电子信息、自动化等专业的学生与单片机初学者,围绕基于MSC-51系列ATSC51单片机与8255A可编程并行I/O接口芯片构建交通灯控制系统展开,解决传统固定配时信号灯难以适应动态车流的问题。压缩包内为1个doc文档,约257KB,完整收录论文正文,涵盖交通灯发展沿革、8051内部结构与引脚说明、硬件原理图与接口电路分析、初始化与状态切换等源程序代码,以及系统测试与结果讨论。文档还涉及红绿灯配时编程调整、倒计时剩5秒黄灯闪烁警示、闯红灯报警和绿灯时段车流量检测显示等智能化功能,可作为毕业设计写作范本与课程设计参考。目前已有1011人学习,适合需要软硬件结合案例、排错思路与完整设计流程的读者借鉴。

1. 从一份 27 页的交通灯毕设说起:它到底能跑通什么

十字路口那套红黄绿循环,看着简单,真让你用一片 8051 加一片 8255A 从零搭出来,多数人会在定时器初值和数码管动态扫描这两处卡住。这份基于单片机控制交通灯的设计资料,核心就是一套能落地的完整方案:MCS-51 系列单片机做控制核心,8255A 扩展并行 I/O,PA 口驱动红黄绿灯,PC 口推双位数码管倒计时,P1 口接键盘设定红绿灯时长,还带了闯红灯蜂鸣报警和绿灯期间车流量检测。它适合正在做单片机课程设计、毕业设计,或者想拿一块最小系统板把并行接口、定时中断、数码管显示串起来练一遍的从业者。下面按硬件接线、定时逻辑、显示与报警、踩坑排查的顺序拆开讲,参数和代码都能直接抄。

2. 硬件选型与 8255A 端口分配:为什么非得扩一片并行接口

2.1 8031/8051 的 I/O 为什么不够用

8051 名义上有 P0、P1、P2、P3 四组 8 位口,但真正能干净拿来做通用 I/O 的只有 P1。P0 要分时复用做低 8 位地址和數據总线,P2 出高 8 位地址,P3 的每一位都挂着第二功能(串口、外中断、定时器输入、读写选通)。这套交通灯系统要同时驱动 6 只信号灯(东西南北各红黄绿)、2 位共阴数码管(段码加位选)、键盘输入、蜂鸣器,还要留出车流量检测的开关输入,端口数量明显不够。常见做法有两种:用外部 RAM 地址空间做 I/O 映射,或者挂一片可编程并行接口芯片。这份资料选的是后者,8255A。

选 8255A 而不是 74 系列锁存器扩 I/O,理由在于它自带方式控制字,A、B、C 三个口可以按需分配输入输出方向,PA 口整组输出信号灯状态,PC 口输出数码管段码,PB 口还能腾出来做位选或键盘扫描,一片芯片把三组方向不同的口都解决了,布线比堆几片 373 清爽。代价是要多占几个地址线做片选,软件上多一步写控制字。

2.2 8255A 方式控制字怎么算

8255A 上电后三个口默认都是输入,必须先往控制口写一个方式控制字,把方向定下来。控制字格式是 D7 置 1 表示方式设置有效,D6D5 选 A 口方式,D4 定 A 口方向,D3 定 C 口高 4 位方向,D2 选 B 口方式,D1 定 B 口方向,D0 定 C 口低 4 位方向。这套系统里 PA 口输出信号灯、PC 口输出数码管段码、PB 口输出位选,三个口全做输出,方式 0 基本 I/O,控制字就是1000 0000B = 80H。

; 8255A 初始化:三个口全部方式0输出 MOV DPTR, #0003H ; 控制口地址(片选有效时 A1A0=11) MOV A, #80H ; D7=1 方式设置, A/B/C 全输出 MOVX @DPTR, A

这里 DPTR 指向的是外部数据存储器地址,8051 用MOVX访问,片选由 P0.7 译码产生。地址分配是 P0.7=0 时 8255 选中,P0.1、P0.0 接 8255 的 A1、A0,于是 PA 口地址 0000H、PB 口 0001H、PC 口 0002H、控制口 0003H。写控制字这一步如果漏了或者写错方向位,后面灯全不亮或者数码管乱码,是新手最常见的翻车点。

2.3 信号灯与数码管的接线逻辑

信号灯接在 PA 口,采用共阳极接法,端口输出低电平对应的灯亮。资料里东西方向红灯亮、南北绿灯亮时 PA 口送0DDH,黄灯闪烁时送0D4H,东西绿灯南北红灯时送0EBH。这几个值不是随手写的,是把 6 只灯按位排好后取反得到的,改接线顺序就得重算,不能照抄。

数码管用共阴极,段码从 PC 口出,位选从 PB 口出。段码表按dp g f e d c b a排列,0 到 9 对应3FH、06H、5BH、4FH、66H、6DH、7DH、07H、7FH、6FH。动态扫描时先送段码再送位选,十位位选码0F7H、个位0FBH,靠人眼视觉暂留合成两位数字。这里有个细节:段码送 PC 口、位选送 PB 口,两次MOVX之间要留一点稳定时间,否则高位会串到低位上,显示成鬼影。

提示:8255A 的 PA、PB、PC 口地址取决于片选和 A1A0 的接法,换一块板子先拿万用表确认片选逻辑,别默认地址就是 0000H 到 0003H。

3. 定时器与状态机:1 秒倒计时是怎么凑出来的

3.1 为什么一个定时器定不了 1 秒

8051 的定时器在 12MHz 晶振下,机器周期 1 微秒。方式 1 是 16 位计数,最大计数值 65536,单次最长定时 65.536 毫秒,离 1 秒差得远。所以必须定时器加软件计数器两级配合:让 T0 每 50 毫秒溢出一次,中断里对一个软件计数器减一,减到 0 就认为 1 秒到了。资料里软件计数器初值给 20,20 乘 50 毫秒正好 1 秒。

50 毫秒的初值算法是TC = M - T / T计数,M 取 2 的 16 次方 65536,T 是 50000 微秒,T计数 1 微秒,算出来 15536,转十六进制3CB0H,所以 TH0 装3CH、TL0 装B0H。这个初值在 12MHz 下成立,换成 11.0592MHz 晶振就得重算,机器周期变成约 1.085 微秒,直接抄 3CB0H 会让倒计时偏慢,跑一晚上能差出好几分钟。

; T0 方式1,50ms 中断一次,20 次凑 1 秒 MOV TMOD, #01H ; T0 方式1(16位定时) MOV TH0, #3CH ; 50ms 初值高字节 MOV TL0, #0B0H ; 50ms 初值低字节 MOV IE, #82H ; 开 T0 中断 SETB TR0 ; 启动 T0 MOV R0, #14H ; 软件计数器 20 次(14H=20)

中断服务里先重装初值,再DJNZ R0递减,减到零跳去刷新显示和倒计时。注意重装初值必须放在中断入口最前面,否则中断响应延迟会让每次定时多出几个机器周期,累积起来 1 秒就不准了。

3.2 红绿黄状态怎么轮转

东西道车流量大,方案定的是东西红灯 60 秒、南北绿灯同步 60 秒,然后双方黄灯闪烁 5 秒做公共停车过渡,接着东西绿灯 80 秒、南北红灯 80 秒,再黄灯 5 秒,循环。这个时序用一组标号加倒计时寄存器 R3 实现:进入某个状态先把该状态时长装进 R3,每秒中断里DEC R3,R3 减到 0 就跳到下一个状态标号。

黄灯闪烁 5 秒不是简单亮 5 秒,是亮 0.5 秒灭 0.5 秒闪 5 次。资料里用 R3 记闪烁次数(初值 05H),R4 做 0.5 秒延时,每次先送黄灯亮码0D4H,延时后送灭码0DDH,再延时,CJNE R3,#00H判断五次闪完没有。这里 R4 的延时用的是CJNE R4,#7DH,$空转,7DH 是 125,配合指令周期凑出约 0.5 秒,属于软件延时,晶振一变就不准,但对黄灯闪烁这种精度要求不高的场合够用。

3.3 键盘设定初值的入口

P1 口接键盘,P3.1 判断是否进入设定模式,P1.7 区分设红灯还是绿灯。按下设定键后,从 P1 读入的数值直接存进 R7(红灯)或 R0(绿灯),同时调显示子程序让人看到当前设定值。这里有个容易忽略的点:P1 口做输入前必须先写FFH,把内部上拉打开,否则读进来永远是 0。资料代码里没显式写这句,实际搭电路时如果键盘读不到值,先补这一条。

; 键盘读入前先把 P1 置为输入态 MOV P1, #0FFH ; P1 写全1,准备读键值 JB P3.1, N0 ; P3.1=1 表示不设定,直接运行 MOV A, P1 ; 读入设定值 JB P1.7, RED_SET ; P1.7=1 设红灯时间 MOV R0, A ; 否则设绿灯时间

4. 显示、报警与车流量检测:三个附加功能怎么落地

4.1 双位数码管动态扫描

倒计时值存在 R3 里,是二进制,要显示得先转成十进制。资料用MOV B,#0AH配合DIV AB,商进 79H 存十位,余数进 7AH 存个位,然后分别查段码表送 PC 口。十位和个位不能同时送,得靠位选轮流点亮:先送十位段码、开十位位选0F7H,延时;再送个位段码、开个位位选0FBH,延时。扫描频率要够高,一般每位停留 1 到 2 毫秒,太低会闪,太高亮度不够。

; 两位动态扫描:先十位后个位 DISP: 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 ; 短延时保持亮度 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

段码表TAB要放在程序存储器里用MOVC查,别放 RAM,8051 的 RAM 只有 128 字节用户空间,塞个段码表就紧张了。另外MOVC的偏移量是 A 寄存器,查表前确保 A 里是 0 到 9,超范围会读到乱码。

4.2 闯红灯报警的触发条件

报警逻辑挂在红灯倒计时期间:如果检测到有车闯红灯(资料里用 P2.0 或外部中断配合判断),启动蜂鸣器,R2 装 3 秒倒计时,响 3 秒后恢复。代码里B0标号段先判断 R3 是否已经减到 0,到 0 说明红灯快结束,不再报警,避免和黄灯切换打架。蜂鸣器接 P3.0,低电平驱动,CLR P3.0响、SETB P3.0停。

实际搭的时候蜂鸣器别直接挂 I/O 口,8051 单口灌电流有限,常见做法是加一只三极管做驱动,基极串 1K 电阻,否则声音小还可能拉低端口电平影响其他功能。

4.3 绿灯车流量检测

绿灯期间按 P2.0 开关模拟车辆通过,每按一次 R7 加一,计到 64H(100)清零重计。这个值在绿灯结束时可以拿去调整下一轮绿灯时长,资料里只做到计数和显示,动态调整留给使用者扩展。检测逻辑放在T03标号段,先SUBB A,#00H判断绿灯倒计时是否结束,没结束才继续读 P2.0。这里SUBB会带进位位,前面最好先CLR C,否则借位标志残留会让判断出错。

注意:车流量检测开关要做消抖,机械按键抖动几毫秒,不加延时或电容滤波,按一次可能被计成好几次,R7 涨得飞快。

5. 避坑排查:这套方案最容易翻车的五个地方

5.1 灯全不亮或常亮

现象:上电后 6 只信号灯要么全灭,要么全亮,改代码没反应。原因多半是 8255A 控制字没写进去,或者片选地址算错,MOVX写到了不存在的地址。解决:先用万用表量 8255 的 CS 脚,确认 P0.7 拉低时 CS 有效;再单步跑初始化那段,看控制口地址 0003H 是否真的选中;最后确认 PA 口输出码和灯的共阳/共阴接法匹配,共阳接法输出低电平才亮,送0DDH亮的是哪几只灯要对着接线图核一遍。

5.2 倒计时走得快或慢

现象:设定 60 秒,实际跑了 55 秒或 65 秒。原因基本是定时器初值按 12MHz 算的,板上晶振却是 11.0592MHz,或者中断里重装初值的顺序不对,被其他中断打断导致漏装。解决:先量晶振实际频率,按TC = 65536 - 50000 / 机器周期重算 TH0、TL0;再把重装初值放到中断入口第一条,关掉其他不必要的中断源,减少响应延迟。

5.3 数码管显示鬼影或只亮一位

现象:两位数字叠在一起,或者只有一位亮。原因一是动态扫描延时太短,段码还没稳定就切位选;二是位选码送错口,PB 口和 PC 口搞反;三是段码表放进了 RAM 导致MOVC读不到。解决:每位扫描延时加到 1 到 2 毫秒;对照原理图确认 PB 出位选、PC 出段码;段码表用DB定义在代码段,别用MOV往 RAM 里塞。

5.4 键盘设值读不进来

现象:按设定键没反应,或者读进来的值一直是 0 或 255。原因:P1 口做输入前没写FFH,内部场效应管没打开,读到的电平被前级拉死;或者 P3.1、P1.7 的判断逻辑和实际按键接法反了。解决:读键前补MOV P1,#0FFH;用万用表量按键按下时对应引脚电平,确认是高有效还是低有效,再改JB还是JNB。

5.5 黄灯闪烁次数不对或卡死

现象:黄灯闪两下就跳走,或者一直闪不切换。原因:R3 闪烁次数初值被前面的倒计时逻辑改掉了,或者CJNE R3,#00H判断时 R3 已经被减成负数(0FFH)导致循环次数暴增。解决:进黄灯段先重新装MOV R3,#05H,别复用红灯的 R3;CJNE比较前确认 R3 是无符号数,递减用DEC后立即判断,别让它在 0 下面再减。

6. 从能跑到好用:把固定配时改成按车流量微调

这套设计跑通只能算及格,真正拉开差距的是绿灯时长能不能跟着车流量走。资料里车流量检测只做到计数显示,我一般会在这个基础上加一层简单映射:绿灯结束时读 R7 的计数值,按区间查一张时长表,下一轮绿灯直接用查到的值装 R3。比如 0 到 20 辆给 40 秒,21 到 50 辆给 60 秒,50 辆以上给 80 秒,表放在程序存储器里,改参数不用动逻辑。

; 绿灯结束按车流量查表定下一轮时长 MOV A, R7 ; 本轮车流量计数 MOV DPTR, #FLOW_TAB ; 时长表首地址 MOVC A, @A+DPTR ; 查得绿灯秒数 MOV R0, A ; 存入绿灯时长寄存器 ... FLOW_TAB: DB 40, 40, 40, 60, 60, 60, 80, 80 ; 按计数区间给时长

查表前要把 R7 限制在表长范围内,超了会读到程序区其他数据,表现就是绿灯时长突然变成一个莫名其妙的数。我习惯在查表前加一句CJNE R7,#08H,CHK,超过表长直接钳到最大值。

验证方法也简单:拿信号发生器或手动按键模拟车流,每轮绿灯结束记一次 R7 和实际绿灯秒数,跑十轮看映射是否单调、有没有跳变。再用秒表卡三次完整循环,总时长误差控制在 1 秒内算合格。想再进一步,可以把东西、南北两个方向的车流量分别计数,按比值分配绿灯时长,这就从固定配时走到了感应控制的门槛上。

血泪经验是:动态调整一定要留上下限,我见过有人把下限设成 5 秒,结果车流一少绿灯闪一下就切,行人根本过不去。从那以后我每次改配时表,都强制先跑一遍空载和满载两个极端场景,确认最短绿灯不短于行人过街时间、最长绿灯不把对向憋死,才敢烧进片子。希望这套拆解帮到你,少在定时初值和片选地址上耗时间。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进