H43基于51单片机秒表proteus设计,keil程序,c语言,AT89C51
作者:单片机仿真   类别:51单片机仿真    日期:2026-11-10 12:55:04    阅读:10 次   消耗积分:0 分

H43 用一片 51 单片机做两位数码管秒表:0.1 秒精度靠定时器 10ms 中断累计十次,K1 一键启动/暂停/继续,K2 清零,暂停时定时器照跑只是不采纳结果,显示函数自带 10ms 节拍在主循环里动态扫描,小数点靠段码最高位清零点亮,附 Keil 程序、Proteus 仿真与设计报告。

H43基于51单片机秒表

01 设计任务

功能介绍:

使用51单片机做一个两位数码管秒表,两个按键操作,要求如下:

1、0.1 秒计时:定时器 10ms 中断,累计 10 次得到 0.1 秒,两位数码管显示"秒.十分位"(如 3.5 表示 3.5 秒);

2、一键启动/暂停:K1 键启动计时,再按一次暂停(数值保持不动),再按继续——一个键管两件事;

3、K2 清零:回到 00.0 并停住,之后按 K1 从零重新开始;

4、满 99 回绕:计时值到 99(9.9 秒)后归零,继续循环。

 特别注意:这是本系列第一个"正向计时"的设计。前面的 H27 抢答器、H29 洗衣机都是倒计时,H30/H31 是外部事件计数——它们的共同点是"从设定值往零走"或"别人给我脉冲",而秒表是"自己的时基在走,我只决定走不走"。

差别最直接体现在代码里:秒表的核心根本不是显示,是一个三态的 mode 变量(停止/计时/暂停)。全篇 106 行,显示占了 20 行,状态逻辑加起来不到 10 行——秒表 = 时基 + 状态机 + 显示,三块拼起来就这么简单。


02 硬件:两位数码管 + 两个按键

这是全系列最省的一个设计,一共就五样东西:

H43基于51单片机秒表 图2单片机秒表proteus仿真整机图
部件引脚说明
两位共阳数码管段码 P0(经 RP1 排阻上拉)、位选 P2.0/P2.1左边位显示秒的整数、右边位显示十分位
K1 启动/暂停P3.6,另一端接地靠 P3 内部上拉,按下为低
K2 清零P3.7,另一端接地同上
晶振XTAL1/XTAL2,C1/C2 22pF12MHz(定时器 10ms 用)
复位C3 10uF + R1 100Ω上电复位

两个细节看图就能确认:

一是 P0 必须上拉。 段码从 P0 出来,先过 RP1 排阻再到数码管——P0 是开漏结构,不接上拉就读不到高电平、屏幕不亮。这几乎是每篇用 P0 驱动外设时的固定动作了。

二是按键直接接地。 K1、K2 一端接 P3.6/P3.7,另一端直接到地,没有外接上拉电阻——因为 P1/P2/P3 内部有弱上拉(约几十 kΩ),按键不按时读到高电平、按下时被拉到地。P0 就没有这个待遇,所以 P0 必须外接上拉。记住这条:P0 要上拉,P1~P3 可以省。

位选用的是"两脚分别选通":S1 = P2^0、S2 = P2^1,共阳数码管的公共端接 P2(高电平选通)。两位一体、段码共用,这就是动态扫描的最小形态——比 H23 的 74HC138 位选、H37 的三极管位选都简单,因为它只有两位、电流也小。

03 0.1 秒是怎么数出来的

秒表的时基在 T0 中断里,逻辑非常直白:

定时器初始化只干四件事:把 T0 设成方式 1(16 位),把 65536 减 10000 拆进 TH0 和 TL0 当 10ms 初值,打开 T0 中断允许位,然后启动 T0 并开总中断。中断服务函数一进来先按同样的算式重装一次初值,之后按 mode 分三支:为 0 时把 Time 和 temp 一起清零;为 1 时走两级累加,Time 超过 99 就回绕成 0;为 2 时整个函数什么都不做,Time 保持原值。

两层计数是秒表的核心结构:

· 第一层 temp:数"10ms 中断了几次",满 10 次(100ms)进位;

· 第二层 Time:0.1 秒的计数,直接就是显示值(0~99)。

显示时 Time = 35 就显示 3.5——把"显示格式"和"存储格式"绑在一起,不做"秒 + 毫秒"的分离存储。这是这份程序最省事的取舍:只需要 0.1 秒精度,就没必要拆成"秒""十分位"两个变量。

精度问题值得说一句。 12MHz 晶振下,一个机器周期是 1μs,10ms 需要 10000 个机器周期,初值 65536-10000 完全正确。但中断响应本身有几个机器周期的延迟,每次重装又有一点误差——累积几十次之后,走时会比真实时间慢一点点。做秒表、时钟这类要求长时准确的设备,正规做法是:要么改用自动重装的方式 2 减小误差、要么用时钟芯片(DS1302/PCF8563)做基准。教学用的秒表,这点误差看不出来,可以不管。

04 一个按键管启动和暂停

K1 分支里用 mode 是否等于 1 当条件,两种结果分别是把 mode 赋 1、把 mode 赋 2;K2 分支不用判断,直接赋 0。

看第一行 if(mode != 1) mode = 1;——判断的是"不等于计时中",所以从"停止(0)"和"暂停(2)"两个状态按下去都会进入计时。一行代码同时实现了"启动"和"继续"两个动作。

这就是用状态而不是用动作来组织逻辑的好处。如果按"动作"思路写,就得记"上一次是什么状态、这次该干什么",还得防重复触发;而按"状态"思路写,只需要回答一个问题:按下去之后我该处于哪个状态?

三个状态各自的行为:

mode含义中断里的行为显示
0停止/已清零每一拍都把 Time、temp 清零一直 00.0
1计时中每 10 拍 Time++数值跳动
2暂停什么都不做,Time 保持不变定格

暂停的实现特别值得学:它不是"停掉定时器"(TR0=0),而是定时器照常跑、只是不加。好处是恢复时不需要重新初始化定时器、也不会有重装初值的误差;坏处是定时器白跑——对一个秒表来说完全无所谓。

如果写成"暂停就 TR0=0",恢复时得记得 TR0=1、还要重装初值,多两个易错点。"让外设一直跑,只控制要不要采纳它的结果",是比"开关外设"更稳的思路。

另外注意 mode=0 时的清零写在中断里,不是按键处理里——这意味着只要处于停止态,每一拍都在清零。这样处理有个好处:按 K2 之后哪怕 Time 因为某种原因是脏的,下一拍立刻归零,界面永远是干净的 00.0。这就是"清零是一种持续状态,不是一次性动作"。

05 显示:自带节拍的动态扫描

Display() 每调用一次就把两位各点亮 5ms,一轮 10ms:

Keil工程里Display函数代码

左位和右位各点亮 5 毫秒,两次之间夹一次全灭;左位送出的是带小数点的十位段码,右位送个位段码。

三步骤的顺序不能乱:先全灭(P0=0xFF)、再换位选、最后送段码。如果先送段码再换位选,切换的那一瞬间新一位会"继承"上一位的段码,出现鬼影(H23、H26、H30、H38 都踩过这个坑,这里是最小复现版)。

小数点怎么来的?table[num/10] & 0x7f——共阳数码管的段码最高位(bit7)是小数点 DP,& 0x7f 把它清 0,共阳是低电平点亮,小数点就亮了。所以"个位带小数点"不是加了什么器件,只是把这一位送出去的字节改了一下。同理 H38 讲过的"仅 g 段亮 = 负号(0xbf)",也是靠改字节。

这份程序最特别的地方是:显示不用定时器刷。 前面大部分篇目(H26、H27、H30、H37、H38)都是把显示扫描挂在定时器中断上,保证刷新节拍绝对均匀。这里放在了主循环里:

主循环的循环体只有三件事:取一次键号,按键处理里更新 mode,最后把 Time 交给显示函数画一遍;画完立刻回到循环开头。

因为 Display() 里面自带两个 Delay_1ms(5),一轮正好 10ms,主循环的节奏就被显示函数"带"起来了——刷新率 100Hz,人眼完全看不出闪。

这是一种"偷懒但有效"的写法,代价是:主循环被显示函数占满,按键扫描也只能每 10ms 做一次。对秒表够用(人按键不可能快过 10ms),但如果哪天要加"同时测温 + 通讯",这种写法就会成为瓶颈——那时候必须把显示挪到定时器中断里。能用主循环刷显示的前提是:主循环没有别的实时任务。

06 只有 9.9 秒?量程怎么扩

有个细节必须指出来:Time 是 uchar,if(++Time > 99) Time = 0;——最大只能显示 9.9 秒,超过就回绕到 00.0。

对课堂演示(跑个几秒)完全够,但真要做"60 秒秒表",得动三处:

1. 显示位数:两位不够了,要么加位(三位/四位数码管 + 位选扫描),要么改显示格式(MM:SS 用两位反而不够,得四位);

2. 计数变量:改成"秒 + 十分位"两个变量,或者用 uint 存"多少个 0.1 秒",显示时再拆;

3. 回绕条件:> 99 改成对上层的进位(十分位满 10 进 1 秒、秒满 60 进 1 分)。

所以看别人代码里的 > 99 这种魔法数字,要意识到它其实是量程的天花板——不是随便写的一个数,而是"两位显示能表达的最大值"。

07 排错清单

1、数码管不亮/全黑:先查 P0 有没有接 RP1 排阻上拉(P0 开漏),再查共阳数码管的公共端(位选)有没有接到 P2.0/P2.1 且为高电平;

2、显示一直是 00.0,按 K1 不动:mode 没进 1,或者进了 1 但定时器没开(TR0)。检查 Time_Init() 有没有被调用、ET0/EA 有没有置位;

3、按 K1 反而清零了:K1、K2 的引脚搞反了(K1=P3.6、K2=P3.7),或者按键一端没接地;

4、计时速度不对(偏快/偏慢):晶振频率与初值不匹配。65536-10000 是按 12MHz(1μs/机器周期)算的 10ms;用 11.0592MHz 晶振会慢约 8%;

5、数码管有鬼影/重影:消隐顺序错了,必须"先 P0=0xFF、再换位选、最后送段码"(见 05 节);

6、小数点不亮或常亮:& 0x7f 写错(写成 & 0xf7 会点到别的段),或者段码表不是共阳表(这份是 0xC0 起,共阴要换 0x3F 起那套);

7、跑到 9.9 秒后突然归零:不是 bug,Time>99 的设计上限(见 06 节),要更长就扩量程。

08 总结

秒表虽小,把三件基础功都占了:

· 时基:定时器 10ms + 软件分频到 100ms,这是所有计时类设计的地基(H27 的 30 秒倒计时、H29 的分钟倒计时都是这个套路);

· 状态机:mode 三态,一个按键管"启动/继续",判断的是状态而不是动作;

· 显示节拍:动态扫描 + 消隐顺序 + 小数点靠段码位,这套动作在这里是最小清晰版。

如果只记一句话:暂停不是"停定时器",而是"定时器照跑、不采纳结果";清零不是"按一下清一次",而是"停止态下一直清"。

下一篇继续,往更复杂的显示和协议上走。

欢迎关注,持续更新!


09 文件日期修改

如果你想把文件修改为最新的日期,又不想新建工程,那么你可以参考下面的文章。

"如何不打开程序的情况下更改proteus和keil程序的日期?"

工具和仿真文件在一起,见下面的下载链接。


10 资料下载

全套资料包含 Keil 工程(C 语言源码 + hex 文件)、Proteus 仿真工程、设计报告、程序流程图、讲解视频,以及编译截图和仿真截图。下载后可直接打开运行,也可用于课程设计、毕业设计参考。

点我下载全套资料 →

也可以关注公众号「单片机仿真」,回复 H43 获取下载入口。公众号内还有 A / T / H 全系列 400 多个单片机仿真实例,回复「目录」可查看完整清单。

同类设计合集:H 系列单片机仿真合集目录(含设计报告、视频讲解、流程图);全部系列合集总目录

版权所有,转载本站文章请注明出处:proteus单片机
上一篇:H42基于51单片机数码管双机通讯proteus设计,keil程序,c语言,AT89C51
下一篇:H44基于51单片机ADDA综合仿真proteus设计,keil程序,c语言,AT89C51
${comment['nickname']}   ${comment['createtime']}
  
       
${comment.content}
${reply.nickname} 回复 ${comment.nickname}    ${reply.createtime}
     
  
回复内容:${reply.content}