数字时钟与整点报时的问题 - C51

数字时钟与整点报时的问题 - C51

题目要求

1.利用8位数码管制作一个24小时制数字时钟,可以显示小时、分钟、秒,且中间有 ’ · ’ 或 ’ - '间隔。
2.小时、分钟可以通过按键进行调整。
3.每个整点蜂鸣器发出相应次数的声响,且每秒响1次,每次0.5秒。

变量声明

1位变量 LSA、LSB、LSC接在3-8译码器上,分别对应8个七段数码管的“使能”
8位变量 P3 的低4位对应4个独立按键,按键按下时为低电平
8位变量 P0 对应七段数码管每一段的电平

第一种实现方案

用 Timer0中断 实现时钟
用 Timer1 控制蜂鸣器每秒响1次,每次0.5秒
用 Timer2 产生蜂鸣器的振荡频率
考虑中断资源不够,采用轮询的方式检测按键

/**
*	用这种方式轮询按键,在Display执行的时间检测不到按键。
*	而Display()里又有跟按键消抖时间相近的延时时间。
*	实测按键检测效果并不好。
*	于是直接以Display()函数作为按键消抖的延时。
*	把按键前后两次检测分别放在Display()的前和后。
*	实测效果好很多。
**/
void main()
{
	Init();
	while(1){
		key_check();
		Display();
	}
}

总代码

#include "reg52.h"			

typedef unsigned char u8;
typedef unsigned int u16;

sbit beep=P1^5;
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
sbit k1=P3^1;
sbit k2=P3^0;
sbit k3=P3^2;
sbit k4=P3^3;

u8 sec=0,min=0,hour=0,Rnd;
u8 flag;

u8 code smgduan[12]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x40};

u8 time[8] = {0};

void delay(u16 i){
		while(i--);
}

void Init()
{
	P3=0x0F;
	TMOD=0X11;
	
	TH0=0XFC;	
	TL0=0X18;	
	TH1=0XFC;
	TL1=0X18;
	TH2=0XFC;
	TL2=0X18;
	
	ET0=1;
	//ET1=1;
	//ET2=1;
	
	EA=1;
	
	TR0=1;
	TR1=1;	 
	TR2=1;
}

void Display()
{
	u8 i,num;
	for(i=0;i<8;i++)
	{
		switch(i)	 //位选,选择点亮的数码管,
		{
		   case(0):
				LSA=1;LSB=1;LSC=1;num=hour/10; break;//显示第0位
			case(1):
				LSA=0;LSB=1;LSC=1;num=hour%10; break;//显示第1位
			case(2):
				LSA=1;LSB=0;LSC=1;num=10; break;//显示第2位
			case(3):	
				LSA=0;LSB=0;LSC=1;num=min/10; break;//显示第3位
			case(4):
				LSA=1;LSB=1;LSC=0;num=min%10; break;//显示第4位
			case(5):
				LSA=0;LSB=1;LSC=0;num=10; break;//显示第5位
			case(6):
				LSA=1;LSB=0;LSC=0;num=sec/10; break;//显示第6位
			case(7):
				LSA=0;LSB=0;LSC=0;num=sec%10; break;//显示第7位	
		}
		P0 = smgduan[num];
		delay(100); 
		P0&=0x00;
	}
}

void key_check(){
	if(P3==0x0F) delay(1000);
	else {
		delay(1000);
		return;
	}
	P3 &= 0x0F;
	if(!(P3&0x01)){					//press k2
		min += 1;	
		if(min>59) min=0;
	}
	else if(!(P3&0x02)){			//press k1
		sec += 1;
		if(sec>59) sec=0;
	}
	else if(!(P3&0x04)){			//press k3
		hour += 1;
		if(hour>23) hour=0;
	}
	else if(!(P3&0x08)){			//press k4    func: reset
		sec = 0;
		min = 0;
		hour = 0;
	}
	return;
}

void main()
{	
	Init();
	while(1){
		//key_check();
		flag=0;
		if(k1 & k2 & k3 & k4) flag=1;
		Display();
		if(!sec && !min && hour) {
			ET1=1;
			Rnd=2*hour-2;
		}
		if(flag){
			if(!k2){				//press k2
				min += 1;	
				if(min>59) min=0;
			}	
			else if(!k1){			//press k1
				sec += 1;
				if(sec>59) sec=0;
			}
			else if(!k3){			//press k3
				hour += 1;
				if(hour>23) hour=0;
			}
			else if(!k4){			//press k4    func: reset
				sec = 0;
				min = 0;
				hour = 0;
			}
		}	
	}
}

void Timer0() interrupt 1					//need to adjust to 1 sec
{
	static u16 x;
	TH0=0XFC;	
	TL0=0X18;
	x++;
	if(x>=1000)
	{
		x=0;
		sec++;
	}	
		if(sec>59) {sec=0;min++;}
		if(min>59) {min=0;hour++;}
		if(hour>23) hour=0;
}

void Timer1() interrupt 3
{
	static u16 y;
	TH1=0XFC;
	TL1=0X18;
	y++;
	if(y>=500)
	{
		y=0;
		if(!Rnd) ET1=0;
		Rnd--;
	}
}

void Timer2() interrupt 5
{
	TH2=0XFC;
	TL2=0X18;
	beep=~beep;
}

方案一的问题

当蜂鸣器在报时的时候,会频繁产生中断,导致数码管刷新频率不够,出现肉眼可见的闪烁。

第二种实现方案

为了解决闪烁的问题,我们回看方案一。
方案一里有一大部分CPU时间开销在了delay()上面,所以考虑将delay()的时间利用上。
于是将 beep=~beep; 加在delay()等处来产生蜂鸣器的震荡。
于是有了下面的代码。

#include "reg52.h"			

typedef unsigned char u8;
typedef unsigned int u16;

sbit beep=P1^5;
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
sbit k1=P3^1;
sbit k2=P3^0;
sbit k3=P3^2;
sbit k4=P3^3;

u8 sec=0,min=0,hour=0,Rnd;		//Rnd -> Round 用于计量蜂鸣器响几次
u8 flag,EB=0;					//EB -> Enable Beep

u8 code smgduan[12]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x40};

u8 time[8] = {0};

void delay(u16 i){
	while(i--);
	if(EB) beep=~beep;			
}

void Init()
{
	P3=0x0F;
	TMOD=0X11;
	
	TH0=0XFC;	
	TL0=0X18;	
	TH1=0XFC;
	TL1=0X18;
	TH2=0XFC;
	TL2=0X18;
	
	ET0=1;
	//ET1=1;
	//ET2=1;
	
	EA=1;
	
	TR0=1;
	TR1=1;	 
	TR2=1;
}

void Display()
{
	u8 i,num;
	for(i=0;i<8;i++)
	{
		switch(i)	 //位选,选择点亮的数码管,
		{
		   case(0):
				LSA=1;LSB=1;LSC=1;num=hour/10; break;//显示第0位
			case(1):
				LSA=0;LSB=1;LSC=1;num=hour%10; break;//显示第1位
			case(2):
				LSA=1;LSB=0;LSC=1;num=10; break;//显示第2位
			case(3):	
				LSA=0;LSB=0;LSC=1;num=min/10; break;//显示第3位
			case(4):
				LSA=1;LSB=1;LSC=0;num=min%10; break;//显示第4位
			case(5):
				LSA=0;LSB=1;LSC=0;num=10; break;//显示第5位
			case(6):
				LSA=1;LSB=0;LSC=0;num=sec/10; break;//显示第6位
			case(7):
				LSA=0;LSB=0;LSC=0;num=sec%10; break;//显示第7位	
		}
		P0 = smgduan[num];
		delay(50);		
		P0&=0x00;
	}
}

void main()
{	
	Init();
	while(1){
		if(EB) beep=~beep;
		flag=0;
		if(k1 & k2 & k3 & k4) flag=1;
		Display();
		if(!sec && !min && hour) {
			ET1=1;
			Rnd=2*hour-2;
		}
		if(flag){
			if(!k2){				//press k2
				min += 1;	
				if(min>59) min=0;
			}	
			else if(!k1){			//press k1
				sec += 1;
				if(sec>59) sec=0;
			}
			else if(!k3){			//press k3
				hour += 1;
				if(hour>23) hour=0;
			}
			else if(!k4){			//press k4    func: reset
				sec = 0;
				min = 0;
				hour = 0;
			}
		}	
	}
}

void Timer0() interrupt 1					//need to adjust to 1 sec
{
	static u16 x;
	TH0=0XFC;	
	TL0=0X18;
	x++;
	if(x>=1000)
	{
		x=0;
		sec++;
	}	
		if(sec>59) {sec=0;min++;}
		if(min>59) {min=0;hour++;}
		if(hour>23) hour=0;
}

void Timer1() interrupt 3
{
	static u16 y;
	TH1=0XFC;
	TL1=0X18;
	y++;
	if(y>=500)
	{
		y=0;
		if(!Rnd) ET1=0;
		EB=~EB;		
		Rnd--;
	}
}

方案二的问题

无法控制蜂鸣器震荡的频率,希望有人可以提供一个更好的思路。

你可能感兴趣的:(单片机)