简介:华南理工大学2020年微机课程设计报告,围绕电子日历时钟项目,给出基于51单片机和汇编语言的完整设计方案。内容从实验目的、功能要求到软硬件方案逐步展开:硬件选用4位LED数码管动态扫描、矩阵键盘一键多用、INT0/INT1中断切换显示模式;软件则通过定时中断产生基准时间、计数器完成秒至年级别的计时,并给出六种显示模式的切换逻辑、闹钟检测流程和数码管显示原理,同时处理了闰年判断、动态扫描消隐等关键细节。资源共1个doc文档,压缩包大小3.89MB,报告层次分明,配有按键操作方法与运行结果截图,便于理解程序入口、MODE切换和校准流程。已有1141人学习下载,适合作为微机原理、单片机应用或汇编语言课程设计的参考资料和实现蓝本。
1. 微机课设报告不是写出来的,是做出来的
微机课程设计最常见的翻车方式,是代码能跑、现象也对,但报告里贴的原理图和实际搭的电路对不上,或者老师追问一句“你这个片选地址为什么是 0x300”就答不上来。课程设计报告本质上不是论文,而是一份工程记录,评的是从需求到实现的闭环:题目、方案选型、硬件电路、软件逻辑、测试结果,每一环都要能禁得住追问。这篇以 8086 最小系统加 8255A、8253 这套最经典的接口方案为例,从选题、选型、电路、汇编代码到报告定稿,把一套能拿得出手的微机课设报告的完整路径讲清楚。适合正在准备或正在写课设报告的学生,也适合带课设的助教拿来当评分参照。
2. 选题与芯片选型:课设工作量一半耗在这
2.1 按“状态机清晰度”选题,别按“听起来高级”选题
微机课设的题目一般围绕控制、显示、输入三个要素展开。常见的有交通灯控制、电子时钟、点阵显示、温度采集、步进电机控制、波形发生器。选型的核心不是哪个题听着有面子,而是工作量是否可控、状态是否清晰。
我一般建议优先选“有一个明确状态机”的题目,比如交通灯。红、黄、绿的切换是有限状态机,状态少、边界条件清楚、答辩时好讲。温度采集这类题看着高端,但要用 ADC0809 做模数转换,现场标定和非线性校准时非常容易陷进去——如果期末周时间紧,大概率会在误差分析里消耗掉大量精力。
选好题之后,先画一张功能拆解表,把每个需求映射到具体芯片和代码模块。比如“每秒加一”映射到 8253 通道 0 中断,“数码管动态扫描”映射到主循环的刷新任务。这张表既是工作量的估算依据,也是后面报告里“方案设计”章节的骨架。
2.2 芯片选型:接口芯片按“够用就好”配
8086 做 CPU 是课设的主流选项,不是因为性能,而是教材覆盖率最高、仿真环境最成熟、能找到的参考设计最多。配套接口芯片通常是以下组合:
| 芯片 | 作用 | 课设典型用途 |
|---|---|---|
| 8255A | 并行 I/O 扩展 | A 口输出数码管段码,B 口输出位码,C 口读按键 |
| 8253/8254 | 定时/计数 | 产生秒脉冲,控制采样频率 |
| 8259A | 中断控制器 | 多中断源管理,课设里非必需 |
| ADC0809 | 8 位 A/D 转换 | 温度、电位器采集 |
| DAC0832 | 8 位 D/A 转换 | 波形发生器 |
| 74LS373 | 地址锁存器 | 锁存 8086 低 8 位地址 |
| 74LS138 | 3-8 译码器 | 片选地址译码 |
选型逻辑是“够用就好”。8255A 有三组端口:A 口输出段码、B 口输出位码、C 口读按键,一块芯片解决输入输出,这是最常见做法。8253 三个定时器通道,通道 0 做秒脉冲,通道 1 做动态扫描节拍,不浪费也不紧张。8259A 在单中断源的课设里没必要上,一个 8253 的中断请求直接接 8086 的 INTR 就行。
2.3 工作量估算:汇编代码超过 800 行就要砍功能
汇编语言调试起来比 C 语言痛苦得多。C 语言有 printf 可以打日志,汇编基本靠单步执行和看寄存器,出问题的时候定位成本按小时算。所以选题时看一眼功能清单,估算一下代码量,超过 800 行就要考虑删减功能。
我见过一个做电子钟的组,给代码加了闹钟、秒表、整点报时三个功能,代码写到 1200 多行,最后一周几乎每天 debug 到凌晨,报告反而是最后两天赶出来的。代码量和报告质量不一定是正相关,但出 bug 的概率和代码量一定正相关。
提示:加一个功能在报告里只需要多画一张图,但在代码里是多一整套状态转移和边界条件。期末周时间很贵,选型时多做减法。
3. 硬件电路与地址译码:地址算不对,代码全白写
3.1 8086 最小系统:ALE 锁存是第一个坑
8086 要跑起来,至少需要时钟发生器、地址锁存器和存储器。课程设计里典型配置是 8086 加 74LS373 锁存地址、74LS245 做数据总线缓冲。关键点是 8086 的地址/数据复用总线:低 16 位地址在 T1 周期出现在 AD0-AD15 上,必须用 ALE 信号的下降沿把它们锁存到 74LS373 的输出端。
这一步出错最常见的原因是忘记接 ALE,或者把 ALE 接到了 74LS373 的 G 端但没接对极性。如果地址线没有锁存,数据周期里 AD 线上出现的数据会干扰地址状态,导致译码混乱、设备地址漂移。现象是仿真时设备时好时坏,或者写某个寄存器时把另一个设备误触发。
3.2 74LS138 译码与地址分配的计算方法
多个接口芯片挂在总线上,需要把地址空间切成若干片选区域。最常用的译码方案是用 74LS138,A、B、C 三个输入端接地址线,G1 接高电平,G2A、G2B 接低电平,Y0 到 Y7 各接一个芯片的片选端。
地址分配的计算逻辑:8086 有 20 根地址线 A0-A19,接口芯片是 8 位的,复用低 8 位数据线,所以一般用 A0-A4 做片内和译码寻址,A5-A19 决定片选区域基地址。以 8255A 为例,片内有 4 个端口,用 A0、A1 两位寻址;8253 也用 A0、A1 寻址 4 个寄存器;ADC0809 有 8 个模拟输入通道,需要 A0、A1、A2 三位寻址。
假设把 74LS138 的 C、B、A 端分别接 A4、A3、A2,区域基地址由 A5 以上决定。最常用的分配方式是从 0x300 开始:
| 译码输入 A4-A2 | 输出端 | 连接设备 | 地址范围 |
|---|---|---|---|
| 000 | Y0 | 8255A | 0x300-0x307 |
| 001 | Y1 | 8253 | 0x308-0x30F |
| 010 | Y2 | ADC0809 | 0x310-0x317 |
| 011 | Y3 | 备用 | 0x318-0x31F |
计算方式是:82xx 系列芯片片内只有两个地址位,所以每个片选区域的有效偏移是 0-3;ADC0809 需要三个地址位,偏移是 0-7。代码里的端口地址必须落在这些范围内。之前见过有人把 8253 的地址写成 0x309 而不是 0x308,结果访问的其实是 8255A 的 C 口,数据写到那边去,现象完全无法解释。
3.3 数据总线、读写信号和 M/IO 的接法
数据总线 D0-D7 直接连到各接口芯片的数据线,8086 的 RD、WR 信号连到芯片的 RD、WR。这里有一个容易被忽略的信号:M/IO。
8086 访问内存和访问 I/O 口时 M/IO 电平不同。接口芯片属于 I/O 设备,译码时要把 M/IO 纳入地址译码条件,否则内存访问也会触发片选,造成总线冲突。我一般会用 M/IO 控制 74LS138 的 G2A,M/IO 为低表示当前是 I/O 访问,允许译码输出;为高则屏蔽全部片选。这样内存访问和 I/O 访问彻底隔离。
另一个实物搭电路才会遇到的坑是上拉电阻。仿真环境里不一定暴露,但实机上数据总线在 CPU 不驱动的间隙会悬空,接口芯片可能读到随机电平。在数据线上加 10k 欧姆上拉电阻是常见做法,尤其是按键输入这类读取静态电平的场景。
提示:电路图画完之后,花十分钟做一次“地址走查”。从 CPU 地址线出发,经过锁存器、译码器、片选端,手写出每个设备的全部有效地址范围,再逐一和代码里的端口地址对照。这一步能提前发现绝大多数“代码跑飞”的问题。
4. 软件设计与汇编代码框架:从流程图到寄存器初值
4.1 主程序三段式:初始化、主循环、中断服务
课设软件的典型结构是三段式:系统初始化、主循环调度、中断服务。初始化部分对 8255A 写入工作方式控制字、对 8253 写入计数初值、设置中断向量表;主循环做查询式任务,比如按键扫描和数码管刷新;中断服务负责定时任务,比如秒计数。
一个常见的误区是让主循环做所有事,中断里只置一个标志位。这样做在简单课设里可行,但主循环里如果有一个查询卡住了,标志位得不到及时处理,系统响应就会延迟。合理的做法是中断里完成轻量级状态更新,主循环只做决策和输出。下面以 8255A 和 8253 的初始化为例。
4.2 8255A 工作方式控制字与初始化代码
8255A 的 A 口设为方式 0 输出,B 口方式 0 输出,C 口低 4 位输入、高 4 位输出,控制字为 0x80。把控制字写入控制端口,然后就可以直接对 A 口和 B 口读写。
INIT_8255: MOV DX, PORT_CTRL ; 控制端口地址 = 8255A基地址 + 3,即 0x306 MOV AL, 80H ; 方式控制字:10000000B OUT DX, AL ; 写入控制端口 MOV DX, PORT_A ; A口地址 0x300 MOV AL, 0FFH ; 段码全置1,对应共阴数码管全灭 OUT DX, AL MOV DX, PORT_B ; B口地址 0x302 MOV AL, 00H ; 位码全0,不选中任何一位 OUT DX, AL RET代码逻辑很简单:先写控制字,再对端口做初始输出。0x80 拆开看是 10000000B,最高位 1 表示“这是方式控制字”,A 口方式 0 输出(位 6-5 为 00,位 4 为 0),B 口方式 0 输出(位 2 为 0),C 口高 4 位输出、低 4 位输入(位 3 和位 0 的组合)。如果初始化后输出不对,先确认控制字是不是写到了控制端口而不是数据端口,这是最常见的低级错误。
4.3 8253 定时初值计算与中断服务
8253 工作在方式 3(方波发生器)产生秒脉冲。假设输入时钟是 2MHz,要产生 1 秒周期,计数初值是 2,000,000。但 16 位计数器最大值是 65,535,放不下,所以需要两级级联:通道 0 先分频到 1kHz,通道 1 再分频到 1Hz。每个通道的初值分别是 2000 和 1000,总的分频系数 2,000,000。
INIT_8253: MOV DX, PORT_TIMER_CTRL ; 8253控制端口地址 0x30E MOV AL, 36H ; 通道0,方式3,先写低字节再写高字节 OUT DX, AL MOV DX, PORT_TIMER0 ; 通道0数据端口 0x308 MOV AX, 2000 ; 初值 = 2MHz / 1kHz = 2000 OUT DX, AL ; 先写低字节 MOV AL, AH OUT DX, AL ; 再写高字节 RET初值计算是答辩高频问题。通用公式:初值 = 输入时钟频率 / 期望输出频率。两级级联时,总初值是各级初值的乘积。这里 2MHz 先除 2000 得到 1kHz,1kHz 再除 1000 得到 1Hz。如果把 2,000,000 直接写进 16 位寄存器,结果是溢出截断,计数器初值变成一个无意义数字,秒脉冲完全不对。
中断服务程序里做的事情要尽量少。秒中断里只做一个操作:内存里的秒计数器加一。主循环检测到秒值变化后再刷新数码管、判断闹钟等任务。这样中断处理时间非常短,不会影响下一次中断触发。
TIMER1_ISR: PUSH AX MOV AX, CS:[SEC_CNT] ; 秒计数器 INC AX MOV CS:[SEC_CNT], AX ; 写回 MOV AX, 20H ; 8259A 中断结束命令 OUT 20H, AL POP AX IRET这里用内存变量传递计数结果,主循环在读这个变量之前要先关闭中断,防止读到半更新状态。8086 的 INC 和 MOV 组合不是一个原子操作,主循环那边读的时候如果中断恰好发生,可能读到未完成的叠加值。这个细节在答辩时主动讲出来,会明显加分。
4.4 数码管动态扫描与按键消抖
数码管动态扫描利用视觉暂留逐位点亮,刷新频率大于 50Hz 才不闪烁。8 位数码管,每位点亮 1-2ms,一轮扫描 8-16ms,对应约 60-125Hz。
SCAN_LOOP: MOV BX, OFFSET DIGIT_BUF ; 显示缓冲区,存放各位置数值 MOV CL, 08H ; 扫描 8 位 MOV AL, 0FEH ; 位选初值 11111110B,选中最低位 NEXT_DIGIT: MOV DX, PORT_B ; B 口输出位选 OUT DX, AL MOV SI, BX MOV DI, OFFSET SEG_TABLE ; 共阴数码管段码表 XLAT ; AL = [SEG_TABLE + AL] MOV DX, PORT_A ; A 口输出段码 OUT DX, AL CALL DELAY_1MS ; 保持点亮 1ms SHL AL, 1 ; 位选左移,选中下一位 INC BX DEC CL JNZ NEXT_DIGIT RET段码表是拿来即用模块。共阴数码管 0-9 的段码依次是 0x3F、0x06、0x5B、0x4F、0x66、0x6D、0x7D、0x07、0x7F、0x6F。显示乱码先核对段码是不是共阴,再检查组端口接线和代码里的端口地址是否一致。如果用了共阳数码管,段码要全部取反。
按键消抖分硬件和软件两种。硬件在按键两端并联 0.1uF 电容;软件在检测到按下后延时 10ms 再检测一次,确认仍是低电平才判定有效。延时 10ms 不是拍脑袋,机械按键的抖动期通常在 5-10ms,采样点选在抖动结束后才有意义。
注意:软件延时尽量用 8253 的通道 1 产生 1ms 中断做基准,不要依赖循环次数估算。仿真环境里时钟频率锁定,循环延时可以算准;实机上温度影响晶振,误差无伤大雅。但如果用软件延时同时做键盘扫描和数码管刷新,两者会互相干扰,出现“按键不灵”或“显示闪烁”。
5. 报告写作与答辩自查:三个比代码更值钱的细节
5.1 时序图比代码截图更有说服力
课设报告里,代码截图是最不值钱的附件。老师想看的是时序图:8253 的 OUT 信号什么时候翻转变低、8255A 的片选和读写信号如何配合、ADC0809 的 START 与 EOC 的先后关系。画时序图可以用 Excel 画波形,或者用 Visio 画交叉信号,关键是把时间轴对齐,标出信号变化的因果关系。
一张有效的时序图是“8253 秒脉冲中断时序图”:标出计数器初值、计数递减过程、OUT 引脚翻转点、CPU 响应中断的延迟窗口。这张图画清楚,整个系统的工作逻辑就立住了。画图时要标注信号极性,低有效就画低有效,别把时序图画成逻辑关系图。
5.2 流程图按“一个模块一张”的粒度画,且必须和代码对应
流程图不是系统架构图,不要一张图画到底。主程序一张、按键扫描一张、定时器中断一张,模块粒度控制在 20 个框以内。画的时候遵守基本规范:菱形判断、圆角矩形起止、矩形处理,箭头方向统一。
流程图和代码的一致性比画工更重要。答辩时老师会指着流程图的某个分支让你找对应代码,如果找不到,或者流程图画的是“想象中的逻辑”,会被认为报告和实现脱节。我在写报告时习惯把大段的代码注释同时写进图侧,保证流程图里每一个判断框都能映射到具体的 CMP 和 JNZ 指令。
5.3 答辩前完成三个自查
我做课设评委时几乎必问三个问题。第一,某个端口地址的地址线怎么来的,比如基地址 0x300 是哪些地址线参与译码得出的,现场讲一遍 A19 到 A0 的推导过程。第二,8253 初值为什么是这个数,如果时钟改成 1MHz 初值怎么算。第三,按键按下到系统响应,中间经过哪几段代码,要求你在代码里把路径指出来。
按这个思路做答辩准备:端口地址推导放一页,初值计算过程放一页,按键响应路径放一页,各配一张小图。调试记录也是加分项,记录两个真实出现过的 bug 和定位过程,比空谈思路更有说服力。如果条件允许,在 Proteus 里跑一段波形录屏放进附件,答辩时直接放,效果远好于口头描述一句“系统运行正常”。
本文还有配套的精品资源,点击获取