C语言 单片机秒表程序

本人是一个新手 对于下边的c程序每一个子函数能看懂,但是像这么多子函数放到一起
就晕了,想请亲们帮我把这个程序的流程梳理梳理中断是何时工作,各个子函数怎么在这个整体里运行 越详细越好,本人十分感谢!

/* 设计的秒表计数到小数点后两位,(ms)用到六个数码管一个小数点加两位小数*/
#include<reg52.h>

sbit ADDR3=P1^3;
sbit ENLED=P1^4;
sbit KEY1=P2^4;
sbit KEY2=P2^5;
sbit KEY3=P2^6; 
sbit KEY4=P2^7;

unsigned char code LedChar[] = {  //数码管显示字符转换表
    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
    0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
unsigned char LedBuff[6]={
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF  //数码管显示缓存区
};
unsigned char KeySta[4]={1,1,1,1};

bit StopwatchRunning=0;
bit StopwatchRefresh=1;
unsigned char IntegerPart=0;//整数部分
unsigned char DecimalPart=0;//小数部分

void ConfigTimer0(unsigned int ms);
void StopwatchDisplay();
void KeyDriver();

void main()
{
    EA=1;
    ENLED=0;
    ADDR3=1;
    P2=0XFE;//P2.0置零选第四行按键
    ConfigTimer0(2);//T0定时2ms

    while(1)
    {
      if(StopwatchRefresh)       //如果刷新需要调用显示函数
          StopwatchRefresh=0;
          StopwatchDisplay();
      KeyDriver();
    }
}

/*配置并启动T0 ms就是T0的定时时间*/
void ConfigTimer0(unsigned int ms)
{
    unsigned long tmp;
    tmp=11059200/12;//   1/T=f频率
    tmp=(tmp*ms)/1000; //    ms/1000=1s   f*T=机械周期个数
    tmp=65536-tmp;
    tmp=tmp+18;
    T0RH=(unsigned char)(tmp>>8);
    T0LH=(unsigned char)tmp;
    TMOD&=0xF0;
    TMOD|=0x01;
    TH0=T0RH;
    TL0=T0RL;
    ET0=1;
    TR0=1;

}
/*秒表计数显示函数*/
void StopwatchDisplay()
{
       signed char i;                         
       unsigned char buf[4];//同理数据转换的缓冲区

    /*小数部分转换到低2位*/
    LedBuf[0]=LedChar[DecimalPart%10];
    LedBuf[1]=LedChar[DecimalPart/10];  
    /*整数部分换到高4位 注意这里和小数部分区别是要用到缓存*/
    buf[0]= IntegerPart%10;
    buf[0]= IntegerPart/10%10;
    buf[0]= IntegerPart/100%10;
    buf[0]= IntegerPart/1000%10;
    for(i=3;i>=1;i++)//把高位为零的换成空字符
    {
        if(buf[i]==0)
           LedBuf[i+2]=0xFF;//置空
        else
          break;
    }
    for(;i>=0;i--)
    {
           LedBuf[i+2]=LedChar[buf[i]];
    }
        LedBuf[i+2]=0x7F;//运行到整数的最后一位上顺便点亮小数点
}
/*秒表复位*/
void  StopwatchReset()
{
    StopwatchRunning=0; //停止秒表
    IntegerPart=0;
    DecimalPart=0;
    StopwatchReset=1//重置刷新标志
}
/*秒表启停函数*/
void StopwatchAction()
{
     if(StopwatchRunning)//若是已经启动则停止
        StopwatchRunning=0;
     else
        StopwatchRunning=1;//若是已经停止则启动 
}
/*按键驱动函数,检测到有按键动作,调度相应动作函数,只有两个按键另外四个。注意:需要在主函数调用*/
void KeyDriver()
{
   unsigned char i;
   static unsigned char backup[4]=[1,1,1,1];
   for(i=0;i<4;i++)
   {
           if(backup[i]!=KeySta[i])
           {
              if(backup[i]!=0)
              {
                 if(i==1)
                    StopwatchReset();
            else if(i==2)
                    StopwatchAction();
              }
                backup[i]=KeySta[i];
           }
   }

}
/*数码管动态扫描刷新函数,注意:需在定时中断中调用*/
void  LedScan()
{
   static unsigned char i=0;

   P0=0XFF;
   P1=(P1&0xF8)|i;
   P0=LedBUF[i];
   if(i<5)         //在C里,i>5 i=0 继续走!!!
       i++;
   else
       i=0;
}
void KeyScan()
{
  unsigned char i;
  static unsigned char keybuff[4]={
      0xFF,0xFF,0xFF,0xFF
  };
    keybuff[0]=(keybuff[0]<<1)| KEY1;
    keybuff[1]=(keybuff[1]<<1)| KEY1;
    keybuff[2]=(keybuff[2]<<1)| KEY1;
    keybuff[3]=(keybuff[3]<<1)| KEY1;

    for(i=0;i<4;i++)
    {
      if(keybuff[i]==0x00)//连续8次扫描为0, 2ms*8(得移动8位上面才能有结果)
      {
          KeySta[i]=0;
      }
      else if(keybuff[i]==0xFF)
      {
          KeySta[i]=1;
      }
    }
}
/* 秒表计数函数,每隔10ms调用一次进行秒表计数累加 */
void StopwatchCount()
{
    if (StopwatchRunning)  //当处于运行状态时递增计数值
    {
        DecimalPart++;           //小数部分+1
        if (DecimalPart >= 100)  //小数部分计到100时进位到整数部分
        {
            DecimalPart = 0;
            IntegerPart++;       //整数部分+1
            if (IntegerPart >= 10000)  //整数部分计到10000时归零
            {
                IntegerPart = 0;
            }
        }
        StopwatchRefresh = 1;    //设置秒表计数刷新标志
    }
}
/*T0中断函数,完成数码管扫描、按键扫描与秒表计数*/
void InterruptTimer0() interrupt 1
{
  static unsigned char tmr10ms =0;
  TH0=T0RH;
  TL0=T0RL;
  LedScan();
  KeyScan();
//定时10ms进行一次秒表计数
    tmr10ms++;
    if(tmr10ms>=5)   //2ms一次 2*5=10ms
    {
      tmr10ms=0;
      StopwatchCount();
    }  
}

定时器呀 使用定时器计时数码管显示

简单化应该做一个树状图,从main()开始。

main开始 纸上画一下就好了
main是一个线程  定时器也是一个线程

画一个流程图就很清楚了

可以从main{}函数入手,使用keil的调试功能,一步一步进行调试,这样可以很好的清楚怎么工作了

可以把程序看成两部分,一部分是while循环,另一部分是中断函数。
首先看while(1)是一个恒成立的循环,系统在空闲的时候,就会运行while里面的函数,也就是StopwatchDisplay(); KeyDriver(); 。另外在运行
while(1)语句块的时候,系统在还会计时。比如说,中断设置的是2秒,那么当计时到2秒的时候,系统就会停下当前执行的语句(并记录下当前语句的下一行,中断结束后,从这里继续运行),跑去执行中断函数 InterruptTimer0() ,中断函数里面有函数LedScan(); KeyScan(); StopwatchCount(); 当中断函数执行完了之后,系统就又跑去上次停下来的地方继续运行。循环往复。
大概就是这个流程。。你要是还有什么不明白可以追问。

比较难的就是中断,这里跳了一下,不是连续运行的

每次中断到了 自动跳到中断函数执行

TH0=T0RH;
TL0=T0RL;

这个就是中断时间控制

时间片查询的方式运行比较好