一、试验的基本功用:

            1.完成时钟体系,在51单片机的液晶显现屏中显现:年月日、时分秒,并且随时刻进行跳动
            2.完成可调功用,能够调整日期和时刻

二、硬件上的预备:

1.DS1302时钟芯片的引脚

51单片机实验一:实现时钟功能(基础)

51单片机实验一:实现时钟功能(基础)

   CE芯片使能:操控信号通讯的逻辑操控端,相当于逻辑操控的“与门”,51端口P3_5
   IO数据输入输出:信号通讯端口,发送指令和数据,51端口P3_4
   SCLK:时序操控端.51端口P3_6
   x1,x2:使DS1302能够不经过写入数据,而自动进行时刻增加

2.DS1302通讯的时序定义

51单片机实验一:实现时钟功能(基础)

    分为读写对应的不同的时序进程,内容也分为指令和数据部分

    当CE上沿,整个通讯有用,为总控。当传指令时,SCLK上沿表明一位数据有用。
      当写数据时,SCLK上沿表明一位数据有用。当读数据时,SCLK下沿表明一位数据有用

3.寄存器定义

51单片机实验一:实现时钟功能(基础)

    对应的读写功用指令,例如:0x81为读取DS1302的存储空间的内容等。可读写年月日,时分秒

三、代码完成的功用函数(当头文件)

DS1302S触及的函数

       1.void DS1302_Init(void) 调控总操控端,完成CE上沿
       2.void DS1302_WriteByte(unsigned char Command,Data) 完成写入数据到寄存器中(包括指令和数据)
       3.unsigned char DS1302_ReadByte(unsigned char Command)完成读入数据到电脑中(包括指令和数据)留意的数据是下沿有用
       4.void DS1302_SetTime(void)完成设置DS1302初始时刻,首要调用存储数组的值
       5.void DS1302_ReadTime(void)完成读取DS1302寄存器的时刻
       6.void TimeShow(void)完成显现时刻到51液晶显现屏上
       7.void TimeSet(unsigned char KeyNum)完成设置DS1302的值,首要经过外部设备进行改动数组的值
       8.void TimeSet(unsigned char KeyNum);完成调控中心数组的数据,完成时钟调控功用
       9.void Timer0_Routine() interrupt 1调用守时器的中断代码,完成调空的数据守时闪耀

由于触及LCD1602液晶显现和独立按键、守时器,所以调用LCD1602.h和keyboad.h,Timer0.h等头文件

四、代码演示和解说说明

1.端口的调用和地址的重置命名

sbit  DS1302_SCLK = P3^6;       //时序端口
sbit  DS1302_IO   = P3^4;       //输入输出端
sbit  DS1302_CE   = P3^5;       //使能操控端
//写入指令指令
#define DS1302_SECOND    0x80   //写入秒     
#define DS1302_MINUTE    0x82   //写入分钟
#define DS1302_HOUR      0x84   //写入小时
#define DS1302_DATE      0x86   //写入日
#define DS1302_MONTH     0x88   //写入月
#define DS1302_DAY       0x8A   //写入星期
#define DS1302_YEAR      0x8C   //写入年
#define DS1302_WP        0x8E   //写保护状况
   将51端口和写入指令的指令进行重置命名,完成愈加清晰化及调用愈加便利,提高代码的阅览性

2.变量定义

unsigned char DS1302_Time[] = {23,3,21,10,29,30,2};
unsigned char TimeSelect,TimeSetFlashFlag;
 DS1302_Time[]:中心存储变量,将从DS1302的数据读出的贮存空间,显现数据将其读出即可
 TimeSelect:用于调控时钟数据,即更改DS1302_Time[]的数据
 TimeSetFlashFlag:调控的数字进行闪耀的操控端

3.配置总操控端CE,完成初始化

void DS1302_Init(void)
{
    DS1302_CE = 0;
    DS1302_SCLK = 0;
}
将使能端先置零,为使能端上沿做预备,也防止了CE开端为1的或许

4.写入数据到DS1302寄存器中

void DS1302_WriteByte(unsigned char Command,Data)
{
    unsigned char i;
    DS1302_CE = 1;          //使能端置1,表时序进程有用
    //写入指令指令
    for (i = 0;i < 8;i++)
    {   
        DS1302_IO = Command & (0x01<<i);//传入一位比特位放入IO,0x01与Command进行位运算,Command对应的位为1就是1,0就是0
        DS1302_SCLK = 1;    //开端SCLK就是0,置1代表数据有用,再置0康复状况
        DS1302_SCLK = 0;
    }
    //写入数据
    for(i = 0;i<8;i++)
    {
        DS1302_IO = Data & (0x01<<i);
        DS1302_SCLK = 1;
        DS1302_SCLK = 0;
    }
   DS1302_CE = 0;
}
写入数据到DS1302存储器件中,写入指令指令和数据都是向上沿有用
程序将信号输入到I0口上,DS1302接纳数据无需关怀
指令和数据是分隔的传入IO口
写到哪里和读到哪里,是由写指令操控

5.读入数据到电脑中

unsigned char DS1302_ReadByte(unsigned char Command)
{
    unsigned char i ,Data = 0x00;
    //第0位表明读写,0为写,1位读
    Command |= 0x01;//设置读的功用,能运用define
    DS1302_CE = 1;
    for(i = 0;i < 8;i++)
    {
        DS1302_IO = Command & (0x01<<i);
        DS1302_SCLK = 0;  //与上述相同,改动0/1方位,但作用不变,使读数据的下沿有用,不发生抵触
        DS1302_SCLK = 1;                
    }
    //读出数据
    for(i = 0;i < 8;i++)
    {   
        DS1302_SCLK = 1;
        DS1302_SCLK = 0;   
        if(DS1302_IO) {Data |= (0x01<<i);} //当DS1302_IO为1时,Data对应的方方位1,当DS1302_IO为0时,Data为0            
    }
    DS1302_CE = 0;
    DS1302_IO = 0; //能处理数据不稳定的问题
    return Data;
}
读出数据,包括写入指令指令(上沿有用),读出数据(下沿有用)
无需关怀DS1302怎么宣布数据的,当指令写入读数据时,DS1302开端写入数据,只需接纳即可
写到哪里和读到哪里,是由写指令操控

6.设置DS1302初始时刻,首要调用存储数组的值

void DS1302_SetTime(void)
{  //十进制转BCD码后写入
   DS1302_WriteByte(DS1302_WP,0x00);
   DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);
   DS1302_WriteByte(DS1302_MONTH,DS1302_Time[1]/10*16+DS1302_Time[1]%10);
   DS1302_WriteByte(DS1302_DATE,DS1302_Time[2]/10*16+DS1302_Time[2]%10);
   DS1302_WriteByte(DS1302_HOUR,DS1302_Time[3]/10*16+DS1302_Time[3]%10);
   DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);
   DS1302_WriteByte(DS1302_SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);
   DS1302_WriteByte(DS1302_DAY,DS1302_Time[6]/10*16+DS1302_Time[6]%10);
   DS1302_WriteByte(DS1302_WP,0x80);
}
调用前面的DS1302_WriteByte(),将数组的数据全部写入DS1302中
DS1302接纳的数据为16进制数据,所以要将十进制数据转成十六进制数据

7.完成读取DS1302寄存器的时刻

void DS1302_ReadTime(void)
 {  //BCD码转十进制后读取
	unsigned char Temp;
	Temp=DS1302_ReadByte(DS1302_YEAR);
	DS1302_Time[0]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_MONTH);
	DS1302_Time[1]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_DATE);
	DS1302_Time[2]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_HOUR);
	DS1302_Time[3]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_MINUTE);
	DS1302_Time[4]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_SECOND);
	DS1302_Time[5]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_DAY);
	DS1302_Time[6]=Temp/16*10+Temp%16;
 }
功用是将DS1302的数据读到数组里,调用DS1302_ReadTime()
数组采用的十进制输入,是因为LCD1602调用的是显现十进制数,所以将读出来十六进制转十进制

8.完成显现时刻到51液晶显现屏上

void TimeShow(void)
{
  DS1302_ReadTime();//读取时刻
	LCD_ShowNum(1,1,DS1302_Time[0],2);//显现年
	LCD_ShowNum(1,4,DS1302_Time[1],2);//显现月
	LCD_ShowNum(1,7,DS1302_Time[2],2);//显现日
	LCD_ShowNum(2,1,DS1302_Time[3],2);//显现时
	LCD_ShowNum(2,4,DS1302_Time[4],2);//显现分
	LCD_ShowNum(2,7,DS1302_Time[5],2);//显现秒
}
调用LCD1602功用显现中心数组的数据,即完成显现时钟的功用部分

9.完成调控中心数组的数据,完成时钟调控功用

void TimeSet(unsigned char KeyNum)
{   
    //选择数组方位
    if(KeyNum == 2)
    {
        TimeSelect++;
        TimeSelect %= 6;
    }
    //加对应的值
    if(KeyNum == 3)
    {
        DS1302_Time[TimeSelect]++;
        if(DS1302_Time[0]>99){DS1302_Time[0]=0;}//年越界判别
		if(DS1302_Time[1]>12){DS1302_Time[1]=1;}//月越界判别
		if( DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 || 
			DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12)//日越界判别
		{
			if(DS1302_Time[2]>31){DS1302_Time[2]=1;}//大月
		}
		else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || DS1302_Time[1]==11)
		{
			if(DS1302_Time[2]>30){DS1302_Time[2]=1;}//小月
		}
		else if(DS1302_Time[1]==2)
		{
			if(DS1302_Time[0]%4==0)
			{
				if(DS1302_Time[2]>29){DS1302_Time[2]=1;}//闰年2月
			}
			else
			{
				if(DS1302_Time[2]>28){DS1302_Time[2]=1;}//平年2月
			}
		}
		if(DS1302_Time[3]>23){DS1302_Time[3]=0;}//时越界判别
		if(DS1302_Time[4]>59){DS1302_Time[4]=0;}//分越界判别
		if(DS1302_Time[5]>59){DS1302_Time[5]=0;}//秒越界判别
    }
    //减对应的值
    if(KeyNum == 4)
    {
        DS1302_Time[TimeSelect]--;
        if(DS1302_Time[0]<0){DS1302_Time[0]=99;}//年越界判别
		if(DS1302_Time[1]<1){DS1302_Time[1]=12;}//月越界判别
		if( DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 || 
			DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12)//日越界判别
		{
			if(DS1302_Time[2]<1){DS1302_Time[2]=31;}//大月
			if(DS1302_Time[2]>31){DS1302_Time[2]=1;}
		}
		else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || DS1302_Time[1]==11)
		{
			if(DS1302_Time[2]<1){DS1302_Time[2]=30;}//小月
			if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
		}
		else if(DS1302_Time[1]==2)
		{
			if(DS1302_Time[0]%4==0)
			{
				if(DS1302_Time[2]<1){DS1302_Time[2]=29;}//闰年2月
				if(DS1302_Time[2]>29){DS1302_Time[2]=1;}
			}
			else
			{
				if(DS1302_Time[2]<1){DS1302_Time[2]=28;}//平年2月
				if(DS1302_Time[2]>28){DS1302_Time[2]=1;}
			}
		}
		if(DS1302_Time[3]<0){DS1302_Time[3]=23;}//时越界判别
		if(DS1302_Time[4]<0){DS1302_Time[4]=59;}//分越界判别
		if(DS1302_Time[5]<0){DS1302_Time[5]=59;}//秒越界判别
    }
    if(TimeSelect==0 && TimeSetFlashFlag==1){LCD_ShowString(1,1,"  ");}
		else {LCD_ShowNum(1,1,DS1302_Time[0],2);}
		if(TimeSelect==1 && TimeSetFlashFlag==1){LCD_ShowString(1,4,"  ");}
		else {LCD_ShowNum(1,4,DS1302_Time[1],2);}
		if(TimeSelect==2 && TimeSetFlashFlag==1){LCD_ShowString(1,7,"  ");}
		else {LCD_ShowNum(1,7,DS1302_Time[2],2);}
		if(TimeSelect==3 && TimeSetFlashFlag==1){LCD_ShowString(2,1,"  ");}
		else {LCD_ShowNum(2,1,DS1302_Time[3],2);}
		if(TimeSelect==4 && TimeSetFlashFlag==1){LCD_ShowString(2,4,"  ");}
		else {LCD_ShowNum(2,4,DS1302_Time[4],2);}
		if(TimeSelect==5 && TimeSetFlashFlag==1){LCD_ShowString(2,7,"  ");}
		else {LCD_ShowNum(2,7,DS1302_Time[5],2);}
}
经过独立按键相应,来完成对应的功用
 例如:  KeyNum = 2对应的TimeSelect就会加1,对应LCD1602的数据闪耀方位改动
        KeyNum = 3 和 KeyNum = 4对应的数组的DS1302_Time[TimeSelect]的数值加1或减1
        KeyNum = 1 改动时钟形式:调控和不调控
处理时刻进位的问题(调控的进位):需要留意的是月份分大月和小月,大月31进,小月30进。
年又份润年和平年,润年2月29天,平年28。
再每次加1之,判别是否越界.KeyNum = 34 都要判别一下,代码重复,可封装

10.完成调空的数据守时闪耀

void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count;
	TL0 = 0x18;		
	TH0 = 0xFC;		
	T0Count++;
	if(T0Count>=500)
	{
		T0Count=0;
		TimeSetFlashFlag=!TimeSetFlashFlag;
	}
}
 完成守时闪耀,守时可满足,规守时刻掉用中断函数,闪耀接纳空白和 显现交替,所以再判别越界结束,
 履行这部分代码,先将LCD1602显现空白,再显现数据即可      

五、mian函数运行

#include "DS1302.h"
#include "LCD1602.h"
#include "Timer0.h"
#include "Keyboard.h"
#include <REGX52.H>
unsigned char KeyNum,Mode;
void main()
{
	LCD_Init();
	DS1302_Init();
	Timer0_Init();
	LCD_ShowString(1,1,"  -  -  ");//?????????
	LCD_ShowString(2,1,"  :  :  ");
	DS1302_SetTime();
	while(1)
	{
		KeyNum = SmallKey();
		if(KeyNum == 1)		//????,????
		{
			if(Mode == 0){Mode = 1;}
			else if (Mode == 1){Mode = 0;}
		}
		switch(Mode)
		{
			case 0:TimeShow();break;
			case 1:TimeSet(KeyNum);break;
		}
  }
}
初始化模块的功用,使对应的模块能够使用,调用对应的功用的模块