STC89C52单片机——定时器闹钟

一、功能概述

        概述:通过按下矩阵键盘输入数字让数码管显示,然后按下独立键盘通过定时器开始倒计时,倒计时结束蜂鸣器响。

二、所使用的功能模块

        矩阵键盘、独立键盘、蜂鸣器、数码管

三、编程过程中所遇到的问题及改进措施

        问题1: 如何通过矩阵键盘点亮数码管并将按键值传递给定时模块?

        措施:(代码模块如下)

                (1)矩阵键盘扫描 0~15。

                注意:<1>受单片机I/O口数量所限,注意到矩阵键盘第一排和独立键盘共用一组I/O口,所以如果忽略,当按下矩阵键盘第一排后会自动进入倒计时模块。

                           <2>扫描末尾要将P3口拉高,否则在程序执行过程中由于程序段开始时进行列扫描将P3口部分拉高,导致矩阵键盘二、三、四排其中一个按键被按下时都有可能自动进入倒计时模块。

uchar KeyScan(uint KeyValue)
{
	P3 = 0XF0;//列扫描  
	if(P3 != 0XF0)//判断按键是否被按下
	{
		delay(10);//软件消抖10ms
		if(P3 != 0XF0)//判断按键是否被按下
		{
			switch(P3) //判断那一列被按下
			{
				case 0xe0:	KeyValue = 0;	break;//第一列被按下
				case 0xd0:	KeyValue = 1;	break;//第二列被按下
				case 0xb0:	KeyValue = 2;	break;//第三列被按下
				case 0x70:	KeyValue = 3;	break;//第四列被按下 
			}
			P3 = 0X0F;//行扫描
			switch(P3) //判断那一行被按下
			{
				case 0x0e:	KeyValue = KeyValue;	  break;//第一行被按下
				case 0x0d:	KeyValue = KeyValue + 4;  break;//第二行被按下
				case 0x0b:	KeyValue = KeyValue + 8;	break;//第三行被按下
				case 0x07:	KeyValue = KeyValue + 12;	break;//第四行被按下
			}
			while(P3 != 0X0F);//松手检测  /***重点***/ 		
		}	
	}
	P3=0xff;  /***重点***/
	return KeyValue;
}

                (2)按键数字显示和值传递

uchar Key_input(uint z)
{
	uint temp1=0;
	uint temp2=0;
	while(!temp1)
	{
		P0=0xfe;
		WE=1;
		WE=0;
		temp1=KeyScan(temp1);
		P0=tabel[temp1];
		DU=1;
		DU=0;
		temp1=temp1 * 10;
	}
	while(!temp2)
	{
		P0=0xfd;
		WE=1;
		WE=0;			  
		temp2=KeyScan(temp2);
		P0=tabel[temp2];
		DU=1;
		DU=0;
	}
	z=temp1+temp2;
	return z;		
}

        问题2: 如何精确控制倒计时的时间间隔(1s)?

        措施:(代码模块如下)

                (1) 使用定时器0,通过配置16位定时器的初始值,溢出一次为50ms,溢出二十次即为1s。

                (2)具体转换步骤:

                        <1> 50ms=50 000us

                        <2>一个机器周期为1.085us(晶振频率为11.0592)

                        <3>50 000/1.085=46082 个机器周期

                        <4>16位定时器,1111 1111 1111 1111 转换为十进制为65535,所以65535-46082=19453   转换为二进制为4BFD,  故高八位为 0x4B, 低八位为0xFD.

void timer0() interrupt 1 //T0溢出中断,中断入口为1
{
     TH0=(65535-46080)/256;	//高四位
	 TL0=(65535-46080)%256;	//低四位
	 m++;
}
//定时器开关
void timer_open()
{
	TMOD=0x01; //定时器工作模式1,16位定时器计数模式
	TR0=1;	//启动定时器0
	TH0=(65536-46080)/256;//4bfd
	TL0=(65536-46080)%256;//定时50ms
	EA=1; //cpu的总中断允许控制位,开放中断	
	ET0=1; //T0的溢出中断允许?				
}

                (4)主程序段

                        *注意标志位flag的使用


void main()
{	
	uchar a1,a0;
	uchar flag=0;
	while(1)  /***重点***/
	{
		if(flag==0)  /***重点***/
		{
			Sec=Key_input(Sec);
			flag=1;   /***重点***/	
		}
		if(flag==1)  /***重点***/
		{
			if(Key_s2==0)  
			{			
				delay(20);
				if(Key_s2==0)
				{	
					timer_open();
					while(1)
					{	
						if(m==20) //中断20次,1秒
						{
					      m=0;//m置零
					      Sec--;//秒减一
						}
						a0=Sec%10;//实时显示十位数字
						a1=Sec/10;//实时显示个位数字
						display(a1,a0); 
						if(Sec==0)//倒计时结束
						{
						   TR0=0;//关闭定时器0
						   beep=0;
						}
					}
				}
			}
		}
	}	
}

 //完整代码如下:

#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
sbit DU = P2^6;
sbit WE = P2^7;
sbit beep=P2^3;
sbit Key_s2=P3^0;
uint i,m,KeyValue;
uint Sec;
//共阴数码管段选表
uchar  code tabel[]= {
//0		1	  2     3          
0x3F, 0x06, 0x5B, 0x4F,
//4     5     6     7
0x66, 0x6D, 0x7D, 0x07, 
//8		9     A     B
0x7F, 0x6F, 0x77, 0x7C,
//C	    D	  E	    F		 
0x39, 0x5E, 0x79, 0x71};

void delay(uint z)
{
	uint x,y;
	for(x = z; x > 0; x--)
		for(y = 114; y > 0 ; y--); 		
}
//定时器0初始化
void timer0() interrupt 1 //T0溢出中断,中断入口为1
{
     TH0=(65535-46080)/256;	//高四位
	 TL0=(65535-46080)%256;	//低四位
	 m++;
}
//定时器开关
void timer_open()
{
	TMOD=0x01; //定时器工作模式1,16位定时器计数模式
	TR0=1;	//启动定时器0
	TH0=(65536-46080)/256;//4bfd
	TL0=(65536-46080)%256;//定时50ms
	EA=1; //cpu的总中断允许控制位,开放中断	
	ET0=1; //T0的溢出中断允许位			
}
//4*4矩阵键盘扫描
uchar KeyScan(uint KeyValue)
{
	P3 = 0XF0;//列扫描  
	if(P3 != 0XF0)//判断按键是否被按下
	{
		delay(10);//软件消抖10ms
		if(P3 != 0XF0)//判断按键是否被按下
		{
			switch(P3) //判断那一列被按下
			{
				case 0xe0:	KeyValue = 0;	break;//第一列被按下
				case 0xd0:	KeyValue = 1;	break;//第二列被按下
				case 0xb0:	KeyValue = 2;	break;//第三列被按下
				case 0x70:	KeyValue = 3;	break;//第四列被按下 
			}
			P3 = 0X0F;//行扫描
			switch(P3) //判断那一行被按下
			{
				case 0x0e:	KeyValue = KeyValue;	  break;//第一行被按下
				case 0x0d:	KeyValue = KeyValue + 4;  break;//第二行被按下
				case 0x0b:	KeyValue = KeyValue + 8;	break;//第三行被按下
				case 0x07:	KeyValue = KeyValue + 12;	break;//第四行被按下
			}
			while(P3 != 0X0F);//松手检测 		
		}	
	}
	P3=0xff; 
	return KeyValue;
}

uchar Key_input(uint z)
{
	uint temp1=0;
	uint temp2=0;
	while(!temp1)
	{
		P0=0xfe;
		WE=1;
		WE=0;
		temp1=KeyScan(temp1);
		P0=tabel[temp1];
		DU=1;
		DU=0;
		temp1=temp1 * 10;
	}
	while(!temp2)
	{
		P0=0xfd;
		WE=1;
		WE=0;			  
		temp2=KeyScan(temp2);
		P0=tabel[temp2];
		DU=1;
		DU=0;
	}
	z=temp1+temp2;
	return z;		
}

void display(uchar sh,uchar g)
{
	P0=tabel[sh];
	DU=1;
	DU=0;
	P0=0xfe;
	WE=1;
	WE=0;
	delay(5);

	P0=tabel[g];
	DU=1;
	DU=0;
	P0=0xfd;
	WE=1;
	WE=0;
	delay(5);
}

void main()
{	
	uchar a1,a0;
	uchar flag=0;
	while(1)  
	{
		if(flag==0)  
		{
			Sec=Key_input(Sec);
			flag=1;   
		}
		if(flag==1)  
		{
			if(Key_s2==0)  
			{			
				delay(20);
				if(Key_s2==0)
				{	
					timer_open();
					while(1)
					{	
						if(m==20) 
						{
					      m=0;
					      Sec--;
						}
						a0=Sec%10;
						a1=Sec/10;
						display(a1,a0); 
						if(Sec==0)//
						{
						   TR0=0;
						   beep=0;
						}
					}
				}
			}
		}
	}	
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

活_在树上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值