51单片机利用定时器和1602液晶显示实现一个计时器

51单片机利用定时器和1602液晶显示实现一个计时器

说明

1.平台:windows10 64位教育版

2.工具:普中科技HC6800-ES V2.0 、keil uVision 2

3.注意:芯片STC89C52,8位1602液晶显示

4.实现效果:

通电之后,1602开始显示计时,按下第一个按键进入设置,按下第二个按键设置时分秒的各个位置,按下第三个按键在对应的时分秒的位置数字加1,按下第四个按键在对应的时分秒的位置数字减1

原理

笔者参考的是郭天祥的《新概念51单片机C语言教程》和网上的普中科技的单片机的使用教程,具体使用教程,在哔哩哔哩上面搜“普中科技51单片机”也有。要找到能用的代码,网上是很多的,不过笔者是自己在明白原理之后,自己编写程序实现的,自己独立思考的过程很重要。
以下是普中科技的使用教程:详细的代码里面也有

流程

我是按照一个一个功能的实现来把代码加进去的,利用单片机上面的独立模块就可以实现独立测试。

1.按键检测

这里我用到的是4个独立按键,实现的功能是第一个按键按下进入时间设置,第二个按键按下设置时分秒,第三个按键按下对应的位置加1,第四个按键按下对应位置减1,注意,这里的其他独立按键起作用的前提是第一个按键要先按下进入设置,否则按键无效。
以下是代码:
I/O口声明

sbit key1 = P3^0;	//第二个按键
sbit key2 = P3^1;	//第一个按键
sbit key3 = P3^2;	//第三个按键
sbit key4 = P3^3;	//第四个按键

按键检测函数

void keyscan()
{

	if (key1 == 0)
	{
		delay10ms();
		if (key1 == 0)
		{
			//要实现的功能
			while(!key1);	//等待按键松开		
		}
	}
	if (key2 == 0)
	{
		delay10ms();
		if (key2 == 0)
		{
			//要实现的功能
			while(!key2);			
		}
	}
	if (key3 == 0)
	{
		delay10ms();
		if (key3 == 0)
		{
			//要实现的功能
			while(!key3);
		}
	}
	if (key4 == 0)
	{
		delay10ms();
		if (key4 == 0)
		{
			//要实现的功能
			while(!key4);
		}
	}
}

//延时10ms以消除按键抖动
void delay10ms()
{
	unsigned char a,b,c;
	for(c = 1;c > 0; c--)
		for(b = 38;b > 0; b--)
			for(a = 130;a>0;a--);
}

2.定时器中断

采用的是定时器0,工作方式2
以下是代码:

	TMOD = 0x02;	//定时器0,工作方式2
	TH0 = 0x9c;		//装入初值,高4位
	TL0 = 0x9c;		//装入初值,低4位
	EA = 1;		//打开总中断
	ET0 = 1;	//打开定时器中断
	TR0 = 1;	//启动定时器
void Timer() interrupt 1
{
	TH0 = 0x9c;	//重装初值
	TL0 = 0x9c;
	num++;	//num = 10000时为1s
}

3.1602液晶显示

这里使用是8位的1602液晶,如果有人用的是4位的,详细代码在之前的普中科技的官方教程里面有。
以下是代码:
I/O口声明

sbit LED_RS = P2^6;	//0为写命令,1为写数据
sbit LED_WR = P2^5;	//0为从1602液晶写数据,1为读数据
sbit LED_EN = P2^7;	//使能接口,置为0

初始化函数

void init()
{
	write_data(0x38,0);	//设置16*2显示,5*7点阵
	write_data(0x0c,0);	//设置开显示,不显示光标
	write_data(0x06,0);	//写一个字符后地址指针加1
	write_data(0x01,0);	//显示清零,数据指针清零
	write_data(0x80,0); //开始写的地址
}

写命令和数据函数,通过传入0和1来控制写命令或者数据

void write_data(uchar dat,uchar wr)
{
	if(wr == 0)
		LED_RS = 0;	//写命令
	else
		LED_RS = 1;	//写命令		
	LED_WR = 0;	
	LED_EN = 0;
	P0 = dat;
	delay(5);	//延时等待数据稳定
	LED_EN = 1;	
	delay(5);
	LED_EN = 0;	
}
void delay(uchar t)
{
	uchar i;
	for(;t>0;t--)
		for(i=199;i>0;i--);
}

在进行测试之前,要连接一下电路,因为对于普中的这款单片机,它是通过U2锁存器来控制数据方向的,如下图所示,要把三个排针中的最右边两个短接在一起。51单片机利用定时器和1602液晶显示实现一个计时器_第1张图片
独立模块测试完成之后就是进行完整的功能组合,即代码组合。

4.完整代码

#include
#define uchar unsigned char
#define uint unsigned int

sbit key1 = P3^0;	//第二个按键
sbit key2 = P3^1;	//第一个按键
sbit key3 = P3^2;	//第三个按键
sbit key4 = P3^3;	//第四个按键
sbit LED_RS = P2^6;	//用来确定是选择写命令还是数据
sbit LED_WR = P2^5;	//读写选择,1表示读,0表示写
sbit LED_EN = P2^7;	//使能信号,置为0
void keyscan();		//检测按键是否按下
void init();
void delay(uchar );
void write_data(uchar ,uchar );
void display(uchar ,uchar ,uchar );


//只有当按下第一个按键时(key_flag),其他按键才起作用
//position 用来记录光标位置
uchar sec,min,hour,key_flag,position;
uchar timer_op;//定时器启动和关闭标志
uchar timedis[8];//保存秒、分、时的各个位的数字
uint num;	//定时器计数使用

void main()
{
	init();
	while(1)
	{
		keyscan();			
		if(num == 10000)	//是否到了1s
		{
			sec++;
			if(sec > 59)
			{
				sec = 0;
				min++;
				if(min > 59)
				{
					min = 0;
					hour++;
					if(hour > 59)
						hour = 0;
				}					
			}
			num = 0;
			display(sec,min,hour);
		}	

	}
	
}

void init()
{
	//1602液晶显示初始化
	write_data(0x38,0); //设置16*2显示,8位数据口
	write_data(0x0f,0); //开显示,光标闪烁
	write_data(0x06,0); //写入一个字符,地址指针加1
	write_data(0x01,0); //显示清零,指针清零
	write_data(0x80,0); //写入的起始地址

	//定时器初始化
	TMOD = 0x02;	//设置定时器工作方式
	TH0 = 0x9c;		//装入初值,高四位
	TL0 = 0x9c;		//装入初值,低四位
	EA = 1;		//打开总中断
	ET0 = 1;	//打开定时器中断
	TR0 = 1;	//启动定时器
}

void keyscan()
{
	uchar i;
	if (key1 == 0)
	{
		delay(25);	//延时10ms
		if (key1 == 0)
		{
			position = 0;
			key_flag = 1;
			timer_op++;
			if(timer_op == 1)
				TR0 = 0;	//关闭定时器				
			else
			{
				TR0 = 1;	//开启定时器
				timer_op = 0;
				key_flag = 0;		//再按一次离开设置	
			}
			write_data(0x80,0);
			while(!key1);	//等待按键松开		
		}
	}
	if (key2 == 0)
	{
		delay(25);
		if (key2 == 0)
		{
			if(key_flag == 1)
			{
				add_num = 0;
				position++;
				if(position == 8)
					position = 0;
				write_data(position+0x80,0);	//更新光标位置				
			}
			while(!key2);			
		}
	}
	if (key3 == 0)
	{	//按下一次加1
		delay(25);
		if (key3 == 0)
		{
			if(key_flag == 1)
			{
				timedis[position]++;
				if((position == 1)||(position == 4)||(position == 7))
				{
					if(timedis[position] == 10)
						timedis[position] = 0;
				}
				else if((position == 0)||(position == 3)||(position == 6))
				{
					if(timedis[position] == 6)
						timedis[position] = 0;
				}
			}			
			hour = timedis[0]*10 + timedis[1];
			min = timedis[3]*10 + timedis[4];
			sec = timedis[6]*10 + timedis[7];
			//按下一次按键更新一次液晶显示
			write_data(0x80,0);
			for(i=0;i<8;i++)
				write_data(0x30+timedis[i],1);	
			while(!key3);
		}
	}
	if (key4 == 0)
	{	//按下一次减1
		delay(25);
		if (key4 == 0)
		{
			if(key_flag == 1)
			{
				timedis[position]--;
				if((position == 1)||(position == 4)||(position == 7))
				{
	
					if(timedis[position] == -1)
						timedis[position] = 9;
				}
				else if((position == 0)||(position == 3)||(position == 6))
				{
					if(timedis[position] == -1)
						timedis[position] = 5;
				}
			}
			hour = timedis[0]*10 + timedis[1];
			min = timedis[3]*10 + timedis[4];
			sec = timedis[6]*10 + timedis[7];
			write_data(0x80,0);
			for(i=0;i<8;i++)
				write_data(0x30+timedis[i],1);
			while(!key4);
		}
	}
}

void delay(uchar t)
{
	uchar i;
	for(;t>0;t--)
		for(i=199;i>0;i--);
}
void write_data(uchar dat,uchar wr)
{
	if(wr == 0)
		LED_RS = 0;	//写命令
	else
		LED_RS = 1;	//写数据
	LED_WR = 0;	
	LED_EN = 0;
	P0 = dat;
	delay(5);
	LED_EN = 1;	
	delay(5);
	LED_EN = 0;	
}

void Timer() interrupt 1
{
	TH0 = 0x9c;	//重装初值
	TL0 = 0x9c;
	num++;	
}

void display(uchar sec_,uchar min_,uchar hour_)
{
	uchar i;
	timedis[2] = 10;
	timedis[5] = 10;
	timedis[0] = hour_/10;
	timedis[1] = hour_%10;
	timedis[3] = min_/10;
	timedis[4] = min_%10;
	timedis[6] = sec_/10;
	timedis[7] = sec_%10;
	write_data(0x80,0);
	for(i=0;i<8;i++)
		write_data(0x30+timedis[i],1);
}

本人水平有限,如有错误或不当之处,望大家批评指正!

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