简介:一份基于AT89C51单片机的自动打铃系统课程设计完整文档,面向电子、嵌入式及相关专业学生,适用于单片机课程设计、实训报告和答辩参考。文档从需求分析到实现细节均有覆盖:采用24小时制计时与LED数码管显示,通过6个PNP三极管驱动降低成本并提升稳定性;支持按键校时、分钟增减、秒清零、强制打铃和关闭打铃,并按照设计任务实现上午6:00起床铃与下午10:30熄灯铃的自动打铃安排。资源包共1个文件,为doc格式,大小314KB,包含系统总体设计、硬件电路、软件流程图、系统调试与测试结果分析以及汇编程序清单,适合对照学习定时器中断、按键扫描和C语言编程,可帮助理解单片机最小系统常见模块的设计思路。已有2013人学习下载,是一份实操性较强的课程设计参考资料。
1. 为什么教学楼打铃还离不了一颗老 C51
如果你在 2010 年前后做过单片机课程设计,大概率见过这样一幕:一块洞洞板、一个 AT89C51、几只数码管、一个蜂鸣器,就构成了整个教学楼的作息控制系统。这个自动打铃系统看起来简单,但它把 51 单片机课程里最核心的几块东西全部串起来了:定时器中断做时基、动态扫描驱动数码管、按键扫描做调时、时间比较控制蜂鸣器动作。系统的核心是 AT89C51,用 6 只 PNP 三极管做位选驱动,配合蜂鸣器和 4 个按键,实现 24 小时制电子钟和定时打铃。对于正在做课程设计的学生来说,这套方案的价值在于:代码量不大,硬件成本低,调试手段直观,而且每一部分都能在开发板上独立验证。这篇博文把硬件电路选型、汇编与 C51 的软件实现、调试步骤和几个时间精度坑一次说透,照着做能直接复现。
2. 硬件电路设计:动态扫描、PNP 驱动与蜂鸣器网络
2.1 AT89C51 的资源分配与最小系统
AT89C51 是 ATMEL 在 MCS-51 基础上做的 Flash 版本,4KB 可编程 Flash、128B RAM、两个 16 位定时器、5 个中断源,这些资源在今天看来不算多,但做电子钟绰绰有余。整个系统的引脚分配是关键,规划不好后面写代码处处别扭。
本设计中各功能模块的 IO 分配如下表:
| 功能 | 引脚 | 说明 |
|---|---|---|
| 数码管段选 | P0.0 - P0.7 | 输出字型码,必须外接上拉电阻 |
| 数码管位选 | P2.4 - P2.7 | 通过 PNP 三极管控制共阴数码管位 |
| 调整选择键 SET_KEY | P1.0 | 每按一次切换调时/调分/调秒状态 |
| 增加键 ADD_KEY | P1.1 | 选中位加 1,长按进入快进 |
| 减少键 DEC_KEY | P1.2 | 选中位减 1,长按进入快进 |
| 蜂鸣器 BEEP | P3.7 | 低电平驱动 PNP 三极管导通 |
| 晶振 | X1、X2 | 12MHz,配合 30pF 负载电容 |
最小系统包含三部分:复位电路(10uF 电容 + 10k 电阻,RST 高电平复位)、晶振电路(12MHz 晶振和两个 30pF 电容)、EA 引脚处理。注意 EA 必须接高电平,否则单片机启动后会从外部程序存储器取指令,而本设计没有外扩 ROM,程序固化在片内 Flash,EA 悬空或接地都会导致系统跑飞。
P0 口是个特别容易踩坑的地方。P0 内部没有上拉电阻,作为准双向 IO 口使用时,输出高电平能力极弱,必须外接 10kΩ 排阻到 VCC。本设计把 8 位数码管的段选全部挂在 P0 上,如果省掉上拉排阻,数码管会出现亮度不均、低亮度段闪烁的现象。
2.2 数码管动态扫描:为什么用 6 只 PNP 三极管
LED 数码管显示有两种驱动方式:静态显示和动态扫描。静态显示每个数码管占用独立的锁存器或 IO 口,8 位数码管需要 8 个锁存器,成本高。动态扫描则利用人眼视觉暂留效应,同一时刻只点亮一位数码管,轮流扫描所有位,扫描频率高于 50Hz 时人眼看到的是所有位同时点亮。
本设计采用 6 个 PNP 三极管(型号 BU208 或 8550)做位选驱动,段选信号从 P0 口直接输出。三极管的接法是:发射极接 +5V,集电极接数码管公共端(共阴数码管的公共端其实接的是三极管集电极,通过三极管把 5V 送到位选端),基极通过电阻接单片机位选引脚。这个连接里有个细节:数码管如果是共阴极,公共端应该接地,但这里用 PNP 三极管把 +5V 送到公共端,那是共阳极接法。附录的仿真图里 RP1 是排阻,LS1 是扬声器,Q1 是 BU208,实际做板子时建议用 8550,电流驱动能力 500mA,导通压降比 BU208 小,数码管亮度更均匀。
动态扫描的关键参数是刷新率。6 位数码管,每一位点亮时间约 1ms,一轮扫描 6ms,刷新率约 166Hz,远高于 50Hz 的闪烁阈值,肉眼看不出闪烁。如果每位点亮时间太短,亮度不够;太长,刷新率低于 50Hz 会看到明显的抖动。位选切换时必须先关闭当前位(送消隐码),再开通下一位,否则会产生拖影。附录里的 DISPLAY 子程序用 MOV P0,A 送字型码、CLR P2.x 打开位选、LCALL DELAY 延时 1ms,这个顺序不能颠倒。
2.3 蜂鸣器驱动电路与按键输入网络
蜂鸣器部分用的是压电式蜂鸣器,工作电压 1.5 - 15V,内部有多谐振荡器,通电即发出 1.5 - 2.5kHz 的音频信号。压电蜂鸣器的驱动电流很小,理论上可以直接用单片机引脚驱动,但为了声音响度和保护引脚,通常加一级三极管放大。
本设计的驱动电路:P3.7 通过 100Ω 电阻接 PNP 三极管基极,三极管发射极接 +5V,集电极接蜂鸣器一端,蜂鸣器另一端接地。初始化和关闭铃声时 SETB BEEP 输出高电平,PNP 管截止,蜂鸣器不响;打铃时 CLR BEEP 输出低电平,三极管导通,蜂鸣器得电发声。这里的低电平有效和常见的 NPN 驱动(高电平有效)正好相反,写代码时要注意。
按键输入更直接:4 个按键一端接地,另一端分别接 P1.0 - P1.3,P1 口内部有上拉电阻,按键未按下时读回高电平,按下时读回低电平。这种接法省掉了外部上拉电阻,但要注意 P1 口内部上拉只有约 30kΩ,按键线比较长时容易受干扰,建议在按键两端并 0.1uF 电容做硬件消抖。附录源码里 KEY_SCAN 子程序返回键值,主程序用 JZ MAIN 判断无键按下,这里用了软件消抖的思路,实际是扫描到低电平后延时 10ms 再读一次,确认电平稳定才认为是有效按键。
2.4 数码管字型码表与段选信号计算
七段数码管内部是 8 个 LED(7 段 + 小数点),共阴极接法是把所有 LED 的阴极连在一起接地,阳极分别接驱动引脚;共阳极则相反。本设计使用的字型码表是共阴极的:
| 显示字符 | dp g f e d c b a | 共阴字型码 | 共阳字型码 |
|---|---|---|---|
| 0 | 0 0 1 1 1 1 1 1 | 0x3F | 0xC0 |
| 1 | 0 0 0 0 0 1 1 0 | 0x06 | 0xF9 |
| 2 | 0 1 0 1 1 0 1 1 | 0x5B | 0xA4 |
| 3 | 0 1 0 0 1 1 1 1 | 0x4F | 0xB0 |
| 4 | 0 1 1 0 0 1 1 0 | 0x66 | 0x99 |
| 5 | 0 1 1 0 1 1 0 1 | 0x6D | 0x92 |
| 6 | 0 1 1 1 1 1 0 1 | 0x7D | 0x82 |
| 7 | 0 0 0 0 0 1 1 1 | 0x07 | 0xF8 |
| 8 | 0 1 1 1 1 1 1 1 | 0x7F | 0x80 |
| 9 | 0 1 1 0 1 1 1 1 | 0x6F | 0x90 |
段选码从高位到低位是 dp g f e d c b a,P0.0 接 a 段,P0.7 接 dp。计算字型码时要特别注意位序,很多初学者的错误是把 0x3F 和共阳的 0xC0 搞混。本设计附录中的表格给出了两种码,实际工程中只需选一种,硬件是共阴就用 0x3F 那列,是共阳就用 0xC0 那列,不能混用。
3. 定时器 0 中断与按键调时的软件实现
3.1 定时器初值计算与 1 秒时基的建立
计时精度是整个打铃系统的核心,而精度的基础是定时器初值和中断服务程序的执行效率。AT89C51 的定时器 0 工作在方式 1,是 16 位计数器,最大计数值 65536。晶振 12MHz,机器周期为 12 个时钟周期,即 1MHz 计数频率,每个计数脉冲 1us。
设定时 50ms,需要计数值为 50000,初值 = 65536 - 50000 = 15536 = 0x3CB0。这就是附录程序里 TH0 = 3CH、TL0 = 0B0H 的来历。中断服务程序每次进入后要重新装载初值,否则计数器从 0 开始数,定时时间变成 65.536ms。
1 秒时基用软件计数实现:定义 20H 单元做 50ms 计数,每进一次中断加 1,加到 20 表示 1 秒到。这个方案简单可靠,但注意中断服务程序里有重装初值、时间累加、打铃比较等操作,执行时间本身会占用定时周期。假设中断服务程序最长执行 50us,50ms 周期内占比只有 0.1%,对计时精度影响很小,可以忽略。如果中断里写太多无关代码,比如数码管扫描,那就要重新计算了。
定时器 0 的初始化在 START 段完成:
MOV TMOD, #01H ; 定时器0方式1,16位定时器 MOV TH0, #3CH ; 高8位初值,定时50ms MOV TL0, #0B0H ; 低8位初值 SETB TR0 ; 启动定时器0 MOV IE, #82H ; EA=1, ET0=1,开总中断和定时器0中断TMOD = 01H 的含义:低 4 位控制定时器 0,M1M0 = 01 表示方式 1;高 4 位控制定时器 1,保持 0。IE = 82H 是二进制 10000010,即 EA = 1 开总中断,ET0 = 1 开定时器 0 中断,其他中断全部关闭。定时器 1 在本设计中没有使用,但 TMOD 高 4 位要清零,避免上电随机值干扰。
3.2 中断服务程序:BCD 加法与时分秒进位
中断服务程序是系统的"心跳"。每次中断要做四件事:重装初值、产生 0.5 秒闪烁基准、产生 1 秒时基、更新时分秒寄存器。时分秒寄存器分别用 21H(秒)、22H(分)、23H(时)存放,以 BCD 码格式存储,即一个字节的高 4 位和低 4 位各表示一位十进制数,比如 59 秒存为 0x59,而不是 0x3B。
为什么用 BCD 码?因为后面要直接送数码管显示,BCD 码可以免去二进制转十进制的除法运算。附录里的加法做了 DA A 十进制调整:
ADD_TIME: INC 20H MOV A, 20H CJNE A, #20, RETI1 ; 不满20次中断则直接返回 MOV 20H, #00H ; 满20次,1秒到 MOV A, 21H ADD A, #01H DA A ; BCD调整 MOV 21H, A CJNE A, #60H, RETI1 ; 不等于60秒则返回 MOV 21H, #00H ; 60秒清零,分加1DA A 指令的执行条件是 A 的低 4 位大于 9 或 AC 标志为 1,这时加 6 调整;高 4 位大于 9 或 CY 标志为 1,则加 60H 调整。做完 ADD 后必须紧跟 DA A,中间不能插入其他影响标志位的指令。CJNE A, #60H 比较后如果相等则 Z 标志置位,但程序里没有用 Z 标志,而是利用 CJNE 不相等时跳转 RETI1,相等时顺序执行清零。这里有个隐含逻辑:CJNE A, #60H 实际上是比较 A 与 60H,相等则继续往下走,不相等就跳走,和一般理解的"等于跳转"正好相反,初看源码容易绕晕。
分和时的进位逻辑完全一样:分加满 60H 清零,时加 1;时加满 24H 清零。注意这里的 24H 是十六进制数 0x24,代表 24 点,写成十进制就是 24,因为 BCD 码的高 4 位为 2、低 4 位为 4,恰好和十进制 24 的 BCD 表示一致。
3.3 按键扫描与调时状态机
4 个按键的功能分配是:SET_KEY(P1.0)切换调整对象,ADD_KEY(P1.1)加数,DEC_KEY(P1.2)减数,第四键强制打铃。调时状态由 R1 寄存器管理:
- R1 = 00H:正常走时模式
- R1 = 01H:调整小时,小时位闪烁
- R1 = 02H:调整分钟,分钟位闪烁
- R1 = 03H:调整秒,秒位闪烁
每次按下 SET_KEY,R1 加 1,到 03H 后下一个循环回到 00H。这个状态机的核心好处是主程序结构清晰,按键处理只依赖 R1 的值,不需要一堆标志位。
闪烁效果靠 24H 单元产生 0.5 秒基准:中断服务程序里 INC 24H,等于 10 时(10 × 50ms = 0.5s)翻转 48H 标志并清零 24H。显示子程序里判断,如果当前处于调时状态且 48H 为 1,就把对应位的显示缓冲设为 0AH,查表得到 0x40(共阴数码管全灭),实现该位熄灭半秒的效果。
按键扫描的汇编实现是轮询方式:
KEY_SCAN: MOV P1, #0FFH ; P1全部写1,准备读输入 MOV A, P1 CPL A ; 取反,按下为1 ANL A, #0FH ; 只保留低4位 JZ NO_KEY ; 低4位全0,无按键 LCALL DELAY10MS ; 延时消抖 MOV A, P1 CPL A ANL A, #0FH ; 再读一次 JZ NO_KEY ; 此时确认有键按下,继续处理CPL A 取反后按下对应的位为 1,ANL A, #0FH 屏蔽高 4 位(高 4 位没有按键),结果为 0 说明没有键按下。这个扫描方式是低电平有效,但代码里两次取反,实际读到的有效键值是高电平逻辑,理解时注意转换。
长按快进的实现用了两个标志位:46H 表示是否处于长按状态,47H 用于产生脉冲。中断服务程序里 CPL 47H 每 50ms 翻转一次,主程序检测到 46H 为 1 且 47H 从 0 变 1 时执行一次加减。这样长按 ADD_KEY 时大约每 100ms 加 1 分钟,比按一下加 1 快得多。这个设计的关键是区分"单击"和"长按",需要在识别到键按下后延时一段时间再判断键是否仍然按下。
3.4 数码管动态扫描子程序
显示部分的核心是查表送段码和位选切换。显示缓冲用 2AH - 2FH 共 6 个单元,分别存放时十位、时个位、分十位、分个位、秒十位、秒个位。显示子程序逐个取出缓冲值,查字型码表送给 P0,再打开对应的位选:
DISP: ; 显示秒个位 MOV DPTR, #TABLE MOV A, 2FH MOVC A, @A+DPTR ; 查表得到字型码 MOV P0, A CLR P2.7 ; 打开秒个位 LCALL DELAY1MS SETB P2.7 ; 关闭秒个位,消隐 ; 显示秒十位 MOV A, 2EH MOVC A, @A+DPTR MOV P0, A CLR P2.6 LCALL DELAY1MS SETB P2.6 ; 剩余时分位逻辑相同,省略MOVC A, @A+DPTR 是从程序存储器查表的指令,TABLE 是字型码表的起始地址。P0 先送段码,再 CLR P2.x 打开位选,延时 1ms 后 SETB P2.x 关闭。这段顺序不能反:如果先开位选再送段码,当前位会短暂显示上一轮的字型码,造成拖影。这个拖影在静态看不太出来,但用手快速晃动板子能看到明显的残留。
4. 打铃逻辑、系统调试与常见故障排除
4.1 打铃比较逻辑:BIJIAO1、BIJIAO2 与铃声控制
打铃功能是把当前时分的 BCD 码和预设的作息时间表做比较,相等则触发蜂鸣器动作。系统预设了两类铃声:起床/熄灯铃(方式 1:响 5 秒、停 2 秒、再响 5 秒)和上下课铃(方式 2:短促铃)。设计文档里给出了两个具体时间点:上午 6:00 起床铃、下午 22:30 熄灯铃,实际使用可以改时间表。
比较子程序的逻辑并不复杂,但有一个细节需要注意:时间的比较必须只比较时和分,不能比较秒。因为秒在 0 - 59 之间跳动,如果条件写成"当前时分秒等于 06:00:00",那只有在 6 点整的那一秒才会触发打铃。本设计用 BCD 码存时间,比较时直接比较 22H(分)和 23H(时)两个字节,秒不参与比较,这样 6:00:00 到 6:00:59 的一分钟内都会满足触发条件。
响铃方式 1 的汇编实现利用了 R3 做打铃时长计数:
; 时段控制说明 ; 打铃持续5秒:从触发开始累计100次0.5秒 ; 停2秒:累计40次0.5秒后停止 ; 再响5秒:再次累计100次具体代码里 R2 是强制打铃标志、R3 是打铃时长计数。定时器中断里CJNE R2, #01H, ADD_M判断是否正在强制打铃,是则 INC R3 累计时间。主程序里的 DALING1 和 DALING2 子程序根据 R3 的值切换蜂鸣器状态。这里要理解的是:打铃不是死循环等待 5 秒,而是在中断服务程序里让 R3 计数,主程序轮询判断什么时候该响、什么时候该停,这样整个系统在打铃期间还能继续显示时间和扫描按键。
强制打铃键是第四只按键,按下去直接把 R2 置 1,走 DALING1 逻辑,再按一次清 R2 关闭铃声。这个功能在调试时非常有用,不用等到设定时间点就能测试蜂鸣器是否正常。
4.2 调试流程:硬件、软件、联调三步走
系统的调试分成三步。
第一步是硬件调试,按模块逐个验证。先焊最小系统,用万用表测 VCC 和 GND 之间的电压,正常应为 5V;再用示波器看晶振引脚,应该能看到 12MHz 正弦波;最后测 RST 引脚,上电瞬间应该有一个高电平脉冲。最小系统正常后,把数码管段选全部接 P0,位选全部接 P2.4 - P2.7,写一段最简单的流水灯程序让所有数码管全亮,检查是否有缺段、亮度不均的问题。
第二步是软件调试,用 WAVE6000L 仿真器或 Keil C51 的单片机仿真功能。单步跟踪定时器中断服务程序,重点检查三个地方:TH0/TL0 重装值是否正确、DA A 执行后 21H 的值是否按 BCD 码递增、CJNE 比较是否走对了分支。Keil C51 里可以用 Peripherals 菜单打开 Timer 0 的寄存器窗口,直接观察 TH0、TL0、TF0 的变化。汇编程序在 Keil 里运行和实际单片机的行为基本一致,但要注意仿真器的时序和真实晶振有差异,定时精度要在硬件上验证。
第三步是软硬件联调,把程序烧录到单片机后上电。先按 SET_KEY 进调时状态,用 ADD/DEC 键把时间调到接近预设打铃时刻的前一分钟,观察到达打铃时间时蜂鸣器是否按 "5 秒响 - 2 秒停 - 5 秒响" 的模式工作。同时验证强制打铃键、调时闪烁功能是否正常。
4.3 五个高频故障及处理办法
根据这个系统的常见问题,整理了一张排查表:
| 故障现象 | 可能原因 | 排查方法 |
|---|---|---|
| 数码管全不亮 | P0 上拉电阻虚焊、位选三极管装反、单片机未运行 | 测 P0 引脚电压,正常时应能看到 0 - 5V 跳变 |
| 某个数码管亮度低 | 对应位选三极管 β 值不足、基极电阻过大 | 把基极电阻从 10k 换成 4.7k,或换 8550 |
| 走时偏快或偏慢 | 晶振频率偏差、定时器初值计算错误 | 用频率计测晶振输出,核对 TH0/TL0 值 |
| 按键按了没反应 | 按键接在 P1 高 4 位上、程序扫描的是低 4 位 | 对照原理图检查 KEY_SCAN 的 ANL A, #0FH |
| 蜂鸣器不响 | P3.7 电平极性接反、三极管烧毁 | 用万用表测 P3.7 电平,打铃时应为低电平 |
故障排查的通用思路是"先静后动":先把单片机从电路板上拔下来或断开电源,测各模块的静态参数;再上电测动态波形。特别是三极管驱动部分,万用表的二极管档可以测三极管的 BE 结和 BC 结是否正常,P3.7 的高低电平用万用表直流档就可以测。
4.4 Proteus 仿真的局限
很多课程设计会用 Proteus 做仿真验证,仿真通过后再焊接实物。Proteus 里 AT89C51、数码管、蜂鸣器这些元件都有现成模型,搭建电路和调试程序确实方便,但仿真有两个明显的局限。
第一个是仿真不反映真实电气特性。Proteus 里的数码管不会出现亮度不均、拖影、电源纹波导致的闪烁,这些在仿真里完全看不出来,而实物上恰恰是这些问题最折磨人。第二个是仿真里的蜂鸣器是理想模型,不会出现驱动电流不足、声音沙哑的情况。所以 Proteus 仿真通过只代表逻辑正确,不代表硬件可靠。
正确的做法是:仿真验证软件逻辑(时间走时、按键调时、打铃比较),然后一定要动手焊板子,把仿真验证不了的问题在实物上暴露出来。做过实物和只做过仿真的区别,就在这些电气细节里。
5. 提高计时精度与代码移植的两个实用技巧
基于 C51 的电子钟最大的痛点就是计时精度。12MHz 晶振下定时器初值 0x3CB0 看起来精确,实际上走一天会偏快或偏慢十几秒甚至更多,原因是晶振本身的频率误差、温度漂移以及中断响应延迟。这里给两个实际可用的修正方法。
第一个是软件补偿。假设走 24 小时偏快了 10 秒,说明系统实际计时的 1 秒比标准 1 秒短。修正思路是定期"扣掉"多余的计时脉冲:在中断服务程序里用一个补偿计数器,每隔 N 秒少计一次 1 秒基准。比如每天偏快 10 秒,就每 8640 秒(24 小时的 1/10)跳过 1 秒。具体做法是把CJNE A, #20, RETI1的比较值在特定次数改为 19,即 19 个 50ms 计为 1 秒,这样这一秒实际只有 950ms,一天扣 10 次就把偏快的时间抵消了。
第二个是改用 11.0592MHz 晶振配合定时器重装值微调。11.0592MHz 是串口通信常用的晶振,它的优势是分频后容易得到精确波特率,但用于定时器计时时初值不好算。定时器 0 方式 1 的初值计算公式是 N = 65536 - fosc × T / 12。以 11.0592MHz、定时 50ms 计算:N = 65536 - 11059200 × 0.05 / 12 = 65536 - 46080 = 19456 = 0x4C00。这个值比 12MHz 下的 0x3CB0 更规整,但实际精度取决于晶振本身的误差。要更高精度,可以做一个校准程序:用秒表掐 24 小时,记录误差秒数,反推实际的 fosc 值,再计算出修正后的初值。
最后一个实用技巧是代码从汇编移植到 C51。附录里的汇编程序功能完整,但可读性和可维护性差,课程设计如果需要用 C 语言重写,核心框架可以这样搭:
#include <reg51.h> sbit BEEP = P3^7; sbit SET_KEY = P1^0; sbit ADD_KEY = P1^1; sbit DEC_KEY = P1^2; unsigned char time_buf[6]; // 时十位、时个位、分十位、分个位、秒十位、秒个位 unsigned char sec, min, hour; bit flash_flag = 0; unsigned char flash_cnt = 0; unsigned char time_mode = 0; // 0-正常 1-调时 2-调分 3-调秒 void timer0_isr(void) interrupt 1 { TH0 = 0x3C; TL0 = 0xB0; // 重装50ms初值 flash_cnt++; if (flash_cnt == 10) { flash_cnt = 0; flash_flag = ~flash_flag; } // 秒、分、时的BCD进位逻辑同汇编 }C51 移植的注意点:interrupt 1 是定时器 0 中断号,寄存器组切换由编译器自动完成;sbit 定义可位寻址的 P3.7 和 P1.0 - P1.2;flash_flag 用 bit 类型可以节省内部 RAM。用 C 写直观很多,但生成的代码体积比汇编大。AT89C51 只有 4KB Flash,如果用了 printf、除法等库函数会迅速占满空间,建议避免使用大型库函数,显示部分保持查表方式,不要用 sprintf 格式化时间字符串再显示。查表在汇编里是 MOVC A, @A+DPTR,在 C51 里用 code 关键字把字型码表放在程序存储器:
unsigned char code table[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x40};0x40 是"全灭"的字型码(共阴数码管全灭),用于调时状态下的闪烁显示。这个表和汇编里的 TABLE 完全一致,C 语言里用 code 修饰符声明后,数据会存到 Flash 而不是 RAM,对 AT89C51 这种只有 128B RAM 的芯片来说,这一步能省下宝贵的内部存储空间。
本文还有配套的精品资源,点击获取