基于LCD1602的秒表计数器


  1. 写这篇文章前,我刚刚完成LCD1602的学习,对它也有了一些感悟,当然也存在一些困惑,在这里,我将尽量把我的感悟详细的保存。

  2. LCD1602
    1602分为8引脚的和4引脚的,所以,我们在编写它的**.h**文件时需要分情况讨论。
    而整个.h文件需要包含写命令的函数以及写数据的函数。这两个函数大体一致,区别在于RS引脚的设置。当它为0时,意味着写入命令,而当它为1时,则是写入数据。需要注意的是,最开始的时候,E和R/W都是低电平。但随着数据(命令)的输入,变化的只有E。当E为下降沿时,数据(命令)发出。基于LCD1602的秒表计数器_第1张图片
    以下就是LCD1602的.h文件全部:

#include "lcd.h"


void Lcd1602_Delay1ms(uint c)  
{
    uchar a,b;
	for (; c>0; c--)
	{
		 for (b=199;b>0;b--)
		 {
		  	for(a=1;a>0;a--);
		 }      
	}
    	
}

/*******************************************************************************
 1. 函 数 名         : LcdWriteCom
 2. 函数功能		   : 向LCD写入一个字节的命令
 3. 输    入         : com
 4. 输    出         : 无
*******************************************************************************/
#ifndef 	LCD1602_4PINS	 //当没有定义这个LCD1602_4PINS时
void LcdWriteCom(uchar com)	  //写入命令
{
	LCD1602_E = 0;     //使能
	LCD1602_RS = 0;	   //选择发送命令
	LCD1602_RW = 0;	   //选择写入
	
	LCD1602_DATAPINS = com;     //放入命令
	Lcd1602_Delay1ms(1);		//等待数据稳定

	LCD1602_E = 1;	          //写入时序
	Lcd1602_Delay1ms(5);	  //保持时间
	LCD1602_E = 0;
}
#else 
void LcdWriteCom(uchar com)	  //写入命令
{
	LCD1602_E = 0;	 //使能清零
	LCD1602_RS = 0;	 //选择写入命令
	LCD1602_RW = 0;	 //选择写入

	LCD1602_DATAPINS = com;	//由于4位的接线是接到P0口的高四位,所以传送高四位不用改
	Lcd1602_Delay1ms(1);

	LCD1602_E = 1;	 //写入时序
	Lcd1602_Delay1ms(5);
	LCD1602_E = 0;

	LCD1602_DATAPINS = com << 4; //发送低四位
	Lcd1602_Delay1ms(1);

	LCD1602_E = 1;	 //写入时序
	Lcd1602_Delay1ms(5);
	LCD1602_E = 0;
}
#endif
/*******************************************************************************
 5. 函 数 名         : LcdWriteData
 6. 函数功能		   : 向LCD写入一个字节的数据
 7. 输    入         : dat
 8. 输    出         : 无
*******************************************************************************/		   
#ifndef 	LCD1602_4PINS		   
void LcdWriteData(uchar dat)			//写入数据
{
	LCD1602_E = 0;	//使能清零
	LCD1602_RS = 1;	//选择输入数据
	LCD1602_RW = 0;	//选择写入

	LCD1602_DATAPINS = dat; //写入数据
	Lcd1602_Delay1ms(1);

	LCD1602_E = 1;   //写入时序
	Lcd1602_Delay1ms(5);   //保持时间
	LCD1602_E = 0;
}
#else
void LcdWriteData(uchar dat)			//写入数据
{
	LCD1602_E = 0;	  //使能清零
	LCD1602_RS = 1;	  //选择写入数据
	LCD1602_RW = 0;	  //选择写入

	LCD1602_DATAPINS = dat;	//由于4位的接线是接到P0口的高四位,所以传送高四位不用改
	Lcd1602_Delay1ms(1);

	LCD1602_E = 1;	  //写入时序
	Lcd1602_Delay1ms(5);
	LCD1602_E = 0;

	LCD1602_DATAPINS = dat << 4; //写入低四位
	Lcd1602_Delay1ms(1);

	LCD1602_E = 1;	  //写入时序
	Lcd1602_Delay1ms(5);
	LCD1602_E = 0;
}
#endif
/*******************************************************************************
 9. 函 数 名       : LcdInit()
 10. 函数功能		 : 初始化LCD屏
 11. 输    入       : 无
 12. 输    出       : 无
*******************************************************************************/		   
#ifndef		LCD1602_4PINS
void LcdInit()						  //LCD初始化子程序
{
 	LcdWriteCom(0x38);  //开显示
	LcdWriteCom(0x0c);  //开显示不显示光标
	LcdWriteCom(0x06);  //写一个指针加1
	LcdWriteCom(0x01);  //清屏
	LcdWriteCom(0x80);  //设置数据指针起点
}
#else
void LcdInit()						  //LCD初始化子程序
{
	LcdWriteCom(0x32);	 //将8位总线转为4位总线
	LcdWriteCom(0x28);	 //在四位线下的初始化
	LcdWriteCom(0x0c);  //开显示不显示光标
	LcdWriteCom(0x06);  //写一个指针加1
	LcdWriteCom(0x01);  //清屏
	LcdWriteCom(0x80);  //设置数据指针起点
}
#endif

这个部分是购买赠与资料里面的,在我看来,我们在使用1602的过程,可以直接把这个模块拿来,至于里面每一行每一个函数,我们只需要知道是干嘛的即可。

  1. 秒表计数器
    计数器讲究的就是计时精准,所以我们不可避免的用到了定时计数器t0;
    在这里我稍微讲一下如何使用:
void Int0int(){
	TMOD = 0x01;   //t0计数器,工作方式1
	TH0=0xfc;	   //设置初值,在这里,我设置为1ms
	TL0=0x18;
	ET0=1;		  //下面三行,是使用t0所需要打开的“开关”
	TR0=1;
	EA=1;
}

为了实现1秒定时,我需要在触发t0中断函数里面,进行相应的循环次数,以及计算相应的时,分,秒;

void Int0() interrupt 1		 //这个数字1我理解为t0的中断级别
{
    TH0=0XFC;				//重新赋初值
	TL0=0X18;
	j++;
	if(j==1000)
	{
	   j=0;
	   s++;
	   if(s==60)
	   {
	   	  s=0;
		  m++;
		  if(m==60)
		  {
		  	 m=0;
			 h++;
			 if(h==24)
			 {
				h=0;
			 }
		  }
	   }
	}
	   
}

在这里,我们就完成了整个计数功能;但我们需要将这些数字显示到1602中。在这里,有一个需要注意的地方,但也是我的疑惑。

u8 zifu[]=" abcdabcdabcdef "  ;
u8 zifu1[6];
u8 num[] = "0123456789" ;//num[]={0,1,2,3,4,5,6,7,8,9};这样写会导致无法显示?
`
如果有谁能为我解答注释的疑惑,希望可以在评论区留下,感谢。
```c
void display(){
   u8 i;
   zifu1[0]	= num[s%10];
   zifu1[1] = num[s/10];
   zifu1[2] = num[m%10];
   zifu1[3] = num[m/10];
   zifu1[4] = num[h%10];
   zifu1[5] = num[h/10];
   for(i=0;i<16;i++)
	   {
	      LcdWriteData(zifu[i]);
	   }
   LcdWriteCom(0x40+0x80);//将我们输入的位置起始点 设置为第二行	
   for(i=0;i<6;i++)
	   {
	        LcdWriteData(zifu1[5-i]);
	   }
}

到这一步,整个功能基本就完成了,如果直接RUN我的代码,你们可能会发现,第二行除了显示计时的数值外,还有几个字母。这是因为,我的第二个for循环只有6次,所以自然而然,会显示第一个for循环的部分字母。

  1. 总结
    这篇文章主要是为了学习1602的用法,最后的结果我并不刻意追求美观。
    哦对了,我们不能直接“h/10”这类表达式的结果赋值给zifu1,那样同样会导致显示乱码,不过你可以试试加上0x30。
    下面是完整的代码:
#include "reg52.h"
#include "lcd.h"
#include "intrins.h"

typedef unsigned char u8;
typedef unsigned int u16;

u16 j,s,m,h;

u8 zifu[]=" abcdabcdabcdef "  ;
u8 zifu1[6];
u8 num[] = "0123456789" ;//num[]={0,1,2,3,4,5,6,7,8,9};这样写会导致无法显示。

void Int0int(){
	TMOD = 0x01;   //t0计数器,工作方式1
	TH0=0xfc;	   //设置初值,在这里,我设置为1ms
	TL0=0x18;
	ET0=1;		  //下面三行,是使用t0所需要打开的“开关”
	TR0=1;
	EA=1;
}

void Int0() interrupt 1		 //这个数字1我理解为t0的中断级别
{
    TH0=0XFC;				//重新赋初值
	TL0=0X18;
	j++;
	if(j==1000)
	{
	   j=0;
	   s++;
	   if(s==60)
	   {
	   	  s=0;
		  m++;
		  if(m==60)
		  {
		  	 m=0;
			 h++;
			 if(h==24)
			 {
				h=0;
			 }
		  }
	   }
	}
	   
}

void display(){
   u8 i;
   zifu1[0]	= num[s%10];
   zifu1[1] = num[s/10];
   zifu1[2] = num[m%10];
   zifu1[3] = num[m/10];
   zifu1[4] = num[h%10];
   zifu1[5] = num[h/10];
   for(i=0;i<16;i++)
	   {
	      LcdWriteData(zifu[i]);
	   }
   LcdWriteCom(0x40+0x80);//将我们输入的位置起始点 设置为第二行	
   for(i=0;i<6;i++)
	   {
	        LcdWriteData(zifu1[5-i]);
	   }
}

void main(){
	LcdInit();
	Int0int();
	while(1)	
	{
	   display();
	}
}

如果我哪里说的不对,或者你在进行这个实验的过程中遇到了一些问题,都请在评论区留言,谢谢。

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