用proteus,应用mcs-51单片机设计电子跑表

1,应用mcs-51单片机设计电子跑表
2,选用4位LED数码显示,跑表计时时间0–99.99s循环,另有一键可循环控制暂停–继续–结束计时–清零功能
要求:实验视频,硬件电路图以及注释,编程方法(说明程序编写的原理和方法),程序清单以及注释,电路图和程序流程图

;定义端口和IO口寄存器
sbit start_button=P3^0 ;开始按钮
sbit reset_button=P3^1 ;重置按钮
sbit led_digit_1 = P1^0 ;第一位LED数码管
sbit led_digit_2 = P1^1 ;第二位LED数码管
sbit led_digit_3 = P1^2 ;第三位LED数码管
sbit led_digit_4 = P1^3 ;第四位LED数码管
sbit led_segments = P0 ;LED数码管段控制

; 定义常量
us equ 2 ; 定时器2的预分频值
digit_count equ 100 ; 跑表计时时间,单位为毫秒

; 定义变量
sdata xdata digits[4] ; 数码管显示的数字
sbit xdata is_paused = 0x4000 ; 是否已暂停标志
sbit xdata is_reset = 0x4001 ; 是否已重置标志
data ddata time_counter_low ; 计时器低字节计数器
data ddata time_counter_high ; 计时器高字节计数器

; 入口点
start:
    ; 初始化 IO 端口
    InitSegments:
        led_segments = 0FFH;  
        led_digit_1=0;
        led_digit_2=0;
        led_digit_3=0;
        led_digit_4=0;

    ; 初始化定时器2   
    InitTimer:     
        TH1 = 256 - digit_count / us;  ; 计算定时器值,us 表示微秒  
        TL1 = TH1;
        TMOD = 20H ; 定时器2工作在模式2,TH1是高字节,TL1是低字节
        TR1 = 1 ; 开始定时器2计时

    ; 循环显示跑表时间
    Loop:
        ; 扫描按钮状态
        if(is_reset)
        {
            ; 重置按钮按下,跑表时间清零
            time_counter_low = 0;
            time_counter_high = 0;
            reset_button = 0;
            is_reset = 0;
        }

        if(is_paused)
        {
            ; 暂停状态
            start_button = 0;
            display_time();
        }
        else
        {
            ; 运行状态
            start_button = 1;
            increment_time();
            display_time();
            if(time_counter_high >= 10)
            {
                ; 计时器已满,停止计时
                time_counter_high = 9;
                time_counter_low = 99;
                is_paused = 1;
            }
        }
        goto Loop; ; 返回循环        
        
    ; 计时器累加
    increment_time:
        time_counter_low = time_counter_low + 1;
        if(time_counter_low == 0)
        {
            time_counter_high = time_counter_high + 1;
        }
        return;

    ; 显示跑表时间
    display_time:
        digits[0] = time_counter_high / 10;
        digits[1] = time_counter_high % 10;
        digits[2] = time_counter_low / 10;
        digits[3] = time_counter_low % 10;
        display_segments();
        return;

    ; 显示数码管的段值
    display_segments:
        led_segments = 0x3f;
        led_digit_1=1;
        led_segments = get_digit_segments(digits[0]);
        delay_us(50);        
        led_digit_1=0;     
        led_segments = 0x3f;
        led_digit_2=1;
        led_segments = get_digit_segments(digits[1]);
        delay_us(50);
        led_digit_2=0;     
        led_segments = 0x3f;
        led_digit_3=1;
        led_segments = get_digit_segments(digits[2]);
        delay_us(50);
        led_digit_3=0;
        led_segments = 0x3f;
        led_digit_4=1;
        led_segments = get_digit_segments(digits[3]);
        delay_us(50);
        led_digit_4=0;
        return;

    ; 获取数码管段控制值
    get_digit_segments:
        mov r0, #0
        mov a, dpl
        anl a, #0FH
        add a, #0F0H
        mov r0, a
        ret

    ; 微秒级延时子程序
    delay_us:
        mov r7, dpl
        delay_us_1:
            mov r6, #us / 3
            delay_us_2:
                mov r5, #us / 3
                delay_us_3:
                    djnz r5, delay_us_3
                djnz r6, delay_us_2
            djnz r7, delay_us_1
        ret

使用 P3^0 和 P3^1 连接开始和重置按钮,使用 P1^0 - P1^3 和 P0 端口连接 LED 数码管,本程序使用定时器2进行计时,定时器工作在模式2,TH1 是高字节,TL1 是低字节,计算定时器值,每次增加一个毫秒,到达时间上限后停止计时,支持暂停和恢复操作以及重置跑表功能。

由于本程序是模拟计时器进行计时的,在实际运用过程中时间精度可能不够高,您可以根据实际情况进行调整。

现成的例子,图也有

对于应用 mcs-51 单片机设计电子跑表,您需要完成以下几个步骤:

硬件设计和调试。将 4 位 LED 数码显示器与单片机连接起来,并添加按键等外设。根据需求,您需要编写相应的硬件驱动程序,例如数码管显示、按键检测等。在硬件调试过程中,请注意检查各个电路元件是否正确连接,以及电源电压是否稳定。

编写程序。通过 Keil C51 等集成开发环境,编写跑表程序。主要包括计时功能、暂停/继续功能、结束计时功能和清零功能。在编写程序时,您需要注意时间精度、按键检测的 debounce 等问题。

调试和测试。在程序编写完成后,您需要进行系统的调试和测试。尝试各种场景下的操作,如开始计时、暂停计时、继续计时、结束计时、清零计时等。

以下是一份基本的程序框架供参考:

#include <reg52.h>
#include <intrins.h>

sbit KEY_START = P1^0; // 开始/继续按钮
sbit KEY_PAUSE = P1^1; // 暂停按钮
sbit KEY_STOP = P1^2; // 停止按钮
sbit KEY_RESET = P1^3; // 清零按钮

sbit DIG_1 = P2^0; // 第一位数码管
sbit DIG_2 = P2^1; // 第二位数码管
sbit DIG_3 = P2^2; // 第三位数码管
sbit DIG_4 = P2^3; // 第四位数码管

unsigned int cnt = 0; // 计时器计数值,初始为0
unsigned char flag_start = 0; // 开始/继续标志位
unsigned char flag_pause = 0; // 暂停标志位
unsigned char flag_stop = 0; // 停止标志位
unsigned char flag_reset = 0; // 清零标志位

void init_timer() {
    TMOD = 0x01; // 定时器0工作在模式116位自动重装载)
    TH0 = 0xFC; // 定时器初值为65536-50000=15536,定时50000us,即50ms
    TL0 = 0x00;
    ET0 = 1; // 定时器0中断使能
    EA = 1; // 总中断使能
    TR0 = 1; // 启动定时器0
}

void display(unsigned int num) {
    unsigned int digit[4];
    digit[0] = num % 10;
    digit[1] = num / 10 % 10;
    digit[2] = num / 100 % 10;
    digit[3] = num / 1000;
    
    DIG_1 = 0; P0 = digit[0]; DIG_1 = 1;
    DIG_2 = 0; P0 = digit[1]; DIG_2 = 1;
    DIG_3 = 0; P0 = digit[2]; DIG_3 = 1;
    DIG_4 = 0; P0 = digit[3]; DIG_4 = 1;
}

void main() {
    init_timer(); // 定时器初始化
    
    while (1) {
        if (!KEY_START && !flag_start) { // 开始/继续计时
            flag_start = 1;
            flag_pause = 0;
            flag_stop = 0;
            flag_reset = 0;
        }
        if (!KEY_PAUSE && flag_start && !flag_pause) { // 暂停计时
            flag_pause = 1;
        }
        if (!KEY_PAUSE && flag_start && flag_pause) { // 继续计时
            flag_pause = 0;
        }
        if (!KEY_STOP && flag_start && !flag_stop) { // 结束计时
    flag_stop = 1;
}
当检测到停止按钮被按下,并且当前正在计时,且之前未标记过停止,则将标志位置为 1。

在主程序中需要添加定时器中断处理函数,如下所示:

void timer0_isr() interrupt 1 {
    TH0 = 0xFC; // 定时器初值重置
    TL0 = 0x00;
    
    if (flag_start && !flag_pause && !flag_stop && !flag_reset) {
        cnt++; // 计时器加一
        if (cnt > 9999) cnt = 0; // 计时器溢出
        display(cnt); // 在数码管上显示计数值
    }
}

该函数会在定时器每次计时完成时被调用。如果已经开始计时、未暂停、未停止并且未清零,则将计数器 cnt 加 1,并将其显示在数码管上。如果计数器超过了 9999,则将其归零。

至此,您可以继续完善其他功能,比如暂停/继续计时和清零计数器。在程序编写过程中,还需要注意处理按键抖动问题,避免错误操作。另外,您还需要将程序烧录到单片机上进行测试,并调试硬件电路以及程序逻辑。

一个简易的,可以借鉴下

电子跑表程序:
/************************  mian.c  *************************/
#include <reg52.h>
#include "1602.h"
#include "main.h"
#include "delay.h"
#include "control.h"
#include "key.h"
uchar code  tablea[]=" 2020-12-31 Thu ";
uchar code tableb[]="    00:00:00    ";

uchar code  tablec[]="0 -- 00:00:00:00";
uchar code tabled[]="  00:00:00:00   ";
extern uint mode,imode;
void main()
{
    uchar num;
    init();
    while(1)
    {
        keyscan()    ;  
        
        if (mode==CLOCK)
        {
            /***********************************/
            if(imode)
            {
                write_com(0x80);
                for(num=0;num<=15;num++)
                {
                    write_date(tablea[num]);
                    delay(5);
                }
                write_com(0x80+0x40);
                for(num=0;num<=15;num++)
                {
                    write_date(tableb[num]);
                    delay(5);
                }
                imode=0;
            }
            /***********************************/
            
            keyscan_clock();
            if(TR0==1)show_clock();
        }
        
        if (mode==RUN)
        {
            /***********************************/
            if(imode)
            {
                write_com(0x80);
                for(num=0;num<=15;num++)
                {
                    write_date(tabled[num]);
                    delay(5);
                }
                write_com(0x80+0x40);
                for(num=0;num<=15;num++)
                {
                    write_date(tablec[num]);
                    delay(5);
                }
                imode=0;
            }
            /***********************************/
            keyscan_run();
            show_run();
        }
    } 
}
/********************** main.h ********************************/
#ifndef __MAIN_H_
#define __MAIN_H_

#define uchar unsigned char 
#define uint unsigned int
    
#endif
/************************ 1602.c *****************************/
#include <reg52.h>
#include "delay.h"
#include "1602.h"
sbit rs=P1^0;
sbit lcden=P1^2;
sbit rw=P1^1;
void write_com(uchar com)
{
    rs=0;
    lcden=0;
    P0=com;
    delay(5);
    lcden=1;
    delay(5);
    lcden=0;    
}

void write_date(uchar date)
{
    rs=1;
    lcden=0;
    P0=date;
    delay(5);
    lcden=1;
    delay(5);
    lcden=0;    
}

void write_sfm(uchar hang,uchar add,uchar date)
{
    uchar shi,ge;
    shi=date/10;
    ge=date%10;
    if (hang==1)
    write_com(0x80+add);
    if(hang==2)
    write_com(0x80+0x40+add);
    write_date(0x30+shi);
    write_date(0x30+ge);
}

/********************  1602.h *******************************/
#ifndef __1602_H_
#define __1602_H_
#include "main.h"
void write_com(uchar com);
void write_date(uchar date);
void write_sfm(uchar hang,uchar add,uchar date);
#endif

/************************ delay.c  ****************************/
#include "delay.h"

void delay(uint z)
{
    uint x,y;
    for(x=z;x>0;x--)
        for(y=800;y>0;y--);
}

/************************* delay.h ***********************/
#ifndef __DELAY_H__
#define __DELAY_H__
#include "main.h"
void delay(uint z);
#endif


/********************** key.c ********************************/
#include <reg52.h>
#include "key.h"
#include "main.h"
#include "delay.h"
#include "1602.h"
sbit s1=P2^0;   //时钟调整按键
sbit s2=P2^1;        //时钟加按键/计时清零键
sbit s3=P2^2;        //时钟减按键/计时开始或停止
sbit time_cap=P2^3;   //计时捕获按键
sbit mode_change=P2^4;        //模式切换按键
uint mode=CLOCK;   //  mode : 模式标志位
extern uchar count,miao,shi,fen;   //时钟参数
extern uchar msecond,second,minute,hour;   //跑表参数
uchar s1num;      //  调整时钟用的状态标志
uint s1_state=0,s3_state=0;
uint site=0,ssite=0;   //计时保存排名位
long time[15];    //计时保存数组
uint flag=0,imode=0;
void keyscan(void)   //模式按键扫描
{
    if(mode_change==0)    
    {
        delay(5);
        if(mode_change==0)            //模式转换huan 
        {
            flag++;
            if(flag%2==0)
            {
                mode=CLOCK;
                imode=1;    
            }                
            else    
            {
                mode=RUN;
                imode=2;
            }                
            while(!mode_change);
        }
    }
}

/*
    keyscan_clock : 时钟模式下按键扫描
        输入 : 无
        输出 : 无
*/
void keyscan_clock(void)
{
    if(s1==0)    
    {
        delay(5);
        if(s1==0)
        {    s1num++;
            while(!s1);
            if(s1num==1)
            {
                TR0=0;
                write_com(0x80+0x40+10);
                write_com(0x0f);
            }
    }
            if(s1num==2)
            {
                write_com(0x80+0x40+7);
            }
            if(s1num==3)
            {
                write_com(0x80+0x40+4);
            }
            if(s1num==4)
            {
                s1num=0;
                write_com(0x0c);
                TR0=1;
            }

                
        }
        if(s1num!=0)
        {
            if(s2==0)
            {
                delay(5);
                if(s2==0)
                {
                    while(!s2);
                    if(s1num==1)
                    {
                        miao++;
                        if(miao==60)
                            miao=0;
                        write_sfm(2,10,miao);
                        write_com(0x80+0x40+10);
                        
                        
                    }
                    if(s1num==2)
                    {
                        fen++;
                        if(fen==60)
                            fen=0;
                        write_sfm(2,7,fen);
                        write_com(0x80+0x40+7);
                    }
                    if(s1num==3)
                    {
                        shi++;
                        if(shi==24)
                            shi=0;
                        write_sfm(2,4,shi);
                        write_com(0x80+0x40+4);
                    }
                }
            }
            if(s3==0)
            {
                delay(5);
                if(s3==0)
                {
                    while(!s3);
                    if(s1num==1)
                    {
                        miao--;
                        if(miao==-1)
                            miao=59;
                        write_sfm(2,10,miao);
                        write_com(0x80+0x40+10);
                    }
                    if(s1num==2)
                    {
                        fen--;
                        if(fen==-1)
                            fen=59;
                        write_sfm(2,7,fen);
                        write_com(0x80+0x40+7);
                    }
                    if(s1num==3)
                    {
                        shi--;
                        if(shi==-1)
                            shi=23;
                        write_sfm(2,4,shi);
                        write_com(0x80+0x40+4);
                    }
                }
            }
        }
}

/*
        keyscan_run : 跑表模式下按键扫描
        输入 : 无
        输出 : 无
*/
void keyscan_run(void)
{
    if(time_cap==0)
    {
        delay(5);
        if(time_cap==0)
        {
            if(site<15)
            {
                site++;
                time[site]=hour*1000000+minute*10000+second*100+msecond;
                write_sfm(2,0,site);
                write_sfm(2,5,time[site]/1000000);
                write_sfm(2,8,time[site]%1000000/10000);
                write_sfm(2,11,time[site]%10000/100);
                write_sfm(2,14,time[site]%100);
            }
        }
    }
    
    if(s1==0)        //计时清零
    {
        uint i=0;
        delay(5);
        if( s1==0  &&  s3_state%2==0 )
        {
            msecond=second=minute=hour=0;
            for(i=0;i<15;i++)
            {
                time[i]=0;
            }
            site=0;                              
            write_sfm(2,0,0);
            write_sfm(2,5,0);
            write_sfm(2,8,0);
            write_sfm(2,11,0);
            write_sfm(2,14,0);
        }
    }
    
    
    
    
    if(s2==0)        //    查询数据
    {
        delay(5);
        if(s2==0)
        {
            ssite++;
            if(ssite>site) ssite=1;
            write_sfm(2,0,ssite);
            write_sfm(2,5,time[ssite]/1000000);
            write_sfm(2,8,time[ssite]%1000000/10000);
            write_sfm(2,11,time[ssite]%10000/100);
            write_sfm(2,14,time[ssite]%100);
        }
    }
    
    
    
    if(s3==0)        //    开始 / 停止 计时
    {
        delay(5);
        if(s3==0)
        {
            s3_state++;
            if(s3_state%2)
            {
                TR1=1;
            }
            if(s3_state%2==0)
            {
                TR1=0;
            }
            while(!s3);
        }
    }
}

/********************** key.h ********************************/
#ifndef __KEY_H_
#define __KEY_H_
#define CLOCK 0    //时钟模式
#define RUN   1       //跑表模式

void keyscan_clock(void);  //时钟模式按键扫描
void keyscan(void);                    //状态模式扫描
void keyscan_run(void);    //跑表模式扫描
#endif

/***************** control.c ******************************/
#include "control.h"
#include "1602.h"
#include "delay.h"

uchar count,miao,shi,fen;           //时钟参数
uchar code  table[]=" 2020-12-31 Thu ";   //初始化LCD见界面参数
uchar code table1[]="    00:00:00    ";
uchar msecond,second,minute,hour;   //跑表参数

//初始化,LCD及定时器
void init(void)
{
    uchar num;
//    lcden=0;
//    fen=59;
//    miao=53;
//    shi=23;
    write_com(0x38);
    write_com(0x0c);
    write_com(0x06);
    write_com(0x01);
    write_com(0x80);
    for(num=0;num<=15;num++)
    {
        write_date(table[num]);
        delay(5);
    }
    write_com(0x80+0x40);
    for(num=0;num<=15;num++)
    {
        write_date(table1[num]);
        delay(5);
    }
    TMOD=0x11;
    TH0 = 0x88;
  TL0 = 0x00;
    EA=1;
    ET0=1;
    TR0=1;
        
    TH1 = 0x0E8;  //定时器1 10ms
    TL1 = 0x00;
    ET1=1;
    TR1 = 0;
}


//定时器2中断服务函数,用于时钟进行
void timer0() interrupt 1
{
    TH0 = 0x88;
  TL0 = 0x00;
    count++;
    if(count==20)
    {
        count=0;
        miao++;
        if(miao==60)
        {
            miao=0;
            fen++;
            if(fen==60)
            {
                fen=0;
                shi++;
                if(shi==24)
                {
                    shi=0;
                }
            }
        }
    }    
}

//定时器1中断服务函数,用于跑表计时
void timer1() interrupt 3
{
    TH1 = 0x0E8;  //定时器1 10ms
    TL1 = 0x00;
    msecond++;
    if(msecond==100)
    {
        msecond=0;
        second++;
        if(second==60)
        {
            second=0;
            minute++;
            if(minute==60)
            {
                minute=0;
                hour++;
                if(hour==24)
                    hour=0;
            }
        }
    }
    
}

//更新时钟函数
void show_clock(void)
{
    write_sfm(2,4,shi);
    write_sfm(2,7,fen);
    write_sfm(2,10,miao);
}


//更新跑表计时函数
void show_run(void)
{
    write_sfm(1,2,hour);
    write_sfm(1,5,minute);
    write_sfm(1,8,second);
    write_sfm(1,11,msecond);
}

/*************************** control.h ********************/
#ifndef __CONTROL_H_
#define __CONTROL_H_

void init(void);
void show_clock(void);
void show_run(void);
#endif
电子跑表程序:
/************************  mian.c  *************************/
#include <reg52.h>
#include "1602.h"
#include "main.h"
#include "delay.h"
#include "control.h"
#include "key.h"
uchar code  tablea[]=" 2020-12-31 Thu ";
uchar code tableb[]="    00:00:00    ";

uchar code  tablec[]="0 -- 00:00:00:00";
uchar code tabled[]="  00:00:00:00   ";
extern uint mode,imode;
void main()
{
    uchar num;
    init();
    while(1)
    {
        keyscan()    ;  
        
        if (mode==CLOCK)
        {
            /***********************************/
            if(imode)
            {
                write_com(0x80);
                for(num=0;num<=15;num++)
                {
                    write_date(tablea[num]);
                    delay(5);
                }
                write_com(0x80+0x40);
                for(num=0;num<=15;num++)
                {
                    write_date(tableb[num]);
                    delay(5);
                }
                imode=0;
            }
            /***********************************/
            
            keyscan_clock();
            if(TR0==1)show_clock();
        }
        
        if (mode==RUN)
        {
            /***********************************/
            if(imode)
            {
                write_com(0x80);
                for(num=0;num<=15;num++)
                {
                    write_date(tabled[num]);
                    delay(5);
                }
                write_com(0x80+0x40);
                for(num=0;num<=15;num++)
                {
                    write_date(tablec[num]);
                    delay(5);
                }
                imode=0;
            }
            /***********************************/
            keyscan_run();
            show_run();
        }
    } 
}
/********************** main.h ********************************/
#ifndef __MAIN_H_
#define __MAIN_H_

#define uchar unsigned char 
#define uint unsigned int
    
#endif
/************************ 1602.c *****************************/
#include <reg52.h>
#include "delay.h"
#include "1602.h"
sbit rs=P1^0;
sbit lcden=P1^2;
sbit rw=P1^1;
void write_com(uchar com)
{
    rs=0;
    lcden=0;
    P0=com;
    delay(5);
    lcden=1;
    delay(5);
    lcden=0;    
}

void write_date(uchar date)
{
    rs=1;
    lcden=0;
    P0=date;
    delay(5);
    lcden=1;
    delay(5);
    lcden=0;    
}

void write_sfm(uchar hang,uchar add,uchar date)
{
    uchar shi,ge;
    shi=date/10;
    ge=date%10;
    if (hang==1)
    write_com(0x80+add);
    if(hang==2)
    write_com(0x80+0x40+add);
    write_date(0x30+shi);
    write_date(0x30+ge);
}

/********************  1602.h *******************************/
#ifndef __1602_H_
#define __1602_H_
#include "main.h"
void write_com(uchar com);
void write_date(uchar date);
void write_sfm(uchar hang,uchar add,uchar date);
#endif

/************************ delay.c  ****************************/
#include "delay.h"

void delay(uint z)
{
    uint x,y;
    for(x=z;x>0;x--)
        for(y=800;y>0;y--);
}

/************************* delay.h ***********************/
#ifndef __DELAY_H__
#define __DELAY_H__
#include "main.h"
void delay(uint z);
#endif


/********************** key.c ********************************/
#include <reg52.h>
#include "key.h"
#include "main.h"
#include "delay.h"
#include "1602.h"
sbit s1=P2^0;   //时钟调整按键
sbit s2=P2^1;        //时钟加按键/计时清零键
sbit s3=P2^2;        //时钟减按键/计时开始或停止
sbit time_cap=P2^3;   //计时捕获按键
sbit mode_change=P2^4;        //模式切换按键
uint mode=CLOCK;   //  mode : 模式标志位
extern uchar count,miao,shi,fen;   //时钟参数
extern uchar msecond,second,minute,hour;   //跑表参数
uchar s1num;      //  调整时钟用的状态标志
uint s1_state=0,s3_state=0;
uint site=0,ssite=0;   //计时保存排名位
long time[15];    //计时保存数组
uint flag=0,imode=0;
void keyscan(void)   //模式按键扫描
{
    if(mode_change==0)    
    {
        delay(5);
        if(mode_change==0)            //模式转换huan 
        {
            flag++;
            if(flag%2==0)
            {
                mode=CLOCK;
                imode=1;    
            }                
            else    
            {
                mode=RUN;
                imode=2;
            }                
            while(!mode_change);
        }
    }
}

/*
    keyscan_clock : 时钟模式下按键扫描
        输入 : 无
        输出 : 无
*/
void keyscan_clock(void)
{
    if(s1==0)    
    {
        delay(5);
        if(s1==0)
        {    s1num++;
            while(!s1);
            if(s1num==1)
            {
                TR0=0;
                write_com(0x80+0x40+10);
                write_com(0x0f);
            }
    }
            if(s1num==2)
            {
                write_com(0x80+0x40+7);
            }
            if(s1num==3)
            {
                write_com(0x80+0x40+4);
            }
            if(s1num==4)
            {
                s1num=0;
                write_com(0x0c);
                TR0=1;
            }

                
        }
        if(s1num!=0)
        {
            if(s2==0)
            {
                delay(5);
                if(s2==0)
                {
                    while(!s2);
                    if(s1num==1)
                    {
                        miao++;
                        if(miao==60)
                            miao=0;
                        write_sfm(2,10,miao);
                        write_com(0x80+0x40+10);
                        
                        
                    }
                    if(s1num==2)
                    {
                        fen++;
                        if(fen==60)
                            fen=0;
                        write_sfm(2,7,fen);
                        write_com(0x80+0x40+7);
                    }
                    if(s1num==3)
                    {
                        shi++;
                        if(shi==24)
                            shi=0;
                        write_sfm(2,4,shi);
                        write_com(0x80+0x40+4);
                    }
                }
            }
            if(s3==0)
            {
                delay(5);
                if(s3==0)
                {
                    while(!s3);
                    if(s1num==1)
                    {
                        miao--;
                        if(miao==-1)
                            miao=59;
                        write_sfm(2,10,miao);
                        write_com(0x80+0x40+10);
                    }
                    if(s1num==2)
                    {
                        fen--;
                        if(fen==-1)
                            fen=59;
                        write_sfm(2,7,fen);
                        write_com(0x80+0x40+7);
                    }
                    if(s1num==3)
                    {
                        shi--;
                        if(shi==-1)
                            shi=23;
                        write_sfm(2,4,shi);
                        write_com(0x80+0x40+4);
                    }
                }
            }
        }
}

/*
        keyscan_run : 跑表模式下按键扫描
        输入 : 无
        输出 : 无
*/
void keyscan_run(void)
{
    if(time_cap==0)
    {
        delay(5);
        if(time_cap==0)
        {
            if(site<15)
            {
                site++;
                time[site]=hour*1000000+minute*10000+second*100+msecond;
                write_sfm(2,0,site);
                write_sfm(2,5,time[site]/1000000);
                write_sfm(2,8,time[site]%1000000/10000);
                write_sfm(2,11,time[site]%10000/100);
                write_sfm(2,14,time[site]%100);
            }
        }
    }
    
    if(s1==0)        //计时清零
    {
        uint i=0;
        delay(5);
        if( s1==0  &&  s3_state%2==0 )
        {
            msecond=second=minute=hour=0;
            for(i=0;i<15;i++)
            {
                time[i]=0;
            }
            site=0;                              
            write_sfm(2,0,0);
            write_sfm(2,5,0);
            write_sfm(2,8,0);
            write_sfm(2,11,0);
            write_sfm(2,14,0);
        }
    }
    
    
    
    
    if(s2==0)        //    查询数据
    {
        delay(5);
        if(s2==0)
        {
            ssite++;
            if(ssite>site) ssite=1;
            write_sfm(2,0,ssite);
            write_sfm(2,5,time[ssite]/1000000);
            write_sfm(2,8,time[ssite]%1000000/10000);
            write_sfm(2,11,time[ssite]%10000/100);
            write_sfm(2,14,time[ssite]%100);
        }
    }
    
    
    
    if(s3==0)        //    开始 / 停止 计时
    {
        delay(5);
        if(s3==0)
        {
            s3_state++;
            if(s3_state%2)
            {
                TR1=1;
            }
            if(s3_state%2==0)
            {
                TR1=0;
            }
            while(!s3);
        }
    }
}

/********************** key.h ********************************/
#ifndef __KEY_H_
#define __KEY_H_
#define CLOCK 0    //时钟模式
#define RUN   1       //跑表模式

void keyscan_clock(void);  //时钟模式按键扫描
void keyscan(void);                    //状态模式扫描
void keyscan_run(void);    //跑表模式扫描
#endif

/***************** control.c ******************************/
#include "control.h"
#include "1602.h"
#include "delay.h"

uchar count,miao,shi,fen;           //时钟参数
uchar code  table[]=" 2020-12-31 Thu ";   //初始化LCD见界面参数
uchar code table1[]="    00:00:00    ";
uchar msecond,second,minute,hour;   //跑表参数

//初始化,LCD及定时器
void init(void)
{
    uchar num;
//    lcden=0;
//    fen=59;
//    miao=53;
//    shi=23;
    write_com(0x38);
    write_com(0x0c);
    write_com(0x06);
    write_com(0x01);
    write_com(0x80);
    for(num=0;num<=15;num++)
    {
        write_date(table[num]);
        delay(5);
    }
    write_com(0x80+0x40);
    for(num=0;num<=15;num++)
    {
        write_date(table1[num]);
        delay(5);
    }
    TMOD=0x11;
    TH0 = 0x88;
  TL0 = 0x00;
    EA=1;
    ET0=1;
    TR0=1;
        
    TH1 = 0x0E8;  //定时器1 10ms
    TL1 = 0x00;
    ET1=1;
    TR1 = 0;
}


//定时器2中断服务函数,用于时钟进行
void timer0() interrupt 1
{
    TH0 = 0x88;
  TL0 = 0x00;
    count++;
    if(count==20)
    {
        count=0;
        miao++;
        if(miao==60)
        {
            miao=0;
            fen++;
            if(fen==60)
            {
                fen=0;
                shi++;
                if(shi==24)
                {
                    shi=0;
                }
            }
        }
    }    
}

//定时器1中断服务函数,用于跑表计时
void timer1() interrupt 3
{
    TH1 = 0x0E8;  //定时器1 10ms
    TL1 = 0x00;
    msecond++;
    if(msecond==100)
    {
        msecond=0;
        second++;
        if(second==60)
        {
            second=0;
            minute++;
            if(minute==60)
            {
                minute=0;
                hour++;
                if(hour==24)
                    hour=0;
            }
        }
    }
    
}

//更新时钟函数
void show_clock(void)
{
    write_sfm(2,4,shi);
    write_sfm(2,7,fen);
    write_sfm(2,10,miao);
}


//更新跑表计时函数
void show_run(void)
{
    write_sfm(1,2,hour);
    write_sfm(1,5,minute);
    write_sfm(1,8,second);
    write_sfm(1,11,msecond);
}

/*************************** control.h ********************/
#ifndef __CONTROL_H_
#define __CONTROL_H_

void init(void);
void show_clock(void);
void show_run(void);
#endif


关于这方面的原理图,程序例子网上都是一大堆的

结合ChatGPT参考建议作答:
1、程序编写的原理和方法
该电子跑表的设计基于MCS-51单片机,使用4位LED数码显示,计时时间为0-99.99秒循环。同时,该跑表还具有暂停、继续、结束计时和清零功能。

2、程序的主要思路是使用定时器中断来实现计时功能,并使用按键中断来实现暂停、继续、结束计时和清零功能。具体实现步骤如下:

1.初始化定时器和按键中断。

2.在定时器中断服务程序中,每隔10ms更新计时器的值,并将计时器的值转换为BCD码,然后将BCD码输出到LED数码管上。

3.在按键中断服务程序中,根据按键的不同,执行相应的操作,如暂停、继续、结束计时和清零。

4.在主函数中,启用中断并等待中断事件的发生。

5.程序清单以及注释

#include <reg51.h>

#define uchar unsigned char
#define uint unsigned int

sbit key1 = P3^2; // 暂停/继续按键
sbit key2 = P3^3; // 结束计时按键
sbit key3 = P3^4; // 清零按键

uchar code table[] = { // 数码管显示表
    0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,
    0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71
};

uchar timer[4] = {0, 0, 0, 0}; // 计时器,初始值为0

void init_timer() { // 初始化定时器
    TMOD = 0x01; // 定时器0,模式1
    TH0 = 0x3c; // 定时器初值,10ms
    TL0 = 0xb0;
    ET0 = 1; // 允许定时器中断
    TR0 = 1; // 启动定时器
}

void init_interrupt() { // 初始化中断
    EA = 1; // 允许中断
    EX0 = 1; // 允许外部中断0
    IT0 = 1; // 下降沿触发
}

void display() { // 数码管显示
    uchar i;
    for (i = 0; i < 4; i++) {
        P2 = 0xff; // 关闭所有数码管
        P0 = table[timer[i]]; // 显示当前数码
        P2 = ~(0x01 << i); // 打开当前数码管
        delay(1); // 延时1ms
    }
}

void pause() { // 暂停计时
    TR0 = 0; // 停止定时器
    while (key1 == 0); // 等待按键释放
}

void resume() { // 继续计时
    TR0 = 1; // 启动定时器
}

void stop() { // 结束计时
    TR0 = 0; // 停止定时器
    timer[0] = timer[1] = timer[2] = timer[3] = 0; // 计时器清零
    display(); // 显示清零后的数码
}

void clear() { // 清零计时
    timer[0] = timer[1] = timer[2] = timer[3] = 0; // 计时器清零
    display(); // 显示清零后的数码
}

void delay(uint ms) { // 延时函数
    uint i, j;
    for (i = 0; i < ms; i++)
        for (j = 0; j < 110; j++);
}

void main() {
    init_timer(); // 初始化定时器
    init_interrupt(); // 初始化中断
    while (1); // 等待中断事件的发生
}

void timer_isr() interrupt 1 { // 定时器中断服务程序
    TH0 = 0x3c; // 重新赋初值
    TL0 = 0xb0;
    timer[3]++; // 计时器加1
    if (timer[3] == 10) { // 判断是否达到10ms
        timer[3] = 0;
        timer[2]++;
        if (timer[2] == 10) { // 判断是否达到1s
            timer[2] = 0;
            timer[1]++;
            if (timer[1] == 10) { // 判断是否达到10s
                timer[1] = 0;
                timer[0]++;
                if (timer[0] == 10) // 判断是否达到99.99s
                    timer[0] = 0;
            }
        }
    }
    display(); // 显示当前计时器的值
}

void key1_isr() interrupt 0 { // 暂停/继续按键中断服务程序
    delay(10); // 延时去抖
    if (key1 == 0) { // 判断按键是否按下
        pause(); // 暂停计时
        while (1) { // 等待按键事件的发生
            if (key1 == 0) { // 判断按键是否按下
                delay(10); // 延时去抖
                if (key1 == 0) // 再次判断按键是否按下
                    resume(); // 继续计时
                break;
            }
            if (key2 == 0) { // 判断是否按下结束计时按键
                delay(10); // 延时去抖
                if (key2 == 0) { // 再次判断是否按下结束计时按键
                    stop(); // 结束计时
                    break;
                }
            }
        }
    }
}

void key2_isr() interrupt 0 { // 结束计时按键中断服务程序
    delay(10); // 延时去抖
    if (key2 == 0) { // 判断按键是否按下
        stop(); // 结束计时
    }
}

void key3_isr() interrupt 0 { // 清零按键中断服务程序
    delay(10); // 延时去抖
    if (key3 == 0) { // 判断按键是否按下
        clear(); // 清零计时
    }
}