AT89C51+四位数码管分秒时钟,解决闹钟的蜂鸣器关不掉的问题

在设计分秒时钟时,用到了数码管作为显示,定时器定时,当时钟走到设定时间时蜂鸣器响。过程中遇到了很多问题,程序也是在大佬的指点下不断优化。可能有些描述不对,我是新手,结论也是实践加现有理解写的,不一定对,不对的地方请多多指教!程序亲测有效

我的设计是:

3个按键:1设置键,1时间加键,1时间减键

四个数码管:两个显示分钟,两个显示秒

有源蜂鸣器1:与三级管相连

Question and Answer

1.为什么设置按键里,将按键加、减功能嵌套进去,设置按键按下的时候,计数停止,但是加、减按键按下加减功能没有反应?.因为程序进入了设置状态却没有进入按键加、减的程序

2.按键按下的时候数码管熄灭?因为程序检测到按键按下,会执行延时函数,延时会导致程序停留在那里,没有及时添加显示函数

3.按键按太快,数码管显示会出问题

本代码将将按键加、减功能设置在设置按下的情况下,设置键按下,当数字不动时,再按一次·设置键即可进行按键加减功能。为什么按下按键到按键抬起后,会干扰到数码管的显示?原因:如果按键扫描函数和数码管显示函数都放在主函数的固定循环内,当按键按下后,按键扫描

函数进行按键扫描,在扫描到按键延时后,CPU被限制在按键延时处进行计时,却无法运行其他程序,从而导致数码管显示函数无法运行,就会出现数码管闪灭显示问题。人能看到数码管闪烁是因为人眼能识别高频率(人眼看不到闪烁的频率为50Hz)低于50Hz,即1/50Hz=0.02秒,因此要想让人眼看不到数码管闪烁,数码管显示函数的显示频率至少是50Hz,即1/50Hz=0.02秒,也不是越高好,还要结合按键扫描函数的按键延时来取出最恰当的显示频率。由于程序内按键扫描函数和数码管显示函数存在大量的不精准延时。定时器解决按键

消抖原理:编写一个类似普通延时函数的定时器延时函数,用来替换掉普通延

时即可。

4.短按加减减实现按一次按键加减 但是按一下却加2?因为没有完全消抖,keytime没有清零,进行累加了。                                                 

5.在按键进行加减时,让计数器停止?有时候要按好多次按键才进入设置状态?  因为没有正常进入设置状态 ,设置状态标志位set不仅要在keyscan函数中将T0启动器关闭,还要在主函数利用设置状态标志位set使自动计时的函数关闭。              

6.计数器会自己停止?   1分钟就停下,定时器初始化函数没有放在mian函数的while循环之前。 

7.定时器中断开启了,但是时钟没有自动计时??

方法一:一直处于0状态?定时器中断子程序和定时器初始化有问题。定时器的计数变量time在中断里定义,不管有没有赋初值0,定时器的计数time都没有执行运算。

方法二:将time在中断程序里设置为静态变量。在定时器初始化的函数里和赋初始值,需要设置为全局变量,,参与运算的变量最好赋初始值,否则初始值会是随机值或者之前使用的值。

8.计数变慢了,显示的时间比设置的时间慢??在中断服务子程序和计时函数内设置标志位,标志位为1时,就开始计时。

9.闹钟函数无法让闹钟关闭,蜂鸣器一直响??难道闹钟和正常走时需要设置在两种状态?

将break 写在if大括号外,不管if的条件成不成立,有没有执行if大括号的while循环退出后buzz仍然为低电平,另外用while循环不好,没有按下按键时程序一直循环在那里,在做项目的时候是不允许的。闹钟函数在后面也有详细分析。

Notice:按键扫描函数中,要注意显示函数的位置,最好在进行自增自减运算后就扫描显示函数,否则有会造成按下按键,数码管熄灭的情况

  思路:

功能:实时显示时间,可以进入两种工作模式:设置模式和正常工作模式

1.确认模块:时间模块,按键模块,显示模块,延时。

时间模块:用定时器计时,逢60进1

按键模块:利用状态标志位进入设置模式,按键功能,设置时间,加、减调时,长按、短按实现不同的效果

显示模块:数码管,通过位选和段选显示相应的数字

延时:ms 和 us级别

  1. 写软件 

主函数在执行的时候,定时器中断在前台是一直在工作,定时器不断溢出(执行中断)清零溢出(执行中断)清零(只要你开启了),当定时器产生中断时,就会暂停主函数,这就称为定时器中断。等中断服务函数执行结束后,又会在主函数被打断的地方,继续执行主函数的内容。

1.这个定时器中断哪里有问题,中断不执行

void T0Init()

{

    TMOD=0x01;                            //定时器选择方式1、

    TH0=(65536-1000)/256;                    //设置初始值为65536-1000,定时1毫秒

    TL0=(65536-1000)%256;

    EA=1;                                   //开总中断

ET0=1;                                    //开启定时器/计数器1中断

TR0=1;

}

void IT_L(void) interrupt 1 using 1       //using 后是寄存器组编号,using

//不写,单片机会自己分配,但是要注意有两个中断时要避免冲突

{

    u8 time;

    time=0;      //这个变量应该设置为全局变量或者在这里将time设置为静态变量 

    static u8 time=0; 

    TH0=(65536-1000)/256;          //设置初始值为65536-1000,定时1毫秒

    TL0=(65536-1000)%256;

    time++;

    if(time==10)

    {

        time=0;

        t++;

}

闹钟函数分析

例1:将break放到if括号,到设定时间数码管熄灭,蜂鸣器不响说明程序有问题,程序进入了while循环却没有执行while里面的任何语句,程序一直在空循环,没有跳到主函数,所以也不会执行显示函数和按键扫描函数。重新写一遍又可以执行while循环的语句了。但是蜂鸣器一直响,只有按着按键蜂鸣器才不响,原因是按键按着的时候一直在执行if大括号语句的内容,break退出循环后,由于程序之前一直执行Alarm()的循环,没有执行时钟走时,故时钟的时间还等于设定的时间,接着又进入while循环,如此反复循环。

void Alarm()

{

    if((sec==sec1)&&(min==min1))   //到设定时间数码管熄灭,蜂鸣器响不停,

    {

        while(1)

        {

            Buzz=0;         

            if(key_set==0)

            {

               Buzz=1;

               break;                    

}              

        }

    }

    

}

例2:将break 写在if大括号外,不管if的条件成不成立,有没有执行if大括号的内容, 都会执行break;按键按下,蜂鸣器不响,弹起又响,在没有按键的情况下,蜂鸣器响一分钟就不响了。

void Alarm()

{

    if((sec==sec1)&&(min==min1))  

    {

        while(1)

        {

            Buzz=0;         

            if(key_set==0)

            {

               Buzz=1;                    

}

 break;              

        }

    }

    

}

优化:由于循环函数会使程序一直停在子函数的循环里,不能执行其他函数。另外,delay函数也是,会使程序停在那里。这些都是不好的。尽量少用!!!这里使用if条件语句代替。

 void Alarm()                    

 {

     if((sec==sec1)&&(min==min1))        //当时钟            

     {

  

             buzz=0;

     }

 }  

void ClrAlarm()

{

        if(key_add==0)

         {

             buzz=1;

      

         }

 }

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

typedef unsigned int u16;   //定义数据类型
typedef unsigned char u8;

u8 code shownum[18]={0xBF,0x86,0xDB,0xCF,0xE6,0xEd,0xFD,0x87,0xFF,0xEF,0xF7,0xFC,0xB9,0xF9,0xF1,0x00};//0-F对应的地址编码
u8 code ledbit[4]={0x0E,0x0D,0x0B,0x07};   //1-4位对应的地址编码
//u8 display[4]={show(0x01),show(0x3F),show(0x08),show(0x60)};

u8 sec=0,j=0,min=0,t=0,time;
u8 sec_set=30,min_set=1;
sbit a=P1^2;
sbit f=P1^4;
sbit b=P1^6;
sbit g=P1^7;
sbit d=P2^4;
sbit e=P2^5;
sbit c=P3^0;
sbit dp=P3^1;

sbit bit_1=P2^2;
sbit bit_2=P2^0;
sbit bit_3=P2^1;
sbit bit_4=P2^3;

sbit key_set=P3^3;
sbit key_add=P3^5;
sbit key_sub=P3^6;
sbit buzz=P3^7;
bit set=0;            //设置状态标志位
u8 alarm_flag=1;      //设置闹铃标志位      

void show(u8 showdata);
void delay_us(u8 us);
void delay_ms(u8 ms);
void choosebit(u8 led_bit);
void TimeMode();
void display(sec, min);
void KeyScan();
void Alarm();

/*********************************************/
/*    函数名:T1Int_s()                          */
/*    功能:定时器及相关中断设置函数               */
/*********************************************/
void T1Int_s()
{

    TMOD|=0x01;                             //定时器选择方式1,用|=防止配置冲突
    TH0=(65536-1000)/256;                     //设置初始值为65536-1000,定时1毫秒
    TL0=(65536-1000)%256; 
 //   PT0=1;                              //设置INT1~为高优先级中断
    EA=1;                                    //开总中断
    ET0=1;                                    //开启定时器/计数器1中断
    TR0=1;                                   //启动定时器1
    time=0;
}
/*********************************************/
/*    函数名:main                           */
/*    功能:主函数                            */
/*********************************************/
void main()
{
    T1Int_s(); 
    while(1)
    {
        
       display(sec, min);
        KeyScan(); 
        Alarm();
        if(set==0)             //如果key_set按下,进入设置模式,停止自动计时
        {
        TimeMode();           //自动计时  
      }
}
	
}
	
/*********************************************/
/*    函数名:IT_L(void) interrupt 1 using 1                         */
/*    功能:  中断服务子程序            */
/*********************************************/
void IT_L(void) interrupt 1 using 1
{    
    TH0=(65536-1000)/256;                     //设置初始值为65536-1000,定时1毫秒
    TL0=(65536-1000)%256; 
    time++;
    if(time==9)
   {
        time=0;
        t++;
   }
}


	
/*********************************************/
/*    函数名:shownum                           */
/*    功能:  显示a,b,c,d,e,f,g,dp 段            */
/*********************************************/
void show(u8 showdata) 
{
	a=showdata&0x01;
	b=(showdata&0x02)>>1;
	c=(showdata&0x04)>>2;
	d=(showdata&0x08)>>3;
	e=(showdata&0x10)>>4;
	f=(showdata&0x20)>>5;
	g=(showdata&0x40)>>6;
	dp=(showdata&0x80)>>7;
	
}

/*********************************************/
/*    函数名:showbit                         */
/*    功能:  选取要显示的数码管对应的位         */
/*********************************************/
void choosebit(u8 led_bit)
{
bit_1=led_bit&0x01;
bit_2=(led_bit&0x02)>>1;
bit_3=(led_bit&0x04)>>2;
bit_4=(led_bit&0x08)>>3;
}

/*********************************************/
/*    函数名:TimeMode                       */
/*    功能:  时间处理         */
/*********************************************/
void TimeMode()
{
   if(t==100)
   {
        t=0;
        sec++;
        if(sec==60)
        {
            buzz=0;
            delay_ms(10);
            sec=0;
            min++;
            if(min==60)
            {
                buzz=0;
                delay_ms(10);
                min=0;
            }
            buzz=1;
        }
        buzz=1;
        
    }
}
/*********************************************/
/*    函数名:Alarm()                       */
/*    功能:  闹钟及蜂鸣器         */
/*********************************************/
void Alarm()
{
    
    if(sec==sec_set&&min==min_set)
    {
        while(1)
        {
            buzz=!buzz;        
            if(key_set==0);
            break;                   //break退出循环后buzz的值依然是低电平,所以//会一直响,关不掉
        }
    }
    
}


/*****************************************************************/
/*    函数名:delay                                              */
/*    功能:  延时,单片机晶振位12M时,延时T=(us*2+5 )微秒         */
/*******************************************************************/

void delay_us(u8 us)                        //当us=248时,延时约等于0.5ms             
{
	
    while(us--);
	
}

void delay_ms(u8 ms)                                  
{
    while(ms--)                           //一个while循环,延时1ms
    {
        delay_us(248);
        delay_us(248);
    }
}

/*********************************************/
/*    函数名:display()                       */
/*    功能:  显示       */
/*********************************************/
void display(sec, min)
{
    choosebit(ledbit[3]);            
    show(shownum[sec%10]);          //显示秒的个位数
    delay_ms(1);                       //间隔秒扫描一次
    choosebit(ledbit[2]);
    show(shownum[sec/10]);            //显示秒的十位数
    delay_ms(1); 
    choosebit(ledbit[1]);
    show(shownum[min%10]);              //显示分的个位数
    delay_ms(1); 
    choosebit(ledbit[0]);
    show(shownum[min/10]);              //显示分的十位数
    delay_ms(1);  
    show(0x00);                           //消隐
}

/*********************************************/
/*    函数名: KeyScan()                       */
/*    功能:  按键扫描       */
/*********************************************/
void KeyScan()      

{
   u8 keytime=0;
	if(key_set==0)	//如果设置按键按下
	{
		delay_ms(10);		 //延时去抖
		if(key_set==0)		 //再次判断按键是否按下
		{
			buzz=0;		//蜂鸣器响
			delay_ms(10);
			buzz=1;	 //蜂鸣器关
			set=!set;	//设置的变量取反,等于1时进入设置状态
              TR0=!set;	//定时器0会在进入设置状态后关闭,退出设置状态后打开  
		}
        while(key_set==0);//等待按键释放
	}
	
	if(key_add==0&&set!=0)	  //在设置的状态下按下加
	{
		delay_ms(20);
		if(key_add==0&&set!=0)
		{
			buzz=0;		//蜂鸣器响
			delay_ms(10);
			buzz=1;	 //蜂鸣器关
             while(key_add==0&&set!=0)	  //再次判断加按键按下
            {
                keytime++;
                delay_ms(50);
                if(keytime==20)              //keytime加到200需要2s
                {
                    keytime=0;
                    sec++;
                    display(sec,min);          //实时显示时间
                    delay_ms(1);                  //时间不可太长,不然不能实时显示时间
                    if(sec==60)
                    {
                        sec=0;
                        min++;
                        if(min==60)
                        {
                            min=0;
                        }
                    }
                }
        keytime=0; //若未达到长按时间2s,则清零,防止多次短按累加计时
        sec++;    //注意sec++的位置是在while大括号内,如果在while大括号外,加1的效果不好
        if(sec==60)
        {
            sec=0;
            min++;
            if(min==60)
            {
                min=0;
            }
        }
        display(sec,min);  
        delay_ms(1);                  //时间不可太长,不然不能实时显示时间
		
      } 
	}
        while(key_add==0);		  //等待按键释放
  }
	if(key_sub==0&&set!=0)	  //在设置的状态下按下加
	{
		delay_ms(20);
		if(key_sub==0&&set!=0)
		{
			buzz=0;		//蜂鸣器响
			delay_ms(10);
			buzz=1;	 //蜂鸣器关
             while(key_sub==0&&set!=0)	  //再次判断加按键按下
            {
                keytime++;
                delay_ms(50);
                if(keytime==20)              //keytime加到200需要2s
                {
                    keytime=0;
                    sec--;
                    display(sec,min);          //实时显示时间,将显示函数写在后面会导致数码
                                          //管不能及时显示,按下按键数码管熄灭的情况
                    delay_ms(1);             //时间不可太长,不然不能实时显示时间
                    if(sec==0)
                    {
                        sec=59;
                        min--;
                        if(min==0)
                        {
                            min=60;
                          
                        }
                    }
                }
        keytime=0;  //若未达到长按时间2s,则清零,防止多次短按累加计时
        sec--;      //注意sec++的位置是在while大括号内,如果在while大括号外,加1的效果不好
        if(sec==0)
        {
            sec=59;
            min--;
            if(min==0)
            {
                min=60;
            }
        }
        display(sec,min);  
        delay_ms(1);                  //时间不可太长,不然不能实时显示时间
    }
		
	}
    while(key_sub==0);		  //等待按键释放
  }

}

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值