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++;//设置选择位加1TimeSetSelect%=6;//越界清零}if(KeyNum==3)//按键3按下{DS1302_Time[TimeSetSelect]++;//时间设置位数值加1if(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]--;//时间设置位数值减1if(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;
}

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

51单片机 (十四)DS1302相关推荐

  1. 51单片机教程:51单片机驱动四个8*8点阵,拼凑16*16点阵显示标准汉字。

    看此篇博文之前建议先看博主的上一篇博文: 51单片机教程:8*8 点阵显示字符.数字.简单汉字 取走点赞哦~ 教你如何用4个8乘8点阵拼成一个16乘16点阵 资料链接:点阵16乘16.rar 一.点阵 ...

  2. 51单片机实例学习四 128X64 液晶显示器、PS/2与单片机通信、密码锁

    十四.128X64 液晶显示器的基本应用 [实验任务] 利用128X64点阵液晶显示屏显示图片,字符,汉字,画点,实现其基本显示 功能. [硬件电路图] [实验原理] 关于液晶显示的原理请用户自行其查 ...

  3. 51单片机(四)—— 51单片机仿真芯片的使用

    在前面的文章中,我们都是直接用串口将程序烧写到单片机中运行,在这种开发方式下,如果程序有问题,很难找到问题所在,用过一些高端单片机的人可能知道,一般单片机是可以用调试器调试的,可以单步调试.设置断点等 ...

  4. 51单片机 (四)延时函数

    接下来我们要讲单片机的延时函数,那什么是延时函数呢,其实在我们单片机代码中,每次执行一条代码是很快的,也就几微秒左右,那有时候我们想让某一条代码执行后等待一阵子,再去执行下一条,这时候就需要用到延时函 ...

  5. 51单片机(四)静态数码管和动态数码管显示

    ❤️ 专栏简介:本专栏记录了从零学习单片机的过程,其中包括51单片机和STM32单片机两部分:建议先学习51单片机,其是STM32等高级单片机的基础:这样再学习STM32时才能融会贯通. ☀️ 专栏适 ...

  6. 51单片机(四).C51编程语言

    一.常量与变量 1.常量 又称为标量,在程序执行过程中不能改变,常量数据类型有整形(int).浮点型(float).字符型和字符串型.应用中一般使用"#define"定义在程序中经 ...

  7. 51单片机学习2——DS1302制作简易数码管电子时钟

    目录 一.硬件电路 二.DS1302 1.DS1302概述 2.ds1302控制寄存器 3.DS1302时序图 (1)读字节程序: (2)写字节程序 (3)初始化程序 (4)转换时间程序 三.数码管 ...

  8. 51单片机复习:DS1302

    一.DS1302的主要性能指标 (1)DS1302实时时钟具有能计算2100年之前的秒.分.时.日.日期.星期.月.年的能力,还有闰年调整的能力. (2)内部含有31个字节静态RAM,可提供用户访问. ...

  9. 51单片机基础之DS1302

    代码: main.c 文件/* DS1302时钟 */ #include "smg.h" #include "ds1302.h"void main() { u8 ...

  10. 51单片机(四)定时器中断(+数码管——24小时制钟表)

    定时器中断 一.中断 1.中断概念 2.中断嵌套 二.中断源(优先级顺序必记!) 三.中断允许寄存器IE和中断优先级寄存器IP 1.中断允许寄存器IE(Interrupt Enable ) 2.中断优 ...

最新文章

  1. 2020-11-24
  2. SAP CRM WebClient UI的cancel按钮处理
  3. Git中非常重要的一个文件——.gitignore详解
  4. 完全关闭App的两种做法
  5. 基于原生js的图片延迟加载
  6. 事件捕获、冒泡、绑定、赋值、委托、兼容、滚轮
  7. 运筹优化(四)--线性规划之对偶问题和灵敏度分析
  8. 更改画面_看够联想姨妈红?阿虚教你更改ThinkPad 开机画面
  9. 下载DirectX 9 SDK开发包
  10. 图解项目管理详细流程全景图
  11. 线性表的链式存储结构 ( 链表 )
  12. H3C-云计算技术专题培训(分享七)
  13. 新一代云上基础技术和架构分论坛
  14. Android之绘制动态折线图
  15. SAP 采购订单历史价格跟物料凭证价格不一致问题
  16. NX二次开发CreateDialog函数在UI.hxx文件和WinUser.h中的冲突
  17. GGNN:GATED GRAPH SEQUENCE NEURAL NETWORKS
  18. oracle 对象类型是什么意思,Oracle对象类型 (转)
  19. 机器人开发--Universal Scene Description(USD)
  20. 杀毒我做主:病毒专杀工具自己编写

热门文章

  1. “降噪”与“匠心”兼得:索尼WH-1000XM4评测体验
  2. Android进度条,自定义进度条,显示百分比
  3. 计算机硬件基础(一)
  4. 游戏工作室与答题工作室谁的利益最大
  5. Paxos协议超级详细解释+简单实例
  6. 大聪明教你学Java设计模式 | 第一篇:单例模式 (懒汉模式和饿汉模式)
  7. 高频交易与量化交易到底有什么区别?
  8. java开发的简单新闻系统
  9. 新站如何推广 新站推广的基础方法
  10. 解决anaconda用代理conda设置代理