简介:这份文档是华南理工大学2020年微机课程设计报告,面向电子信息、计算机等专业学习微机原理与51单片机应用的学生,围绕电子日历时钟项目提供完整的设计思路与实现记录。报告以汇编语言编程为核心,涵盖北京时间显示、时间与日期校准、公历日期、秒表、闹钟及PC联机校准等功能,并讨论4位LED数码管动态扫描驱动、矩阵键盘一键多用、定时中断基准时间、闰年平年判断等软硬件方案,还给出六种显示模式与按键操作逻辑。资源包共1个doc文件,约3.89MB,为课程设计报告书正文,包含实验目的、功能要求、方案考虑、程序流程与结果分析等章节,可帮助读者理解汇编程序结构、硬件接口设计与调试思路。已有1145人学习,适合课程设计参考与单片机入门实践对照。
1. 一份 2020 年的 51 单片机课设:电子日历时钟到底能跑出什么
翻到一份 2020 年的微机课程设计报告,题目是电子日历时钟,硬件平台是 STC-10F08XE 开发板,软件全程用汇编在 μVision 里编译。第一眼看上去像是老掉牙的作业,但把源码拆开看,它其实是一个相当完整的 51 单片机综合练习:4 位 LED 数码管动态扫描、74HC595 串行驱动、矩阵键盘一键多用、INT0/INT1 双外部中断、定时器 0 做时基、蜂鸣器做闹钟,六个显示模式靠一个按键轮转。对正在找 51 单片机汇编课设参考、或者想把“定时中断 + 动态扫描 + 按键状态机”这套组合拳吃透的人来说,这份报告的价值不在报告本身,而在它附带的那套可直接编译的汇编源码和清晰的模式划分。下面按“它是什么、怎么复现、哪里会翻车”的顺序拆。
2. 硬件与软件方案拆解:为什么是 595 + 动态扫描 + 双中断
2.1 显示方案选型:4 位 LED 数码管为什么走 74HC595 串行驱动
报告里明确写了显示器驱动可选静态或动态扫描,最终代码走的是动态扫描,并且段码通过 74HC595 串行输入。这个选择在 51 单片机上很典型:4 位数码管如果静态驱动,每位要 8 根段线加 1 根位线,IO 根本不够;动态扫描把 4 位的段线并联,靠位选轮流点亮,IO 需求降到 8 段 + 4 位。而 595 的作用是把 8 位段码从 3 根线(SER 数据、SRCK 移位时钟、RCK 锁存时钟)串行送出去,进一步省 IO。
源码里的引脚定义值得抄下来对照开发板:
RCK BIT P0.5 ; 595 锁存时钟 SRCK BIT P0.4 ; 595 移位时钟 SER BIT P0.7 ; 595 串行数据 RST BIT P0.6 ; 595 复位端,程序中 CLR RST 保持不复位 BUZZER BIT P1.3 ; 蜂鸣器这里有个容易忽略的点:RST在初始化里被CLR RST拉低,注释写的是“不能让他复位”。595 的复位是低有效还是高有效取决于具体型号,代码按低有效处理,如果你换的板子 595 复位极性相反,数码管会全程不亮,这是第一个要核对的硬件差异。
发送一个字节的函数是理解整块显示的关键:
SEND_BYTE: MOV BYTE,A MOV A,#80H ; 从最高位开始逐位送出 MOV R7,#9 JUDGE_R7: DJNZ R7,SEND_LOOP CLR RCK ; 8 位送完后拉低再拉高 RCK,锁存输出 SETB RCK CLR RCK RET SEND_LOOP: RL A MOV B,A ANL A,BYTE JNZ SET_SER CLR SER ... SET_SER: SETB SER ...逻辑是:把待发字节放BYTE,用#80H做掩码逐位左移比对,决定 SER 送 1 还是 0,每送一位翻转一次 SRCK,8 位送完用 RCK 的下降沿把移位寄存器内容锁存到输出端。参数上唯一要改的是R7的初值 9——它比 8 多一次,是为了让循环在送完第 8 位后还能走到锁存分支,改这个数会直接导致少送一位或锁存时机错位。
2.2 时基与中断优先级:定时器 0 的 50ms 怎么变成“秒”
报告要求基准时间必须是“秒”的整除数,且不超过 16 位定时器最长定时。代码用定时器 0 方式 1(16 位),初值TH0=03CH, TL0=0B0H,对应 50ms 溢出一次,20 次凑成 1 秒。这个 50ms 是权衡结果:再短中断太频繁,中断服务程序执行时间会吃掉精度;再长单次定时逼近 16 位上限,且秒的进位粒度变粗。
初始化里对中断优先级的设置是这份代码里比较讲究的一处:
SETB TR0 ; 启动定时器 0 SETB ET0 ; 定时器 0 中断允许 SETB EX0 ; 外部中断 0 允许(模式切换键) SETB IT0 ; 边沿触发 SETB EX1 ; 外部中断 1 允许(进入/退出校准) SETB IT1 SETB EA SETB PT0 ; 定时器 0 高优先级 SETB PX0 ; 外部中断 0 次高 SETB PT1优先级从高到低是定时器 0、外部中断 0、定时器 1、外部中断 1。为什么定时器 0 要最高?因为它是走时基准,被按键中断打断久了会丢秒。而外部中断 1 负责进入校准状态,优先级最低可以接受,因为校准是人工操作,慢一点无感。这个排序不是随便写的,改优先级会直接表现为“按着按键时间就不准了”。
2.3 六个显示模式的状态机:一个 MODE 变量撑起全部交互
整个交互的核心是内存里的MODE变量(地址 23H),取值 0 到 5,分别对应年、月日、时分、秒毫秒、闹钟、秒表。INT0 每按一次INC MODE,到 6 归零;INT1 翻转TD标志,决定矩阵键盘扫描是开启还是关闭。主循环START里用一串CJNE A,#n做分发,每个分支调各自的显示函数再跳回START。
这种写法的好处是逻辑极简、汇编里好调试;代价是主循环是阻塞式的,显示函数内部靠DELAY循环拖时间做扫描。也就是说,走时的准确性完全依赖定时器中断,主循环只负责“把当前值刷到数码管上”。理解这一点,后面排查“显示正常但时间不走”就有方向了——问题一定在定时器中断,不在主循环。
3. 从源码到开发板:编译、烧录与逐模式验证
3.1 在 μVision 里建工程并编译这份汇编
这份代码是纯汇编,没有 C 文件,工程配置和常见 C 工程不同。步骤按下面走:
- 新建工程,器件选 STC 系列对应型号(报告用的是 STC-10F08XE,μVision 里若没有该器件包,选同内核的 8051 通用型号即可,汇编不依赖器件库)。
- 添加一个
.asm源文件,把报告里的源码整段贴入,注意文件编码用 ANSI 或 GB2312,避免中文注释乱码导致汇编报错。 - 在 Options for Target 里勾选 “Create HEX File”,输出格式选 HEX-80。
- 编译,看 Build Output 里是否有
0 Error(s)。
# 若用命令行版汇编器(部分环境自带),典型调用形式 a51 source.asm # 汇编,生成 .obj bl51 source.obj # 链接 oh51 source # 转 HEX参数说明:a51是 8051 汇编器,bl51负责链接定位,oh51生成 Intel HEX。命令行方式适合批量验证,但多数人直接在 μVision 里点 Build 更省事。编译通过后,HEX 文件用开发板配套的下载工具烧录,注意烧录前确认晶振频率与代码里 50ms 初值匹配——代码按 12MHz 算的初值,换 11.0592MHz 晶振走时会偏,这是血泪经验里最常见的一条。
3.2 逐模式验证:先确认显示,再确认走时,最后确认校准
上电后默认MODE=0,显示年份 2020。验证顺序建议按依赖关系来,不要一上来就测闹钟:
| 验证项 | 操作 | 预期现象 | 失败时先查 |
|---|---|---|---|
| 显示通路 | 上电 | 4 位数码管显示 2020 | 595 复位极性、位选 P0.0~P0.3 |
| 模式切换 | 按 INT0 | 依次切到月日、时分、秒、闹钟、秒表 | INT0 消抖延时、边沿触发设置 |
| 走时 | 停在 MODE2 观察 | 分钟每分钟加一 | 定时器 0 初值、R6 计数 |
| 校准 | 按 INT1 后按 S11/S12 | 对应位加减 | 矩阵键盘扫描、TD 标志 |
| 闹钟 | 设闹钟时分为当前时间 | 蜂鸣器 3 短 1 长 | 闹钟比较逻辑、BUZZER 引脚 |
校准部分要特别注意报告里写的“一键多用”:MODE0 下 S11/S12/S15/S16 分别控制年份的千位、十位、百位、个位;MODE1 下 S11/S12 调月加减、S15/S16 调日加减;MODE2 下调时分;MODE3 下 S11/S12 调秒的十位和个位。同一组按键在不同模式下含义完全不同,验证时一定要先按 INT1 进入校准态,否则按键只会在模式间乱跳。
3.3 闹钟与秒表的独立验证
闹钟逻辑放在主循环入口,任意模式下都持续比较CLOCK_H/CLOCK_M与HOUR/MINUTE,相等就调BEEP_CLOCK响 3 短 1 长。验证时把闹钟设成当前时间加一分钟,等一分钟听响。这里有个细节:比较只到分钟,秒不参与,所以闹钟会在目标分钟的任意秒触发,属于设计取舍,不是 bug。
秒表在 MODE5,用定时器 1 计时,WATCH_MIN/WATCH_SEC/WATCH_MSEC三个变量。S11 清零、S12 开始、S15 暂停。注意源码里SHOW_WATCH开头会根据TD标志决定是否CLR TR1,也就是退出校准态时定时器 1 会被停掉,这是防止秒表在后台空跑。验证秒表时先按 INT1 进入校准态再按 S12,否则按键不响应。
4. 避坑与排查:这份课设源码最容易翻车的五处
4.1 数码管显示重影、字符发虚
现象:动态扫描时相邻位数字叠在一起,或者字符边缘发毛。原因:报告软件方案里专门提过,更新显示内容前必须先把所有数码管熄灭,否则余辉会让新旧内容叠加。源码里SEG_DISPLAY每次送新段码前都有ANL A,#0F0H把 P0 低四位清零的动作,就是在做消影。解决:如果你改代码时删掉了这几行消影,或者把DELAY的R7调得太小导致位间切换太快,重影立刻出现。把消影语句补回,位点亮延时R7保持10H左右。
4.2 时间不走或走时明显偏快偏慢
现象:显示正常,但分钟不动,或者一小时快好几分钟。原因分两类:一是定时器 0 中断没进,检查ET0/EA/TR0是否都置位;二是初值按 12MHz 算的,实际晶振是 11.0592MHz,50ms 变成约 46ms,累积起来就快。解决:确认晶振频率,若不同则重算TH0/TL0;同时检查中断服务里对R6的计数是否到 20 才进位秒,计数阈值写错会直接改变走时倍率。
4.3 按键按一下跳好几个模式
现象:按一次 INT0,MODE 从 0 直接跳到 2 甚至更多。原因:机械按键抖动,边沿触发下多次进入中断。源码在PRO_INT0里有MOV R7,#10 / LCALL DELAY / JB P3.2,INT0R的消抖,但延时长度和按键质量相关。解决:加大消抖延时,或在中断里先关EX0、处理完再开;另外确认IT0是边沿触发而非电平触发,电平触发会持续进中断。
4.4 进入校准后按键无反应
现象:按 INT1 后按矩阵键,数值不变。原因:TD标志没正确翻转,或者KEYSCAN1扫描的端口与开发板矩阵键盘实际接线不一致。源码里PRO_INT1对TD做INC,TD=1才调KEYSCAN1。解决:先用万用表或示波器确认矩阵键盘行线列线接在哪些 P 口,对照KEYSCAN1里的扫描码;再单步确认TD在按键后确实变成 1。
4.5 闹钟到点不响或一直响
现象:设了闹钟不响,或者蜂鸣器长鸣不停。原因:不响多半是CLOCK_H/CLOCK_M与当前时分比较的分支写反,或BUZZER引脚极性与板子不符(源码SETB BUZZER是关、CLR BUZZER是响,属于低电平驱动);一直响则是比较相等后没有退出条件,主循环每轮都重新触发。解决:核对蜂鸣器驱动极性,把比较逻辑改成“相等才响、响完继续走时”,确保不会在同一分钟内反复触发。
提示:以上五条里,前两条几乎占了这份课设调试时间的大头,先把显示和走时钉死,再碰按键和闹钟,顺序反了会互相干扰判断。
5. 把这份课设当模板用:三个可复用的改造点
这份 2020 年的报告放到今天,直接照抄交作业意义不大,但它的骨架很适合当 51 汇编的模板去改。第一个改造点是时基精度:把 50ms 定时改成用定时器 2 的自动重装,或者干脆换 11.0592MHz 晶振重算初值,走时误差能压下来一截,这是从“能跑”到“跑得准”的分水岭。第二个是显示刷新与走时解耦:现在主循环阻塞在DELAY里,按键响应和显示刷新互相牵制,可以把扫描拆进定时器中断,主循环只做状态判断,响应会跟手很多。第三个是闹钟比较加上秒参与,避免同一分钟内重复触发,顺手把“3 短 1 长”的节奏参数化成一个表,改铃声不用动逻辑。
验证改造是否成功,我一般会盯两个指标:连续跑 24 小时看累计误差多少秒,以及快速连按 INT0 二十次看 MODE 是否严格按 0→1→2→3→4→5→0 循环不跳号。这两个都过了,说明时基和按键状态机是干净的。从那以后我每次拿这类课设源码当底子,都强制先把“显示消影、定时初值、按键消抖”这三件事单独验一遍再往下加功能,因为后面所有花活都架在这三块地基上,地基歪了越改越乱。希望这份拆解帮到你,少走几段我当年绕过的弯路。
本文还有配套的精品资源,点击获取