51单片机 (十四)DS1302

19 篇文章 27 订阅
订阅专栏

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;
}


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

基于51单片机ds1302多功能电子时钟
11-23
DS1302实现电子时钟,掉电自动存储数据。通过LCD1602显示年月日周时分秒,独立按键设定定时时间,具有定时报警功能
51单片机串口修改DS1302时间
07-03
本文将详细讲解如何利用51单片机通过串口来修改DS1302时钟芯片的时间。 DS1302是一款低功耗、高性能的实时时钟(RTC)芯片,能够准确地记录时间,包括年、月、日、星期、小时、分钟和秒。它具有串行接口,易于与...
51单片机-DS1302
m0_51319492的博客
05-13 1157
DS1302是由美国DALLAS公司推出的低功耗实时时钟芯片。它可以对年、月、日、周、时、分、秒进行计时,且具有闰年补偿等功能实时时钟,是一种集成电路,通常称为时钟芯片,有备用电池,可以掉电运行定时器也可以实现实时时钟的功能,采用DS1302是因为定时器实现精度不高、占用单片机CPU的时间且定时器时钟不可以掉电运行。
DS1302时钟芯片全解析——概况,性能,MCU连接,样例代码
最新发布
Dunkle.T的博客
07-29 1642
DS1302时钟芯片全解析——概况,性能,MCU连接,样例代码
十二、51单片机DS1302(SPI)
西西弗的博客
03-16 1112
51单片机DS1302
51单片机DS1302数字时钟
weixin_34306446的博客
03-21 389
2019独角兽企业重金招聘Python工程师标准>>> ...
ds1302通信
曹鑫的博客
09-23 388
#define ds1302_sec_add   0x80   #define ds1302_min_add   0x82   #define ds1302_hr_add   0x84   #define ds1302_date_add   0x86   #define ds1302_month_add  0x88   #define ds1302_day_add   0x8a   #
51单片机案例——利用DS1302实现实时时钟和可调时钟的功能代码示例
04-20
2.是否已连接DS1302(我的单片机板子利用的是郭天祥的51单片机,因此没有DS1302,这时就可以自行淘宝购买,2块钱一个) 如果上述两个事项都没有问题,则需要检查引脚配置: 1.打开LCD1602.c,参考单片机原理图修改...
STC51单片机20-DS1302可调电子时钟1602显示proteus仿真
12-22
标题中的“STC51单片机20-DS1302可调电子时钟1602显示proteus仿真”表明我们要探讨的是一个基于STC51系列单片机的电子时钟设计,该设计使用了DS1302时钟芯片来获取时间,并通过LCD1602显示器进行可视化显示。...
万年历_单片机万年历_LED1602_ds1302_51单片机_万年历_
09-30
本项目是关于一个基于51单片机的万年历系统,它结合了DS1302时钟芯片和LED1602液晶显示器,能够实现精确的时间显示和闹钟设置功能。 首先,51单片机是这个系统的核心,它是Intel 8051系列微控制器的扩展,拥有强大...
51单片机+LCD1602+DS1302组成的电子时钟电路包含源代码和Proteus仿真电路
07-01
在电子设计领域,51单片机、LCD1602显示器和DS1302实时时钟模块的组合常用于构建简易的电子时钟系统。这个项目将深入讲解这三个核心组件以及它们如何协同工作来实现时间显示和管理功能。 51单片机,全称8051系列微...
51单片机ds1302实现万年历
06-11
基于51单片机驱动ds1302等实现万年历功能,带有闹钟的设定功能,菜单选择功能,附电路仿真图
电源技术中的实时时钟电路DS1302的原理及应用
11-22
摘 要:介绍美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟电路DS1302的结构、工作原理及其在实时显示时间中的应用。它可以对年、月、日、周日、时、分、秒进行计时,且具有闰年补偿等多种功能。给出DS1302在读写中的C51程序及流程图,以及在调试过程中的注意事项。  关键词:时钟电路;实时时钟;单片机;应用   1 引言   现在流行的串行时钟电路很多,如DS1302、 DS1307、PCF8485等。这些电路的接口简单、价格低廉、使用方便,被广泛地采用。本文介绍的实时时钟电路DS1302是DALLAS公司的一种具有涓细电流充电能力的电路,主要特点是采用串行数据传输,可
DS1302时钟电路很不错了。已经调试过了
08-20
有些程序 来自网骆不过大部都 经过我改写,比较清楚,易懂,
时钟芯片接口分析与DS1302典型原理电路图
07-15
时钟芯片是时钟一个很重要的部分。时钟芯片大多都是由美国DALAS公司生产的,有着低电流充电的作用,并且低消耗电量。下面我们分享下时钟芯片的接口,时钟芯片DS1302的原理。
DS1302时钟模块原理图
03-31
DS1302,,,,时钟模块原理图,,,,,,,,,,,,,,,
实时时钟DS1302原理详解和单片机编程
m0_73931287的博客
08-07 7207
实时时钟DS1302原理详解和单片机编程
51单片机--DS1302时钟
m0_74068921的博客
07-16 2973
这是51单片机上的DS1302芯片;左上图是两种不同的封装工艺方式,DIP封装也叫双列直插式封装技术,是一种最简单的封装方式;SOP封装是一种元件封装形式,一般封装材料较多样:塑料、陶瓷、玻璃、金属等;现在普遍使用塑料封装;主要应用于各种集成电路中。在我们的STC89C52单片机上就是用SOP的封装方式;它在单片机上有8个引脚VCC为电源,其中VCC1为备用电池,也就是主电源断电时,由备用电池供电,但需要注意的是,在该款单片机上并没有备用电池;且VCC2才是主电源,不能于备用电池弄反了;
DS1302应用电路
weixin_30486037的博客
10-16 851
http://www.diangon.com/wenku/rd/danpianji/201501/00017904.html 转载于:https://www.cnblogs.com/qdrs/p/7675507.html
51单片机驱动DS1302实现万年历显示
本篇文章主要介绍了如何使用51单片机(如8051或类似型号)配合DS1302实时时钟芯片以及DM12864液晶显示器来实现一个实时显示万年历的功能。51单片机在此项目中作为核心控制器,负责与外部时钟和液晶屏进行通信,DS...
写文章

热门文章

  • STM32 (三)GPIO的八种模式及其原理 25680
  • 51单片机 (四)延时函数 17831
  • ESP32 Arduino 学习篇(五)TFT_eSPI库 16788
  • ESP32 Arduino学习篇 (三) 蓝牙 14601
  • ESP32 Arduino学习篇 (一) 引脚和串口的基本使用 13098

分类专栏

  • ESP32 16篇
  • 正点原子imx6ull 12篇
  • STM32 CubeMX 24篇
  • matlab 1篇
  • github 1篇
  • K210 12篇
  • 模电笔记 1篇
  • 51 单片机 19篇
  • PID 1篇
  • 数字电路笔记 6篇
  • STM32 平衡小车 4篇
  • stm模块
  • openMV
  • STM32 FreeRTOS 6篇

最新评论

  • 51单片机 (五)位移函数和流水灯

    不断前进.......: 看了这么多还是这个讲得最明白

  • ESP32 Arduino 学习篇(五)TFT_eSPI库

    iwonako: 请教一下大佬,怎么知道要改这些头文件的?

  • STM32 PID调节输出电压

    2302_80026528: 加了PID之后DAC不输出了,怎么办

  • ESP32 Arduino (十) HTTPClient库

    m0_48749958: 请问博主get访问网址后重定向了该怎么处理呢

  • ESP32 Arduino(十一) 按键控制库 OneButton

    火车没有火: loop这里要用delay吗

大家在看

  • 背包九讲——完全背包问题 1015
  • 华为od面试手撕代码真题题型2——数学题 27
  • (2024最新毕设合集)基于Django的房价分析平台-65434|可做计算机毕业设计JAVA、PHP、爬虫、APP、小程序、C#、C++、python、数据可视化、大数据、文案 953
  • # 探索CIO的数字化转型之旅:2024年9月Gartner洞察
  • 考完PMP认证还有必要考NPDP吗? 299

最新文章

  • linux文件夹介绍
  • MX6ULL学习笔记(十三)Linux 自带按键驱动程序
  • MX6ULL学习笔记(十二)Linux 自带的 LED 灯
2023年79篇
2022年30篇

目录

目录

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

玻璃钢生产厂家河北户外商场美陈市场价江苏校园玻璃钢雕塑公司玻璃钢浮雕景观雕塑定做红色玻璃钢卡通雕塑费用是多少万盛玻璃钢雕塑厂家玻璃钢海欣雕塑玻璃钢鱼雕塑哪家好玻璃钢仿铜雕塑工艺品哈密酒店玻璃钢雕塑定做玻璃钢雕塑维护宿迁商场新春美陈中国台湾玻璃钢雕塑介绍福州玻璃钢雕塑厂家玻璃钢雕塑手绘素材泉州玻璃钢花盆厂厦门玻璃钢卡通雕塑手工制作福州玻璃钢玩偶雕塑玻璃钢雕塑雕塑报价玻璃钢动物不锈钢雕塑公司克孜勒苏玻璃钢雕塑加工周口景观校园玻璃钢雕塑厂家安阳景观玻璃钢雕塑厂家广州创意玻璃钢雕塑联系方式赣州玻璃钢动物雕塑太原潮州玻璃钢卡通雕塑天津精美玻璃钢座椅雕塑福建玻璃钢雕塑定制安徽多彩玻璃钢雕塑订做价格玻璃钢雕塑打磨后地图片银川大型玻璃钢雕塑厂家香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化