基于C程序的51单片机秒表计时器:数码管显示,Proteus仿真演示视频,包含启动、暂停与清除功能
所以TH0和TL0的初始值其实是65536-10000的十六进制,老司机都懂这里要留神计算器算劈叉了。烧录测试时翻过两个坑:Proteus里数码管共阴共阳设置反了直接导致显示全乱,还有按键消抖时间没调好导致偶尔抽风。实际调试中发现,如果放在定时器中断里处理会更优雅,但考虑到新手理解成本,先这么写着。项目需求简单粗暴——三个功能键控制,四位数码管显示,精度到0.1秒。这里有个隐藏优化点——用flag
51单片机秒表计时器(一) C程序、proteus仿真、报告、仿真演示视频! 支持数码管显示计时时间 支持按键设置启动、暂停、清除
刚焊完电路板的手指还带着松香味,咱们直接开撸这个51单片机秒表。项目需求简单粗暴——三个功能键控制,四位数码管显示,精度到0.1秒。先上效果图镇楼(此处脑补仿真动图)
核心代码里藏着几个魔鬼细节,先说定时器配置这块硬骨头:
void Timer0_Init()
{
TMOD &= 0xF0; // 定时器0模式1
TL0 = 0x66; // 10ms定时初值
TH0 = 0xFC;
ET0 = 1; // 开定时器中断
TR0 = 1; // 启动定时器
}
这配置看着平平无奇?注意这里初值计算有讲究。12MHz晶振下,机器周期1μs,要凑满10ms需要10000个周期。所以TH0和TL0的初始值其实是65536-10000的十六进制,老司机都懂这里要留神计算器算劈叉了。
数码管动态扫描是个技术活,直接上代码:
void Display()
{
static u8 pos=0;
P2 = 0xFF; // 消隐
switch(pos){
case 0: P0=smgduan[time%10]; P2=0xFE; break; // 个位
case 1: P0=smgduan[time/10%6]; P2=0xFD; break; // 十位带60进制
case 2: P0=smgduan[time/60%10]|0x80; P2=0xFB; break; // 秒个位带小数点
case 3: P0=smgduan[time/600%10]; P2=0xF7; break; // 秒十位
}
if(++pos>3) pos=0;
}
这里有个骚操作——第三位数码管的小数点控制。用或运算0x80直接点亮DP段,比查表法省了一个数组空间。注意switch里的pos变量必须用static修饰,否则每次调用都会重置位置。
按键处理是另一个重灾区,来看扫描函数:
void KeyScan()
{
if(K_START==0){
DelayMs(10);
if(K_START==0){
TR0=1; // 启动计时
flag_pause=0;
}
while(!K_START); // 松手检测
}
// 暂停和清零逻辑类似
}
这里的松手检测while循环是个双刃剑。好处是能防止长按重复触发,坏处是会阻塞主程序。实际调试中发现,如果放在定时器中断里处理会更优雅,但考虑到新手理解成本,先这么写着。
51单片机秒表计时器(一) C程序、proteus仿真、报告、仿真演示视频! 支持数码管显示计时时间 支持按键设置启动、暂停、清除
主程序骨架其实简单得感人:
void main()
{
Timer0_Init();
EA=1;
while(1){
if(flag_10ms){ // 10ms标志位
flag_10ms=0;
KeyScan();
}
Display();
}
}
这里有个隐藏优化点——用flag_10ms替代直接处理,避免在中断里做耗时操作。实测发现,若在中断里处理按键,数码管会出现肉眼可见的闪烁。
最后上个数据结构全家福:
unsigned long time=0; // 计时变量
bit flag_10ms=0; // 10ms标志
bit flag_pause=1; // 暂停标志
注意time变量必须用unsigned long,否则最大只能记到6553.5ms(别问我是怎么知道的)。flag_pause用位变量省内存,51内核的位寻址区不用白不用。
烧录测试时翻过两个坑:Proteus里数码管共阴共阳设置反了直接导致显示全乱,还有按键消抖时间没调好导致偶尔抽风。后来在报告里专门写了章硬件调试血泪史,这里篇幅有限就不展开了。
完整工程已打包(报告+源码+仿真文件),需要的老铁评论区自取。下期预告:给这个秒表加上lap计时和报警功能,咱们不见不散!

更多推荐



所有评论(0)