找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1417|回复: 13
收起左侧

单片机秒表 可以实现开始、暂停、复位功能 求指导记录和查看功能

[复制链接]
ID:1099267 发表于 2023-11-12 17:22 | 显示全部楼层 |阅读模式
多功能秒表系统设计
设计要求:
(1)两位LED显示,显示时间为00~99秒。
(2)具有开始、暂停、复位功能。
(3)在秒表工作过程中能通过按键多次(不少于4次)记录当前的时间。
(4)可以通过按钮查看记录的时间 。
本人现在可以实现开始、暂停、复位功能,后面的记录和查看功能有没有哪位大佬可以帮忙改一下。求求了。
#include<reg51.h>
#define uint unsigned int
#define uchar unsigned char
sbit w0=P2^0;
sbit w1=P2^1;

sbit s1=P3^4;      //启动
sbit s2=P3^5;     //暂停
sbit s3=P3^6;     //清零
sbit s4=P3^7;     //记录
sbit s5=P3^3;     //查询

unsigned char recordTimes[4]; // 记录的时间,长度为4
uchar count=0;        //对50ms定时时间进行计数   
uchar miao=0;       //秒计数器
uchar j,k;
unsigned char code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,
                        0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00};//共阴极
void timedr0init()
{
  TMOD=0x01;
        TH0=(65536-50000)/256;
        TL0=(65536-50000)%256;
        TR0=0;
        ET0=1;
        EA=1;
}
void delay(unsigned char i)
{
  for(j=i;j>0;j--)
    for(k=125;k>0;k--);
}                           //延时
void display(uchar n)
{
   uchar c,d;
   c=n/10;
   d=n%10;
   P0=0x00;        //xiao
   P0=table[c];
   w0=0,w1=1;
   delay(5);

   P0=0x00;
   P0=table[d];
   w1=0,w0=1;
   delay(5);
                                              // P0=table[n/10];w0=0,w1=1;
                                        // P0=tabie[n%10];w0=1,w1=0;        
}
void scankeys()
{static bit recordFlag = 0;     //声明了一个静态的位变量 recordFlag,并将其初始化为 0
        static unsigned char recordIndex = 0;//静态无字符变量

if(s1==0)//启动
{
TR0=1;                  
}
if(s2==0)//暂停
{
TR0=0;                  
}
if(s3==0)//清零
{
TR0=0,miao=count=0;                  
}

        if (s4 == 0) {  // 记录
        if (!recordFlag && recordIndex < 4) {
            recordFlag = 1;

                recordTimes[recordIndex++] = miao;
                                                                while (s4 == 0);
                                                                delay(100); // 等待按键释放,防止重复记录

        }
    }
                else {
        recordFlag = 0;
    }

    if (s5 == 0) {  // 查看记录
        if (recordIndex > 0)
                                        {
            uchar i;
            for (i = 0; i < recordIndex; i++)
                                        {
                display(recordTimes[ i]);
                delay(100);
                                        }
                                }
                        }                        
                }
void main()
{
       timedr0init();

        while(1)
        {
        display(miao);
                                        scankeys();

    }
}

void time0() interrupt 1
{       TH0=(65536-50000)/256;
        TL0=(65536-50000)%256;
        count++;
        if(count==20)
        {
            count=0;
                                          miao++;
            if(miao==100)
            miao=0;        

        }

}

51hei.png
回复

使用道具 举报

ID:213173 发表于 2023-11-12 20:12 | 显示全部楼层
按下记录键,缓存recordTimes[3]=recordTimes[2];recordTimes[2]=recordTimes[1];recordTimes[1]=recordTimes[0];全部后移,再将当前秒数存在recordTimes[0]。始终保存最近4次的记录
回复

使用道具 举报

ID:1099267 发表于 2023-11-12 21:03 | 显示全部楼层
wulin 发表于 2023-11-12 20:12
按下记录键,缓存recordTimes[3]=recordTimes[2];recordTimes[2]=recordTimes[1];recordTimes[1]=recordTim ...

大佬,程序添加在哪里,求指导,纯新手,不是特别熟练
回复

使用道具 举报

ID:1099267 发表于 2023-11-12 21:27 | 显示全部楼层
wulin 发表于 2023-11-12 20:12
按下记录键,缓存recordTimes[3]=recordTimes[2];recordTimes[2]=recordTimes[1];recordTimes[1]=recordTim ...

if (s4 == 0) {  // 记录
        if (!recordFlag && recordIndex < 4) {
            recordFlag = 1;

            // 将记录后移
            for ( i = 3; i > 0; i--) {
                recordTimes[ i] = recordTimes[i-1];
            }
            // 记录当前时间
            recordTimes[0] = miao;

            while (s4 == 0);
            delay(100); // 等待按键释放,防止重复记录
        }
    } else {
        recordFlag = 0;
    }
回复

使用道具 举报

ID:1099267 发表于 2023-11-12 21:38 | 显示全部楼层
wulin 发表于 2023-11-12 20:12
按下记录键,缓存recordTimes[3]=recordTimes[2];recordTimes[2]=recordTimes[1];recordTimes[1]=recordTim ...

求大佬指导,纯萌新
回复

使用道具 举报

ID:195496 发表于 2023-11-12 22:08 | 显示全部楼层
建产数组存储数据,再修改按键程序,上下翻看
回复

使用道具 举报

ID:123289 发表于 2023-11-14 09:50 | 显示全部楼层
本帖最后由 yzwzfyz 于 2023-11-15 09:36 编辑

1、建立一个二维数组【P,2】,P记录你历史上记录的秒表数据是第几个。2秒表读数0-99。
2、建立三个指针P1\P2\P3:P1=当前的秒表P号,P2=【记录】按键指向的P号,P3=【查询】按键指向的P号。
3、显示程序,只从【P1,2】取数字显示,也即只显示P1所指向的数据。
键操作:
1、按下【记录】键:P2=P2+1(表示进行到第几个记录了),P1=P2(显示指向新的P2记录)。
2、按下【查询】键:P3=P3+1(表示查询到第几个记录了),P1=P3(对查询到的记录进行显示)。
3、按下【开始、清0、暂定】键:加一条 P1 = P2 (显示改为【记录】所指定的记录号,并在此记录上进行操作,这样保证【查询】后,能回到之前的记录号,不致混乱),其它操作与你的定义相同。
如此:
能让你记录到P组秒表数据(循环使用1-P),做到P组数据互不干扰,可查可询,且查询不影响当前的操作,甚至在读秒时查询也不影响当前的秒表计秒工作。
回复

使用道具 举报

ID:397054 发表于 2023-11-14 11:19 | 显示全部楼层
他已经建立数组了,只是没必要二维吧,序号就可以作为记录的数据是第几个。问题是他这个程序实在是太勉强了,直接驱动数码管不是这种方法,他的 display 函数这样写是不对的,只能在不按S4和S5的情况下正常,但也只是看似正常,这就是我说的“勉强”的意思,当按下S4或S5时,在按下的这段时间里有一个数码管会灭,而亮的那个数码管会保持,不会变化,尤其是S4,只要不抬起来一直保持这样不变,这恐怕并不是你需要的功能吧,按下S5键情况就更复杂了,按下不动基本上是乱显示,这取决于你的delay函数,delay(100)究竟会延迟多长时间,如果时间很短,那就是乱显示,如果时间很长比如超过0.5秒,会看到两个数码管闪亮几次,次数由记录的次数决定,最多4次,闪亮的时间很短由display函数中的delay(5);决定,基本也看不清,如果按着S5不放就是这个过程反复循环。

由这个分析能够猜出你的用意,想的是挺美,但这个程序做不到,仅仅是秒表勉强能用。要修改的话,量也不是很大,但框架就变了。。。
回复

使用道具 举报

ID:1099267 发表于 2023-11-14 21:19 | 显示全部楼层
鹈鹕 发表于 2023-11-14 11:19
他已经建立数组了,只是没必要二维吧,序号就可以作为记录的数据是第几个。问题是他这个程序实在是太勉强了 ...

大佬,可以帮忙修改一下吗,新手不是特别懂
回复

使用道具 举报

ID:1099267 发表于 2023-11-14 21:26 | 显示全部楼层
1556544 发表于 2023-11-14 21:19
大佬,可以帮忙修改一下吗,新手不是特别懂

上面呢位大佬讲的二维数组没有听说过,刚学得新手
回复

使用道具 举报

ID:397054 发表于 2023-11-15 21:30 | 显示全部楼层
原理更重要吧,清楚了原理怎样做是很自由的,我先把原理说下,一个修改方案提供给你,你也不必照搬的,可随意修改试验。

这种LED数码管的直接驱动都是以扫描方式实现的,且扫描的频率要稳定不受其他程序部分的干扰,你的程序之所以仅在秒表时看起来正常,是因为它就是在快速重复扫描,但你这种扫描太易受到干扰而停顿,即在主函数的while中循环,当你不按按键或只按s1-s3时,scankeys可以快速返回,显示会连续,但按下s4或者s5我上面那贴说过因为延迟和判断是否抬起它就会延迟返回或者按着不放它就不返回,显示就不正常。

所以这种数码管的显示一般都是分两个部分,驱动和赋值是分开的,驱动部分只是将显示缓冲区的内容依次轮换送数码管显示,而向显示缓冲区赋值是另一部分,可以在程序的任何地方、任何时候执行(在这里显示缓冲区就是c、d两个无符号字符变量)。驱动部分又必须等频率不断重复执行,这样数码管才会看起来常亮且不受干扰而停顿,要做到这点驱动部分就只能放在计时器的中断程序里,这样,你的display函数就要修改,把它分成两部分,计算和赋值部分(实际只有两句)留下,其余都移到中断程序中。

但这还不够,引起这个中断的是计时器,而你启停秒表是靠启停这个计时器实现的,这又不行,因为计时器一停中断就消失,数码管没了驱动就立即不亮了,所以这个也要改,计时器只要一上电初始化后就必须立即工作直到断电,这样才能使显示驱动不停地等间隔刷新数码管,并且频率要足够高以使眼睛觉察不出闪烁。这又引来了一个问题,看你的程序参数,你用的是12兆的晶振,计时器0,模式1,中断频率是20Hz,作为秒表用,这个没问题,但兼作显示驱动频率太低,数码管肯定会闪,,,所以兼顾编程方便和人眼的视觉暂留特性,这个频率应该提高到100Hz,为此 timedr0init 函数中的50000应改成10000,对应的,中断函数中的 if(count==20)改成 if(count==100),即每100次中断为一秒到。由于计时器一上电初始化后就要立即工作,所以这个函数中的另一处TR0=0;要改成TR0=1;。既然TR0不能再=0了,那么启停秒表就用更常规的办法:设置一个全局的标志位,比如Bit Stop_run,加在程序头部的说明区中;而且作为显示缓冲区的变量c、d也要全局化,因为主函数要向它赋值,中断函数也要用到它,为此将他俩的定义从display函数中取出也放置在此处。启停秒表就靠标志位Stop_run被置1/清零来实现,置1表示秒表启动;清零表示暂停/停止,反过来当然也可以,,,相应的中断程序中miao++;和下面的两句改为:

if (Stop_run)
  {
     miao++;
     if(miao==100) miao=0;
  }

如果Stop_run的01定义与此相反的话前面加个非号就行了。Stop_run的置1/清零就用你的scankeys函数中的s1-s3处理语句来执行,将TR0=0或1改成相应的Stop_run=0或1。

另外,你也没说你想达到什么目的,秒表就不用说了,主要是回显那几个记录值用什么方式、要达到什么效果,根据你贴出的程序看,你大概想以这样的方式回显:按下s5健,就立即在数码管上依次显示所有记录值,每个记录值显示的时间由delay(100);控制。你的display函数在scankeys函数中的这种用法根本达不到目的,且逻辑还很混乱,并且delay函数的性能究竟怎样,delay(100)究竟能延时多长时间这都不知道,直觉看它远不会达到1秒,如果你真想用这种方式回显,那么每个记录值显示的时间怎么也应该超过1秒吧。

根据这些条件我给你提供一个修改方案供你参考:

在变量声明区后面再附加两句:

bit stp_run=0;
uchar c,d,timer;



函数timedr0init改为:

void timedr0init()
{
  TMOD=0x01;
        TH0=(65536-10000)/256;
        TL0=(65536-10000)%256;
        TR0=1;
        ET0=1;
        EA=1;
}



增加一个延时函数:

void wait(unsigned char t)//t单位为10ms,当t=200时,即延时2秒。
  {
    timer=0;
    while (timer<t);
   }



函数display改为:

void display(uchar n)
{
   c=n/10;
   d=n%10;
}



函数scankey改为:

void scankeys()
{static bit recordFlag = 0;     //声明了一个静态的位变量 recordFlag,并将其初始化为 0
        static unsigned char recordIndex = 0;//静态无字符变量

if(s1==0)//启动
{
stp_run=1;                  
}
if(s2==0)//暂停
{
stp_run=0;                  
}
if(s3==0)//清零
{
stp_run=miao=count=0;                  
}

        if (s4 == 0) {  // 记录
        if (!recordFlag && recordIndex < 4) {
            recordFlag = 1;

                recordTimes[recordIndex++] = miao;
                                                                while (s4 == 0);
                                                                delay(100); // 等待按键释放,防止重复记录

        }
    }
                else {
        recordFlag = 0;
    }

    if (s5 == 0) {  // 查看记录
        if (recordIndex > 0)
                                        {
            uchar i;
            for (i = 0; i < recordIndex; i++)
                                        {
                display(recordTimes[ i]);
                wait(150);        //延时1.5秒,即每个数字显示1.5秒。
                                        }
                                }
                        }                        
                }



中断程序改动比较大,但大部分代码可以复制/粘贴过来:

void time0() interrupt 1
{
        static bit dp;
        TH0=(65536-10000)/256;
        TL0=(65536-10000)%256;
        P0=0x00;        //xiao
        timer++;
        count++;
        if(count==100)
        {
           count=0;
           if (stp_run)
             {
                miao++;
                if (miao==100) miao=0;
             }

        }
        if (dp=~dp)
          {
             P0=table[c];
             w0=0,w1=1;
          }
         else
         {
             P0=table[d];
             w1=0,w0=1;
         }
}


未提及的,就保留原文。

评分

参与人数 1黑币 +100 收起 理由
admin + 100 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

ID:1099267 发表于 2023-11-16 10:47 | 显示全部楼层
鹈鹕 发表于 2023-11-15 21:30
原理更重要吧,清楚了原理怎样做是很自由的,我先把原理说下,一个修改方案提供给你,你也不必照搬的,可随 ...

太牛了,大佬
回复

使用道具 举报

ID:397054 发表于 2023-11-17 15:27 | 显示全部楼层
呵呵你试没试,结果怎样?你是不是这个意思,达没达到目的?
另外,我的那个中断程序中前半部分不甚合理,应该把对stp_run的判断放在最外层,还有就是count和miao的+1都没不要单独存在,都可以合并到条件判断中,这样程序就更简洁清晰,也更符合C风范:

void time0() interrupt 1
{
  static bit dp;
  TH0=(65536-10000)/256;
  TL0=(65536-10000)%256;
  P0=0x00;        //xiao
  timer++;
  if (stp_run)
    {
       if(++count==100)
         {
            count=0;
            if (++miao==100)
              {
                 miao=0;
              }
         }
    }
  if (dp=~dp)
    {
       P0=table[c];
       w0=0,w1=1;
    }
   else
    {
       P0=table[d];
       w1=0,w0=1;
    }
}
回复

使用道具 举报

ID:1099267 发表于 2023-11-21 13:33 | 显示全部楼层
鹈鹕 发表于 2023-11-17 15:27
呵呵你试没试,结果怎样?你是不是这个意思,达没达到目的?
另外,我的那个中断程序中前半部分不甚合理, ...

试了,符合我想的功能
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表