DS1302时钟与LCD1602液晶显示

DS1302时钟

    DS1302是一个实时时钟芯片,它在实时显示时间中应用。它可以对年、月、日、周、时、分、秒进行计时。

DS1302的引脚及原理图如下:

DS1302时钟与LCD1602液晶显示_第1张图片

    它与CPU的连接需要SCLK(7)、I/O(6)和RST(5)三个引脚,VCC2和VCC1分别为主电源和备份电源,X1和X2分别接外部振荡器。在于CPU的通信中I/O用来传输数据,RST作为复位信号,SCLK用来控制数据字节的读写。


DS1302的读写时序如下:

DS1302时钟与LCD1602液晶显示_第2张图片

    程序中对芯片的操作主要是参考芯片的时序状态,时序中说明只要有对数据的操作RST就需要置1,在读数据的前8个时钟中在SCLK处于高电平的过程中会获取控制字信息,此过程I/O数据不会变化,而在低电平的过程中I/O数据进行改变;而到后8个时钟会读数据,对于SCLK的控制与前8个时钟相反。对于写数据的过程与读的过程相似只是后面读写的SCLK时钟控制信号不同。


DS1302的时钟寄存器如下:


    第6位用来选择时钟或RAM,第0位用来做读写的选择,对于第1、2、3为从000到111依次对应秒、分、时、日、月、周、年和控制字地址。


LCD1602液晶

    LCD1602它是一种专门用来显示字母、数字、符号等的点阵型液晶模块。用它来显示需要的字母、数字和符号等会比数码管和LED点阵都要方便。

LCD1602的引脚图如下:

DS1302时钟与LCD1602液晶显示_第3张图片

    引脚1、2为地和电源,引脚4为RS用来控制数据和命令选择,引脚5为R/W用来控制读写的选择控制,引脚6为使能控制信号。从第7到14分别对应数据的8个位。


LCD1602的读写时序如下:

写时序:

DS1302时钟与LCD1602液晶显示_第4张图片

读时序:

DS1302时钟与LCD1602液晶显示_第5张图片

    RS控制数据和命令的选择,读写数据时置高电平否则置低电平;R/W用来控制读写的选择,写操作时为低电平(第1个图),读操作时为高电平(第2个图);读写过程需要E给一个高脉冲。


LCD1602的状态字和相应指令:

    状态字

DS1302时钟与LCD1602液晶显示_第6张图片

    前6位对应数据的地址,第7位为读写的使能控制位。


    相应指令:

    模式设定指令为:0x38。清屏显示为0x01;数据指针清零为0x02。

    显示开关及光标的设置:

DS1302时钟与LCD1602液晶显示_第7张图片


程序

下边是利用LCD1602显示DS1302时钟信息的例程。

    程序分为4个文件信息分别如下,对于其中的内容可以参考相关注释信息:

1、1602.h文件(在1602.c中作为引用),用来定义LCD1602的相关引脚和函数声明。

typedef bit bool;

sbit RS = P2^6;	   //复位端
sbit RW = P2^5;	   //写数据端
sbit EN = P2^7;	   //使能端
#define uint unsigned int 
#define uchar unsigned char

void delay(int i);
bit lcd_bz();
void lcd_wcmd(int cmd);
void lcd_showstring(uchar r,uchar c,uchar *str);
void lcd_wdat(uchar dat);
void lcd_init();

2、1602.c文件,其中含有对LCD1602初始化和数据及命令的读写控制。

#include 
#include 
#include "1602.h"

//延时函数
void delay(int i)
{
	int j;
	while(i--)
	{
		for(j=0;j<250;j++)
		{
			_nop_();
			_nop_();
			_nop_();
			_nop_();
		}
	}
}

//侧忙,判断LCD是否为忙。
bool lcd_bz()
{
	bool result;
	RS = 0;
	RW = 1;
	EN = 1;
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	result = (bool)(P0 & 0x80);//检测P0最高位是否为1.
	EN = 0;
	return result;	//返回判断的结果。	
}

//写命令函数。
void lcd_wcmd_8bit(int cmd)
{
	while(lcd_bz());
	RS = 0;
	RW = 0;
	EN = 0;		//先为低电平。
	_nop_();
	_nop_();
	P0 = cmd;	 //获得数据。
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	EN = 1;		 //将电平拉高。
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	EN = 0;		 //再拉低。
}  

//写命令函数。
void lcd_wcmd(int cmd)
{
	while(lcd_bz());
	RS = 0;
	RW = 0;
	EN = 0;	   //先为低电平。
	_nop_();
	_nop_();
	P0 = cmd;	//获得高四位数据。
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	EN = 1;		//拉高。
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	EN = 0;		//再拉低。

	P0 = (cmd & 0x0f)<<4;  //再获得低四位数据。
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	EN = 1;		 //将电平拉高。
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	EN = 0;		  //再拉低。
}


void lcd_showstring(uchar r,uchar c,uchar *str)
{
	uchar i=0;
	code uchar DDRAM[] = {0x80,0xc0};	 //设定显示的位置。
	lcd_wcmd(DDRAM[r] | c);
	for(i=0;str[i] && i<16;i++)
		lcd_wdat(str[i]);
	for(;i<16;i++)
		lcd_wdat(' ');
}

//数据写入的函数。
void lcd_wdat(uchar dat)
{
	while(lcd_bz());
	RS = 1;
	RW = 0;
	EN = 0;		//先处于低电平。
	P0 = dat;	 //获得数据高四位。
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	EN = 1;
	_nop_();
	_nop_();	  //再产生一个负脉冲。
	_nop_();
	_nop_();
	EN = 0;

	RS = 1;
	RW = 0;
	EN = 0;
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	P0 = (dat & 0x0f)<<4;	//同理获得低四位。
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	EN = 1;
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	EN = 0;
}

//LCD初始化。
void lcd_init()
{
	lcd_wcmd_8bit(0x38);
	delay(1);
	lcd_wcmd_8bit(0x38);
	delay(1);
	lcd_wcmd_8bit(0x38);
	delay(1);
	lcd_wcmd(0x38);
	delay(1);
	lcd_wcmd(0x0c);
	delay(1);
	lcd_wcmd(0x02);
	delay(1);
	lcd_wcmd(0x01);
	delay(1);
}

3、DS1302时钟.c文件,其中含有DS1302时钟的引脚定义及时钟信息的读写等相关操作。

#include 
#include 

sbit SCK = P3^6;  //时钟线
sbit IO = P3^4;	  //数据线
sbit RST = P3^5;  //DS1302复位线。

bit ReadRTC_Flag; //读DS1302的标志。

//七项数据:秒分时日月周年。
unsigned char l_tmpdate[7] = {0,0,12,15,5,3,8};
//用来存放转化好的时间数据。
unsigned char l_tmpdisplay[8];

//7个数据的写地址。
code unsigned char write_rtc_add[7] = {0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};
//7个数据的读地址。
code unsigned char read_rtc_add[7] = {0x81,0x83,0x85,0x87,0x89,0x8b,0x8d};

//函数的声明。
void Write_Ds1302_byte(unsigned char temp);
void write_Ds1302(unsigned char add,unsigned char dat);
unsigned char Read_Ds1302(unsigned char add);
void Read_RTC(void);
void Set_RTC(void);
void InitTIMER0(void);

//定时器的初始化。
void InitTIMER0(void)
{
	TMOD |= 0x01;
	TH0 = 0xef;
	TL0 = 0xf0;
	ET0 = 1;
	TR0 = 1;
	EA = 1;
}

//向1302中发送一个字节数据。
void Write_Ds1302_Byte(unsigned char temp)
{
	unsigned char i;
	for(i=0;i<8;i++)  //循环8位依次写入数据。
	{
		SCK = 0;
		IO = temp & 0x01;	//传输时从低到高。
		temp>>=1;	  //右移1位。
		SCK = 1;

	}
}

//向1302中写入数据。参数有要写入的地址和数据。
void Write_Ds1302(unsigned char add,unsigned char dat)
{
	RST = 0;
	_nop_();
	SCK = 0;
	_nop_();
	RST = 1;
	_nop_();
	Write_Ds1302_Byte(add);	//发送地址。
	Write_Ds1302_Byte(dat);	//发送数据。
	RST = 0;
}

//从1302中的读出数据。
unsigned char Read_Ds1302(unsigned char add)
{
	unsigned char i,temp=0x00;
	RST = 0;
	_nop_();
	_nop_();
	SCK = 0;
	_nop_();
	_nop_();
	RST = 1;
	_nop_();
	_nop_();
	Write_Ds1302_Byte(add);	 //发送地址,找到地址。
	for(i=0;i<8;i++)   //循环8次读出数据。
	{
		if(IO)		//传输从低到高。
			temp |= 0x80;
		SCK = 0;
		temp>>=1;	//右移1位。
		_nop_();
		_nop_();
		_nop_();
		SCK = 1;
	}
	RST = 0;			//之后为DS1302复位。
	_nop_();
	_nop_();
//	RST = 0;    //试验时去掉该句没有影响。
	SCK = 0;
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	SCK = 1;
	_nop_();
	_nop_();
	IO = 0;
	_nop_();
	_nop_();
	IO = 1;
	_nop_();
	_nop_();
	return temp;	//将读到的数据返回。
}

//从时钟中读取数据。
void Read_RTC(void)
{
	unsigned char i,*p;
	p = read_rtc_add;  //读日历数据对应的地址。
	for(i=0;i<7;i++)	//分7次分别将:时分秒日月周年读出。
	{
		l_tmpdate[i] = Read_Ds1302(*p);
		p++;
	}
}

//设定时钟的时间数据。
void Set_RTC(void)
{
	unsigned char i,*p,tmp;
	for(i=0;i<7;i++)   //将数从BCD码转化出来,因为1302中用BCD码表示数值。
	{
		tmp = l_tmpdate[i]/10;
		l_tmpdate[i] = l_tmpdate[i]%10;
		l_tmpdate[i] = l_tmpdate[i] + tmp*16;
	}

	Write_Ds1302(0x8e,0x00);  //清除写入保护。

	p = write_rtc_add;		//传送地址。
	for(i=0;i<7;i++)		//将数据依次写入。
	{
		Write_Ds1302(*p,l_tmpdate[i]);
		p++;
	}

	Write_Ds1302(0x8e,0x80);  //打开写入保护,不能再写入。
}


//定时器中断函数。
void tim(void) interrupt 1 using 1
{
	static unsigned char i,num;
	TH0 = 0xf5;
	TL0 = 0xe0;

	i++;

	if(i==8)
	{
		i=0;
		num++;
		if(10==num)		  //间隔一定时间读取1302中数据,更新数码管数据。
		{
			ReadRTC_Flag = 1;	 //置标志位,从而进行判断。
			num = 0;
		}
	}
}

4、LCD显示1302时钟.c文件,这是主函数所在文件,用来将从DS1302获得的信息在LCD1602中显示的操作。

#include 
#include 
#include 

#define uchar unsigned char
#define uint unsigned int

uchar *week[] = {"sun","mon","tus","wes","thu","fri","sat"}; //周信息,周一到周日。
uchar lcd_buf1[] = "date 00-00-00   ";	  //日期信息。
uchar lcd_buf2[] = "time 00:00:00   ";	  //时间信息。

//从其他文件引进的函数。
extern void InitTIMER0(void);
extern void lcd_init();
extern void lcd_showstring(uchar ,uchar ,uchar *);
extern uchar l_tmpdate[7];
extern bit ReadRTC_Flag;
extern void delay(int i);
extern void Read_RTC();			  
extern void Set_RTC();

//将日期和时间信息转化为数字字符。
void format_datetime(int d,uchar * a)
{
	*a = (d >> 4) + '0';
	*(a+1) = (d & 0x0f) + '0';
}

void main()
{
	lcd_init();	  //LCD的初始化。
	InitTIMER0(); //初始化定时器0。
//	Set_RTC();	//写入时钟值(写入时钟初值),如果不用改时间可以不用这一项。

	while(1)
	{
		if(ReadRTC_Flag)
		{
			ReadRTC_Flag = 0;
			Read_RTC();
	
			//日期转换。
			format_datetime(l_tmpdate[6],lcd_buf1+5);
			format_datetime(l_tmpdate[4],lcd_buf1+8);
			format_datetime(l_tmpdate[3],lcd_buf1+11);
			//星期转换。
			strcpy(lcd_buf1 + 13,week[l_tmpdate[5]-1]);
			//定义一个字符串char a[20],和一个字符串c[]="i am a teacher!";
			//把c复制到a中就可以这样用:strcpy(a,c);
	
			//时间转换。
			format_datetime(l_tmpdate[2],lcd_buf2+5);
			format_datetime(l_tmpdate[1],lcd_buf2+8);
			format_datetime(l_tmpdate[0],lcd_buf2+11);
	
			lcd_showstring(0,0,lcd_buf1);		 //将lcd_buf1和lcd_buf2字符输出。
			lcd_showstring(1,0,lcd_buf2);

		}
	}
}

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