
简介这份毕业设计论文面向电子信息、自动化等专业学生及单片机初学者围绕城市路口车流疏导问题给出以MSC-51系列ATSC51单片机配合8255A可编程并行I/O接口芯片构建交通灯控制器的完整方案可用于课程设计参考、毕设选题借鉴或智能交通控制入门学习。资源包共1个doc文件约257KB内容涵盖交通灯发展沿革、8051内部结构与引脚说明、硬件原理图与接口电路、初始化与状态切换等源程序代码以及系统测试与结果分析。论文实现了按实际车流量经P1口设定红绿灯时长、倒计时剩5秒黄灯闪烁警示、闯红灯报警和绿灯期间车流量检测并数码管显示等功能软硬件设计流程完整测试结论明确。目前已有1007人学习适合需要一份结构规范、可直接参照实现的单片机控制类毕设范本的读者。1. 交通灯控制器为什么一个 8 位单片机就能撑起一个路口的秩序十字路口车流穿梭、行人往来看起来杂乱无章实际上背后有一套极其严格的时序在约束每一盏灯的亮灭。很多人第一次看到基于 8051 的交通灯设计会觉得这不过是「点亮几个 LED、做个倒计时」的入门练习没什么可深挖的。但真正把系统跑起来才会发现难点根本不在点灯而在于如何用一个没有操作系统、没有硬件定时中断保障、I/O 资源还捉襟见肘的 8 位单片机稳定地维持一个多状态、带倒计时、能响应外部事件的有限状态机。这份毕业设计论文围绕 MSC-51 系列单片机与 8255A 并行接口芯片展开把红绿灯循环、黄灯闪烁警示、闯红灯报警、车流量检测这几件事塞进同一套时序里恰好是一个观察「资源受限系统如何做调度」的绝佳样本。搞懂它你就理解了嵌入式控制里最朴素也最核心的那套机制定时器怎么变出一秒、状态怎么切换、I/O 不够时怎么扩展。2. 定时器与软件计数器一秒到底是怎么「数」出来的2.1 为什么硬件定时器单独搞不定一秒8051 的定时器本质上是一个加法计数器从设定的初值开始往上数数到全 1 再进一位溢出时产生中断。它的定时上限由计数器位宽决定。论文里给了明确的计算方式 0 是 13 位计数器最大定时 8192 个机器周期方式 1 是 16 位最大 65536 个机器周期。假设晶振 12MHz经过 12 分频后一个机器周期是 1 微秒那么方式 1 的极限就是 65.536 毫秒。一秒远远超过这个上限所以单靠一次定时器溢出根本凑不出一秒。这就是整个时间基准设计的起点。常见的实现思路是「硬件定时器负责产生一个较短的固定节拍软件计数器负责把这个节拍累加成一秒」。论文选的是 T0 定时 50 毫秒主程序里设一个初值为 20 的软件计数器 R0每进一次中断 R0 减一减到零就说明 20×50ms1s 到了。这个 20 和 50ms 的搭配不是随便定的50ms 既在 16 位定时器的能力范围内又能让中断频率不至于太高20Hz给主循环留出足够的执行时间。2.2 初值计算3CB0H 是怎么来的论文给出的公式是 TC M − T / T计数其中 M 是计数器模值方式 1 下 M 2^16 65536T计数 是一个机器周期即 1 微秒。要定时 50ms就是 50000 微秒TC 65536 − 50000 15536 0x3CB0所以高 8 位 TH0 装 3CH低 8 位 TL0 装 B0H。这段初始化在汇编里长这样ORG 1000H START: MOV TMOD, #01H ; T0 工作在方式116位定时器 MOV TH0, #3CH ; 装入定时初值高字节 MOV TL0, #B0H ; 装入定时初值低字节 MOV IE, #82H ; 开 T0 中断EA1, ET01 SETB TR0 ; 启动 T0 计数 MOV R0, #14H ; 软件计数器初值 20十进制 LOOP: SJMP $ ; 原地等待中断这里有个容易忽略的细节中断服务程序里必须重新装初值。方式 1 不是自动重装载的溢出后计数器归零如果不重装下一次定时就变成从 0 数到 65536节拍全乱。论文的中断服务程序里确实有MOV TH0,#3CH和MOV TL0,#B0H的重装动作这是方式 1 和方式 2 的关键区别——方式 2 是 8 位自动重装适合做波特率但定时范围太小这里用不了。2.3 中断服务程序里的减法逻辑中断服务程序的核心是一个「减到零才动作」的判定ORG 000BH AJMP BRT0 BRT0: DJNZ R0, NEXT ; R0 减1不为0则跳过时间处理 AJMP TIME ; R0 归零说明1秒到跳去刷新显示和倒计时 NEXT: MOV R0, #14H ; 恢复软件计数器 MOV TH0, #3CH ; 重装定时初值 MOV TL0, #B0H RETI注意这里的执行顺序先DJNZ判断只有 R0 减到零才去执行 TIME 子程序否则直接重装初值返回。这意味着倒计时递减、显示刷新这些「每秒一次」的动作全部挂在一秒节拍上而不是每 50ms 都做一遍。这种分层节拍的设计在资源受限系统里非常典型高频中断只做最轻的计数重活留给低频节拍避免中断服务程序过长导致主程序饿死。2.4 软件延时另一条不靠中断的路论文还给了纯软件延时的方案用双重循环凑时间。DELAY1 是内层R5、R6 各装 0即 256循环 256×25665536 次每次DJNZ是 2 个机器周期6MHz 晶振下机器周期 2 微秒算下来约 131072 微秒论文取整说约 125ms这里论文的数值有出入实际按 2us×2×65536 应是 262ms 量级属于原稿计算口径问题不影响机制理解。外层 DELAY 让 R4 循环 8 次凑出接近一秒。DELAY: MOV R4, #08H ; 外层循环 8 次 DE2: LCALL DELAY1 DJNZ R4, DE2 RET DELAY1: MOV R6, #0 ; 内层计数 256 MOV R5, #0 DE1: DJNZ R5, $ ; 空转 256 次 DJNZ R6, DE1 RET软件延时的致命问题是CPU 被完全占死延时期间无法响应按键、无法检测闯红灯。所以论文在正式系统里用的是定时器中断方案软件延时只作为显示子程序里的短延时比如数码管动态扫描的位间延时。这个取舍很关键凡是需要「同时」响应多件事的地方就必须用中断凡是独占式、短时间的等待才用软件延时。3. 状态机与 I/O 扩展红绿灯循环背后的调度逻辑3.1 四状态循环一张表定死整个时序论文用一张表把东西、南北两个方向的状态组合列清楚了东西红灯 60 秒时南北绿灯黄灯闪烁 5 秒作为公共过渡东西绿灯 80 秒时南北红灯再黄灯闪烁 5 秒。因为「东西车流量大」所以绿灯给到 80 秒红灯只给 60 秒。这个不对称是设计上的主动选择不是笔误。落到代码里每个状态对应往 8255 的 PA 口写一个特定的字节。比如东西红灯、南北绿灯时写0DDH两个方向黄灯亮时写0D4H东西绿灯、南北红灯时写0EBH。这些字节的每一位对应一盏灯的亮灭因为 LED 是共阳极接法输出低电平才点亮所以字节里为 0 的位对应的灯亮。这种「一个字节编码一组灯状态」的做法本质上是把状态机的输出表压缩成了常量表切换状态就是换一个常量写端口执行效率极高。N00: MOV A, 76H ; 取东西红灯倒计时值 MOV R3, A ; R3 作为当前倒计时寄存器 MOV DPTR, #0000H ; 指向 8255 PA 口 MOV A, #0DDH ; 东西红、南北绿 MOVX DPTR, A N01: JB P2.0, B0 ; 检测 P2.0若为高跳去闯红灯报警 N02: SETB P3.0 CJNE R3, #00H, N01 ; 倒计时未到 0 就继续循环这里CJNE R3,#00H,N01构成了倒计时的等待循环而 R3 的递减发生在定时器中断里DEC R3。也就是说主程序在等一个由中断异步修改的变量归零这是裸机编程里最经典的「前后台」协作模式前台是主循环的状态推进后台是中断里的时间递减。3.2 黄灯闪烁用循环次数而不是再开一个定时器黄灯闪烁 5 秒的实现很有意思。它没有另设定时器而是在状态里用一个 R3 计数 5 次每次内部再让黄灯亮 0.5 秒、灭 0.5 秒N1: MOV R3, #05H ; 闪烁 5 次 MOV DPTR, #0000H MOV A, #0D4H ; 黄灯亮 MOVX DPTR, A N11: MOV R4, #00H N12: CJNE R4, #7DH, $ ; 空转约 0.5 秒 N13: MOV A, #0DDH ; 黄灯灭回到红绿状态 MOVX DPTR, A N14: MOV R4, #00H CJNE R4, #7DH, $ ; 再空转约 0.5 秒 CJNE R3, #00H, N1 ; 5 次没闪完继续用CJNE R4,#7DH,$这种「自己跟自己比较、原地打转」的写法做短延时是 8051 汇编里的常见手法7DH 即 125配合指令周期凑出半秒量级。闪烁的本质是「状态内的高频子循环」它不改变主状态机的推进只是在一个状态内部反复切换输出字节。这种嵌套结构让代码不需要额外的定时器资源代价是这段时间 CPU 被占住无法做别的事——但黄灯期间本来也不需要检测车流量所以可以接受。3.3 8255 扩展8051 的 I/O 为什么不够用8051 名义上有 P0、P1、P2、P3 四组共 32 根 I/O但真正能自由支配的很少P0 要传外部地址低 8 位和数据P2 传地址高 8 位P3 的每一根都带第二功能串口、中断、定时器输入、读写选通。论文说得很直白——真正能借用的只有 P1 口。而系统需要键盘输入初始时间、数码管输出倒计时、红绿黄信号灯输出三样都要端口显然不够。解决办法是挂一片 8255A。它有三个 8 位端口 PA、PB、PC 加一个控制口通过 8051 的 P0 口分时复用地址/数据总线来访问。片选接 P0.7地址译码后 PA 口地址 00H、PB 口 01H、PC 口 02H、控制口 03H。初始化时往控制口写 80H含义是方式控制字 D71 有效、A 口方式 0、A 口输出、C 口高 4 位输出、B 口方式 0、B 口输出、C 口低 4 位输出——全部配成方式 0 基本输出因为交通灯只需要单向输出不需要选通和握手。MOV DPTR, #0003H ; 指向 8255 控制口 MOV A, #80H ; 方式0PA/PB/PC 全部输出 MOVX DPTR, A这里用MOVX而不是MOV是因为 8255 被映射到外部数据存储器空间CPU 通过读写外部 RAM 的指令来访问它。论文特别提到「8051 分时对 8255 和存储器访问P0 口不会冲突」说的就是这种总线复用机制地址锁存信号 ALE 先把 P0 上的低 8 位地址锁进 74LS373然后 P0 再转为数据线传数据时序上天然错开。3.4 数码管显示段码表与位选倒计时用两个共阴极七段数码管段码接 8255 的 PC 口和 PB 口位选由另外的输出控制。论文给了一张驱动代码表比如显示「0」是 3FH、「1」是 06H、「8」是 7FH。这些值的来历是共阴极数码管段码位为 1 时对应段点亮a~g 七段加小数点按位排列0 需要 a、b、c、d、e、f 亮而 g 灭二进制 00111111 即 3FH。显示子程序里做的是「二转十」把 R3 里的倒计时值除以 10商放十位、余数放个位再分别查表取段码输出。动态扫描靠位选信号快速切换人眼因视觉暂留看到两位同时亮。这套「段码表 位选扫描」是数码管显示的通用套路理解了一次换任何段码接法都能自己推。4. 避坑与排查这套设计最容易翻车的地方4.1 倒计时不动或跳变现象数码管显示的数字不递减或者一下子跳好几秒。原因多半是定时器初值没在中断里重装方式 1 溢出后从 0 重新数节拍被拉长也可能是软件计数器 R0 的初值和重装值不一致导致一秒的实际长度偏离。解决检查中断服务程序里MOV TH0/MOV TL0是否每次溢出都执行确认 R0 初值与NEXT分支里的恢复值相同。4.2 黄灯闪烁频率不对现象黄灯闪得太快或太慢甚至只亮不闪。原因是CJNE R4,#7DH,$这类空转延时的实际时长依赖晶振频率和指令周期换晶振后 7DH 这个常数就不准了。解决按实际机器周期重新算循环次数或者干脆改用定时器节拍来驱动闪烁把延时从「数指令」变成「数节拍」。4.3 闯红灯报警误触发现象没有车闯红灯蜂鸣器却时不时响。原因是 P2.0 检测的是电平如果该引脚悬空或上拉不足会读到随机电平。解决给 P2.0 加上拉电阻确认检测逻辑是「低电平有效」还是「高电平有效」与硬件接法一致并在报警前加去抖判断避免毛刺触发。4.4 8255 端口读写全无反应现象往 PA 口写数据灯不亮读回来也不对。常见原因是片选地址译码错了或者控制字没先写。8255 上电后端口方向是未定义的必须先写方式控制字再读写数据口。另外MOVX的 DPTR 地址要和硬件译码一致P0.7 接 CS 时地址高位置 1 才选中写 00H 还是 80H 取决于译码逻辑得对着原理图核。4.5 主循环卡死在某状态现象系统停在某个灯色不再切换。原因是状态等待循环里CJNE R3,#00H,xxx依赖中断递减 R3如果中断被意外关闭比如某处改了 IE 寄存器R3 永远不减主程序就死等。解决排查所有对 IE 的写操作确保没有在状态切换中误清 ET0必要时在等待循环里加超时保护。5. 从固定配时到动态配时这套架构的边界与延伸把这份设计拆到底会发现它的核心价值不在于「实现了交通灯」而在于展示了一个极简的实时调度骨架定时器中断提供时间基准软件计数器分频出一秒主循环按状态表推进输出外部引脚检测事件并插入报警或车流量统计。这套骨架的边界也很清楚——所有状态切换都是阻塞式等待黄灯闪烁期间 CPU 被占死车流量检测只能在绿灯倒计时未结束时通过 P2.0 采样本质上还是「轮询 阻塞」的裸机模型。论文里提到的「绿灯时间可检测车流量并动态调整」落到代码上其实只是T03段里对 P2.0 的采样和 R7 计数计数到 64H 归零并没有真正把车流量反馈到下一个周期的配时计算里。这是毕业设计常见的「功能点到为止」。如果要往真实的自适应控制走需要做的是把车流量统计结果存进一个变量在状态切换时用它去修正 R0绿灯时长的装载值形成闭环。这一步不难难的是保证修正逻辑不破坏原有的时序约束——比如绿灯不能短于行人过街的最小时间黄灯过渡不能被压缩。设计点本方案做法真实系统通常的改进时间基准T0 定时 50ms 软件计数 20 次专用 RTC 或更高精度晶振状态推进主循环阻塞等待倒计时归零事件驱动状态机查表跳转车流量P2.0 采样计数仅显示多路传感器 配时算法闭环故障保护MAX692 看门狗双机热备、灯故障检测我一般会这么理解这类设计的取舍在资源受限的平台上先保证时序确定性和实现简单再谈智能化。固定配时虽然「笨」但它的行为可预测、可验证不会因为一个传感器误判就把路口搞乱。真正要上动态配时前提是有一套可靠的检测和降级机制否则不如老老实实跑固定周期。从那以后我每次做带状态机的裸机项目都会先把状态表和每个状态的进入/退出条件写死在纸上再动手写代码避免在中断和主循环之间埋下互相等待的死锁。希望帮到你。本文还有配套的精品资源点击获取