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计时和报警功能,咱们不见不散!

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐