时钟模块的应用51单片机:DS1302时钟模块的应用

1.DS1302介绍

  • DS1302是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它可以对年、月、日、周、时、分、秒进行计时,且具有闰年补偿等多种功能
  • RTC(Real Time Clock):实时时钟,是一种集成电路,通常称为时钟芯片
  • 引脚定义和应用电路

     这边是DS1302与CPU之间的一个关系,我先列一个表格来介绍一下各个部分的功能。

            首先是电源部分,VCC2接的是外部电源,而VCC1接的是内置电源(51开发板上没有内置电源,所以不能看到掉电之后继续走的现象了,但我的闹钟可以掉电后走,hahaha!) ,之后就是GND接地。

             之后就是晶振部分,接的是32.768KH的晶振,而单片机本身是11.0592MHZ,这个要注意区分,主要作用就是提供一个稳定的计数脉冲,用来计时。

           之后的CE就是芯片使能,不使能就不能读取,但时钟还是可以运行滴;IO数据输入输出,用来获取和修改数据;SCLK串行时钟,和之前的SERCLK是类似的。

            这个是更为详细的图片:

    而我们一般是直接使用模块的

    这种模块帮我们把晶振电路接好了,我们可以使用51单片机直接进行数据的读取和发送,

    具体引脚和上面一样,不过少了一个VCC1,因为模块这里的纽扣电池就充当了VCC1,

    而X1,X2也帮我们连接好了,其他的一样。

    而本人的DS1302是直接焊在板子上了,如下是原理图,其实直接使用模块操作也是一样的。

    2.DS1302的通讯协议及时序

            事实上,DS1302 的通信是 SPI 的变异种类,它用了 SPI 的通信时序,但是通信的时候没有完全按照 SPI 的规则,也许有人要问什么是SPI协议,这个可以自行百度一下,我后面也会出一期SPi协议的。下面我们一点点解剖 DS1302 的变异 SPI 通信方式。

    先看一下DS1302的控制寄存器(相当于命令控制寄存器)

    用于存放ds1302的控制命令,他用于对ds1302的读写进行控制

    要求命令字的bit7在每一次的数据传输过程中必须置为1,如果置为0,则禁止对ds1302写入数据。

    位 6 在逻辑 0时规定为时钟/日历数据,
    位6在逻辑 1时为 RAM数据.

    位 1 至 位 5 为寄存器的5位地址位,用于选择进行读写的日历,时钟寄存器或者片内RAM

    LSB (位 0) 在逻辑0时为写操作(输出)
    LSB(位0)为逻辑1时为读操作(输入).命令字以 LSB (位 0)开始总是输入.
     

    与日历时钟相关的寄存器(相当于数据寄存器)

    当我们需要初始化时间的时候,就是通过向这写寄存器地址写入数据,来实现时间的初始化。

    **秒寄存器0x81,0x80:**当CH=0时,DS1302内部时钟运行,反之CH=1停止。
    对于数据位,已知秒十位最大为5,个位最大为9,所以高位只需要3位,低位则需要4位。

    **分钟寄存器0x83,0x82:**分钟寄存器就只有bit0-bit6作为数据位,bit7为0就好。

    **小时寄存器0x85,0x84:**当BIT7为1时为12小时制,当BIT7为0时为24小时制。

    **控制寄存器0x8f,0x8e:**当WP为1时,不能对Ds1302做任何操作。

    寄存器内容是按照BCD码存放的,如READ:0x83地址读到的0x28代表28分钟。
     

     注:将BCD码转为十进制码

    
     uchar BCD_TO_DEX(uchar BCD)
    {
    	return((BCD&0xF0)>>4)*10+(BCD & 0x0F);
    }
    
     uchar DEX_TO_BCD(uchar DEX)
    {
    	return ((DEX/10)<<4)*10|(DEX%10);
    }

    单字节写入操作:

    写操作描述:
    首先要操作ds1302首先得拉高CE口
    然后放上要写的数据
    最后观察上面的时序图,在控制字指令输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从最低位(0位)开始。

    单字节读操作:

    读操作描述:
    首先要操作ds1302首先得拉高CE口
    在命令字的最后一个上升沿时,我们令SCLK拉低,紧接着就可以读数据了
    读出DS1302的数据,读出的数据也是从最低位到最高位。

    总结:CE从低到高的的时候,CLK必须为低。
    上升沿写入,下降沿读出
    写操作:写完命令的紧接着的上升沿写入数据。
    读操作:写完命令的紧接着的下降沿读出数据。
    注意读写位是低位在前,不管是命令字还是地址。

    复位以及时钟控制:
    所有的数据传输在RST置一时进行(反复强调), 输入信号有两种功能:
    首先,RST 接通控制逻辑,允许地址/命令序列送入移位寄存器;
    其次,RST 提供终止单字节或多字节数据的传送手段。
    当 RST 为高电平时,所有的数据传送被初始化,允许对 DS1302 进行操作。
    如果在传送过程中 RST 置为低电平,则会终止此次数据传送,I/O 引脚变为高阻态。
    上电运行时,在 Vcc≥2.5V 之前,RST 必须保持低电平。只有在 SCLK 为低电平时,才能将 RST 置为高电平。
    I/O 为串行数据输入输出端(双向),后面有详细说明。SCLK 始终是输入端。
     

    数据输入:
    经过 8 个时钟周期的控制字节的输入,一个字节的输入将在下 8 个时钟周期的上升沿完成,数据传输从字节最低位开始。

    数据输出:
    经过 8 个时钟周期的控制读指令的输入,控制指令串行输入后,一个字节的数据将在下个 8 个时钟周期的下降沿被输出,注意第一位输出是在最后一位控制指令所在脉冲的下降沿被输出,要求 RST 保持位高电平。

    同理 8 个时钟周期的控制读指令如果指定的是突发模式,将会在脉冲的上升沿读入数据,下降沿读出数据,突发模式一次可进行多字节数据的一次性读写,只要控制好脉冲就行了。
     

    代码分析:

    单字节写入:

    /**
      * @brief  DS1302初始化
      * @param  无
      * @retval 无
      */
    void DS1302_Init(void)
    {
    	DS1302_CE=0;
    	DS1302_SCLK=0;
    }
    
    /**
      * @brief  DS1302写一个字节
      * @param  Command 命令字/地址
      * @param  Data 要写入的数据
      * @retval 无
      */
    void DS1302_WriteByte(unsigned char Command,Data)
    {
    	unsigned char i;
    	DS1302_CE=1;
    	for(i=0;i<8;i++)
    	{
    		DS1302_IO=Command&(0x01<<i);
    		DS1302_SCLK=1;
    		DS1302_SCLK=0;
    	}
    	for(i=0;i<8;i++)
    	{
    		DS1302_IO=Data&(0x01<<i);
    		DS1302_SCLK=1;
    		DS1302_SCLK=0;
    	}
    	DS1302_CE=0;
    }

    单字节读入:

    /**
      * @brief  DS1302读一个字节
      * @param  Command 命令字/地址
      * @retval 读出的数据
      */
    unsigned char DS1302_ReadByte(unsigned char Command)
    {
    	unsigned char i,Data=0x00;
    	Command|=0x01;	//将指令转换为读指令
    	DS1302_CE=1;
    	for(i=0;i<8;i++)
    	{
    		DS1302_IO=Command&(0x01<<i);
    		DS1302_SCLK=0;
    		DS1302_SCLK=1;
    	}
    	for(i=0;i<8;i++)
    	{
    		DS1302_SCLK=1;
    		DS1302_SCLK=0;
    		if(DS1302_IO){Data|=(0x01<<i);}
    	}
    	DS1302_CE=0;
    	DS1302_IO=0;	//读取后将IO设置为0,否则读出的数据会出错
    	return Data;
    }
    

    时间设置写入:

    
    //寄存器写入地址/指令定义
    #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
    
    //时间数组,索引0~6分别为年、月、日、时、分、秒、星期
    unsigned char DS1302_Time[]={22,11,21,12,59,55,6};
    
    /**
      * @brief  DS1302设置时间,调用之后,DS1302_Time数组的数字会被设置到DS1302中
      * @param  无
      * @retval 无
      */
    void DS1302_SetTime(void)
    {
    	DS1302_WriteByte(DS1302_WP,0x00);
    	DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);//十进制转BCD码后写入
    	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);
    }
    

    时间读取:

    /**
      * @brief  DS1302读取时间,调用之后,DS1302中的数据会被读取到DS1302_Time数组中
      * @param  无
      * @retval 无
      */
    void DS1302_ReadTime(void)
    {
    	unsigned char Temp;
    	Temp=DS1302_ReadByte(DS1302_YEAR);
    	DS1302_Time[0]=Temp/16*10+Temp%16;//BCD码转十进制后读取
    	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;
    }

    完整代码如下:

    main.c:

    #include <REGX52.H>
    #include "LCD1602.h"
    #include "DS1302.h"
    
    void main()
    {
    	LCD_Init();
    	DS1302_Init();
    	LCD_ShowString(1,1,"  -  -  ");//静态字符初始化显示
    	LCD_ShowString(2,1,"  :  :  ");
    	
    	DS1302_SetTime();//设置时间
    	
    	while(1)
    	{
    		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);//显示秒
    	}
    }
    
    

    DS1302.c

    #include <REGX52.H>
    
    //引脚定义
    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
    
    //时间数组,索引0~6分别为年、月、日、时、分、秒、星期
    unsigned char DS1302_Time[]={22,11,21,12,59,55,6};
    
    /**
      * @brief  DS1302初始化
      * @param  无
      * @retval 无
      */
    void DS1302_Init(void)
    {
    	DS1302_CE=0;
    	DS1302_SCLK=0;
    }
    
    /**
      * @brief  DS1302写一个字节
      * @param  Command 命令字/地址
      * @param  Data 要写入的数据
      * @retval 无
      */
    void DS1302_WriteByte(unsigned char Command,Data)
    {
    	unsigned char i;
    	DS1302_CE=1;
    	for(i=0;i<8;i++)
    	{
    		DS1302_IO=Command&(0x01<<i);
    		DS1302_SCLK=1;
    		DS1302_SCLK=0;
    	}
    	for(i=0;i<8;i++)
    	{
    		DS1302_IO=Data&(0x01<<i);
    		DS1302_SCLK=1;
    		DS1302_SCLK=0;
    	}
    	DS1302_CE=0;
    }
    
    /**
      * @brief  DS1302读一个字节
      * @param  Command 命令字/地址
      * @retval 读出的数据
      */
    unsigned char DS1302_ReadByte(unsigned char Command)
    {
    	unsigned char i,Data=0x00;
    	Command|=0x01;	//将指令转换为读指令
    	DS1302_CE=1;
    	for(i=0;i<8;i++)
    	{
    		DS1302_IO=Command&(0x01<<i);
    		DS1302_SCLK=0;
    		DS1302_SCLK=1;
    	}
    	for(i=0;i<8;i++)
    	{
    		DS1302_SCLK=1;
    		DS1302_SCLK=0;
    		if(DS1302_IO){Data|=(0x01<<i);}
    	}
    	DS1302_CE=0;
    	DS1302_IO=0;	//读取后将IO设置为0,否则读出的数据会出错
    	return Data;
    }
    
    /**
      * @brief  DS1302设置时间,调用之后,DS1302_Time数组的数字会被设置到DS1302中
      * @param  无
      * @retval 无
      */
    void DS1302_SetTime(void)
    {
    	DS1302_WriteByte(DS1302_WP,0x00);
    	DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);//十进制转BCD码后写入
    	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);
    }
    
    /**
      * @brief  DS1302读取时间,调用之后,DS1302中的数据会被读取到DS1302_Time数组中
      * @param  无
      * @retval 无
      */
    void DS1302_ReadTime(void)
    {
    	unsigned char Temp;
    	Temp=DS1302_ReadByte(DS1302_YEAR);
    	DS1302_Time[0]=Temp/16*10+Temp%16;//BCD码转十进制后读取
    	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.h

    #ifndef __DS1302_H__
    #define __DS1302_H__
    
    //外部可调用时间数组,索引0~6分别为年、月、日、时、分、秒、星期
    extern unsigned char DS1302_Time[];
    
    void DS1302_Init(void);
    void DS1302_WriteByte(unsigned char Command,Data);
    unsigned char DS1302_ReadByte(unsigned char Command);
    void DS1302_SetTime(void);
    void DS1302_ReadTime(void);
    
    #endif
    

    LCD1602.c

    #include <REGX52.H>
    
    //引脚配置:
    sbit LCD_RS=P2^6;
    sbit LCD_RW=P2^5;
    sbit LCD_EN=P2^7;
    #define LCD_DataPort P0
    
    //函数定义:
    /**
      * @brief  LCD1602延时函数,12MHz调用可延时1ms
      * @param  无
      * @retval 无
      */
    void LCD_Delay()
    {
    	unsigned char i, j;
    
    	i = 2;
    	j = 239;
    	do
    	{
    		while (--j);
    	} while (--i);
    }
    
    /**
      * @brief  LCD1602写命令
      * @param  Command 要写入的命令
      * @retval 无
      */
    void LCD_WriteCommand(unsigned char Command)
    {
    	LCD_RS=0;
    	LCD_RW=0;
    	LCD_DataPort=Command;
    	LCD_EN=1;
    	LCD_Delay();
    	LCD_EN=0;
    	LCD_Delay();
    }
    
    /**
      * @brief  LCD1602写数据
      * @param  Data 要写入的数据
      * @retval 无
      */
    void LCD_WriteData(unsigned char Data)
    {
    	LCD_RS=1;
    	LCD_RW=0;
    	LCD_DataPort=Data;
    	LCD_EN=1;
    	LCD_Delay();
    	LCD_EN=0;
    	LCD_Delay();
    }
    
    /**
      * @brief  LCD1602设置光标位置
      * @param  Line 行位置,范围:1~2
      * @param  Column 列位置,范围:1~16
      * @retval 无
      */
    void LCD_SetCursor(unsigned char Line,unsigned char Column)
    {
    	if(Line==1)
    	{
    		LCD_WriteCommand(0x80|(Column-1));
    	}
    	else if(Line==2)
    	{
    		LCD_WriteCommand(0x80|(Column-1+0x40));
    	}
    }
    
    /**
      * @brief  LCD1602初始化函数
      * @param  无
      * @retval 无
      */
    void LCD_Init()
    {
    	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
    	LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
    	LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
    	LCD_WriteCommand(0x01);//光标复位,清屏
    }
    
    /**
      * @brief  在LCD1602指定位置上显示一个字符
      * @param  Line 行位置,范围:1~2
      * @param  Column 列位置,范围:1~16
      * @param  Char 要显示的字符
      * @retval 无
      */
    void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
    {
    	LCD_SetCursor(Line,Column);
    	LCD_WriteData(Char);
    }
    
    /**
      * @brief  在LCD1602指定位置开始显示所给字符串
      * @param  Line 起始行位置,范围:1~2
      * @param  Column 起始列位置,范围:1~16
      * @param  String 要显示的字符串
      * @retval 无
      */
    void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
    {
    	unsigned char i;
    	LCD_SetCursor(Line,Column);
    	for(i=0;String[i]!='\0';i++)
    	{
    		LCD_WriteData(String[i]);
    	}
    }
    
    /**
      * @brief  返回值=X的Y次方
      */
    int LCD_Pow(int X,int Y)
    {
    	unsigned char i;
    	int Result=1;
    	for(i=0;i<Y;i++)
    	{
    		Result*=X;
    	}
    	return Result;
    }
    
    /**
      * @brief  在LCD1602指定位置开始显示所给数字
      * @param  Line 起始行位置,范围:1~2
      * @param  Column 起始列位置,范围:1~16
      * @param  Number 要显示的数字,范围:0~65535
      * @param  Length 要显示数字的长度,范围:1~5
      * @retval 无
      */
    void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
    {
    	unsigned char i;
    	LCD_SetCursor(Line,Column);
    	for(i=Length;i>0;i--)
    	{
    		LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
    	}
    }
    
    /**
      * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字
      * @param  Line 起始行位置,范围:1~2
      * @param  Column 起始列位置,范围:1~16
      * @param  Number 要显示的数字,范围:-32768~32767
      * @param  Length 要显示数字的长度,范围:1~5
      * @retval 无
      */
    void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
    {
    	unsigned char i;
    	unsigned int Number1;
    	LCD_SetCursor(Line,Column);
    	if(Number>=0)
    	{
    		LCD_WriteData('+');
    		Number1=Number;
    	}
    	else
    	{
    		LCD_WriteData('-');
    		Number1=-Number;
    	}
    	for(i=Length;i>0;i--)
    	{
    		LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
    	}
    }
    
    /**
      * @brief  在LCD1602指定位置开始以十六进制显示所给数字
      * @param  Line 起始行位置,范围:1~2
      * @param  Column 起始列位置,范围:1~16
      * @param  Number 要显示的数字,范围:0~0xFFFF
      * @param  Length 要显示数字的长度,范围:1~4
      * @retval 无
      */
    void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
    {
    	unsigned char i,SingleNumber;
    	LCD_SetCursor(Line,Column);
    	for(i=Length;i>0;i--)
    	{
    		SingleNumber=Number/LCD_Pow(16,i-1)%16;
    		if(SingleNumber<10)
    		{
    			LCD_WriteData(SingleNumber+'0');
    		}
    		else
    		{
    			LCD_WriteData(SingleNumber-10+'A');
    		}
    	}
    }
    
    /**
      * @brief  在LCD1602指定位置开始以二进制显示所给数字
      * @param  Line 起始行位置,范围:1~2
      * @param  Column 起始列位置,范围:1~16
      * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
      * @param  Length 要显示数字的长度,范围:1~16
      * @retval 无
      */
    void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
    {
    	unsigned char i;
    	LCD_SetCursor(Line,Column);
    	for(i=Length;i>0;i--)
    	{
    		LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
    	}
    }
    

    LCD1602.h

    #ifndef __LCD1602_H__
    #define __LCD1602_H__
    
    //用户调用函数:
    void LCD_Init();
    void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
    void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
    void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
    void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
    void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
    void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
    
    #endif
    

    扩展:

    可调时钟进阶版代码如下:

    main.c

    #include <REGX52.H>
    #include "LCD1602.h"
    #include "DS1302.h"
    #include "Key.h"
    #include "Timer0.h"
    
    unsigned char KeyNum,MODE,TimeSetSelect,TimeSetFlashFlag;
    
    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);//显示秒
    }
    
    void TimeSet(void)//时间设置功能
    {
    	if(KeyNum==2)//按键2按下
    	{
    		TimeSetSelect++;//设置选择位加1
    		TimeSetSelect%=6;//越界清零
    	}
    	if(KeyNum==3)//按键3按下
    	{
    		DS1302_Time[TimeSetSelect]++;//时间设置位数值加1
    		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)//按键3按下
    	{
    		DS1302_Time[TimeSetSelect]--;//时间设置位数值减1
    		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;}//秒越界判断
    	}
    	//更新显示,根据TimeSetSelect和TimeSetFlashFlag判断可完成闪烁功能
    	if(TimeSetSelect==0 && TimeSetFlashFlag==1){LCD_ShowString(1,1,"  ");}
    	else {LCD_ShowNum(1,1,DS1302_Time[0],2);}
    	if(TimeSetSelect==1 && TimeSetFlashFlag==1){LCD_ShowString(1,4,"  ");}
    	else {LCD_ShowNum(1,4,DS1302_Time[1],2);}
    	if(TimeSetSelect==2 && TimeSetFlashFlag==1){LCD_ShowString(1,7,"  ");}
    	else {LCD_ShowNum(1,7,DS1302_Time[2],2);}
    	if(TimeSetSelect==3 && TimeSetFlashFlag==1){LCD_ShowString(2,1,"  ");}
    	else {LCD_ShowNum(2,1,DS1302_Time[3],2);}
    	if(TimeSetSelect==4 && TimeSetFlashFlag==1){LCD_ShowString(2,4,"  ");}
    	else {LCD_ShowNum(2,4,DS1302_Time[4],2);}
    	if(TimeSetSelect==5 && TimeSetFlashFlag==1){LCD_ShowString(2,7,"  ");}
    	else {LCD_ShowNum(2,7,DS1302_Time[5],2);}
    }
    
    void main()
    {
    	LCD_Init();
    	DS1302_Init();
    	Timer0Init();
    	LCD_ShowString(1,1,"  -  -  ");//静态字符初始化显示
    	LCD_ShowString(2,1,"  :  :  ");
    	
    	DS1302_SetTime();//设置时间
    	
    	while(1)
    	{
    		KeyNum=Key();//读取键码
    		if(KeyNum==1)//按键1按下
    		{
    			if(MODE==0){MODE=1;TimeSetSelect=0;}//功能切换
    			else if(MODE==1){MODE=0;DS1302_SetTime();}
    		}
    		switch(MODE)//根据不同的功能执行不同的函数
    		{
    			case 0:TimeShow();break;
    			case 1:TimeSet();break;
    		}
    	}
    }
    
    void Timer0_Routine() interrupt 1
    {
    	static unsigned int T0Count;
    	TL0 = 0x18;		//设置定时初值
    	TH0 = 0xFC;		//设置定时初值
    	T0Count++;
    	if(T0Count>=500)//每500ms进入一次
    	{
    		T0Count=0;
    		TimeSetFlashFlag=!TimeSetFlashFlag;//闪烁标志位取反
    	}
    }
    

    Key.c

    #include <REGX52.H>
    #include "Delay.h"
    
    /**
      * @brief  获取独立按键键码
      * @param  无
      * @retval 按下按键的键码,范围:0~4,无按键按下时返回值为0
      */
    unsigned char Key()
    {
    	unsigned char KeyNumber=0;
    	
    	if(P3_1==0){Delay(20);while(P3_1==0);Delay(20);KeyNumber=1;}
    	if(P3_0==0){Delay(20);while(P3_0==0);Delay(20);KeyNumber=2;}
    	if(P3_2==0){Delay(20);while(P3_2==0);Delay(20);KeyNumber=3;}
    	if(P3_3==0){Delay(20);while(P3_3==0);Delay(20);KeyNumber=4;}
    	
    	return KeyNumber;
    }
    

    Delay.c 

    
    void Delay(unsigned int xms)
    {
    	unsigned char i, j;
    	while(xms--)
    	{
    		i = 2;
    		j = 239;
    		do
    		{
    			while (--j);
    		} while (--i);
    	}
    }
    
    

     

    #include <REGX52.H>
    
    /**
      * @brief  定时器0初始化,1毫秒@12.000MHz
      * @param  无
      * @retval 无
      */
    void Timer0Init(void)
    {
    	TMOD &= 0xF0;		//设置定时器模式
    	TMOD |= 0x01;		//设置定时器模式
    	TL0 = 0x18;		//设置定时初值
    	TH0 = 0xFC;		//设置定时初值
    	TF0 = 0;		//清除TF0标志
    	TR0 = 1;		//定时器0开始计时
    	ET0=1;
    	EA=1;
    	PT0=0;
    }
    
    
    

    其余代码均无需更改,如上实验.

    物联沃分享整理
    物联沃-IOTWORD物联网 » 时钟模块的应用51单片机:DS1302时钟模块的应用

    发表评论