51单片机STC89C52RC——9.1 DS1302涓流充电计时芯片

目录

目的/效果

一,STC单片机模块

二,DS1302计时器

[2.1 特性/板子位置](#2.1 特性/板子位置)

[2.1.1 特性](#2.1.1 特性)

[2.1.2 板子上的位置](#2.1.2 板子上的位置)

[2.2 针脚定义](#2.2 针脚定义)

[2.3 数据传输](#2.3 数据传输)

[2.3.1 读数据](#2.3.1 读数据)

[2.3.2 写数据](#2.3.2 写数据)

[2.4 BCD码](#2.4 BCD码)

[2.5 可编程涓流充电器](#2.5 可编程涓流充电器)

[2.6 时钟动态设置](#2.6 时钟动态设置)

三,创建Keil项目

四,代码

[4.1 读写显示时间 main.c](#4.1 读写显示时间 main.c)

[4.2 手动设置时间、重新上电显示最后设置的时间 main.c](#4.2 手动设置时间、重新上电显示最后设置的时间 main.c)

五,代码编译、下载到51单片机


目的/效果

1:读取DS1302计时器时间,在LCD1602液晶显示屏上显示。

2:使用按键手动设置DS1302计时器时间,下电后重新上电显示。

一,STC单片机模块

二,DS1302计时器

2.1 特性/板子位置

2.1.1 特性

★ 实时时钟计算年、月、日、时、分、秒、星期,直到 2100 年,并有闰年调节功能
★ 31 x 8 位 通用暂存 RAM
★ 串行输入输出使管脚数最少
★ 2.0V 至 5.5V 宽电压范围操作
★ 在 2.0V 时工作电流小于 300nA
★ 读写时钟或 RAM 数据时有单字节或 多字节(脉冲串模式)数据传送方式
★ 8 管脚 DIP 封装或可选的 8 管脚
表面安装 SO 封装
★ 简单的 3 线接口
★ 与 TTL 兼容 (VCC = 5V)
★ 可选的工业温度范围: -40°C to +85°C
★ 与 DS1202 兼容
★ 美国保险商试验室 (UL®) 认证

DS1302 涓流充电计时芯片包含一个实时时钟/ 日历和 31 字节的静态 RAM.通过简单的串行 接口与微处理器通讯 . 这个实时时钟 / 日历提供年月日 , 时分秒信息 . 对于少于 31 天的月份月末 会自动调整 , 还有闰年校正 . 由于有一个 AM/PM 指示器,时钟可以工作在 12 小时制或者 24 小时制。
使用同步串行通讯简化了 DS1302 与微处理器的接口。与时钟 /RAM 通讯只需要三根线: CE, I/O ( 数据线 ), and SCLK ( 串行时钟 ). 数据输出输入时钟 /RAM 一次 1字节或者在脉冲串中多达 31 字节 . DS1302 被设计工作在非常低的电能下 , 在低于 1μW 时还能保持数据和时钟信息. DS1302 是 DS1202 的后继者 . 除了 DS1202 的基本计时功能以外 , DS1302 有额外特点比如, 双管脚主电源和备用电源 , 可编程涓流充电器 V CC1 , 还附加 7 字节的暂存器 .

2.1.2 板子上的位置


2.2 针脚定义

cpp 复制代码
//引脚定义
sbit DS1302_SCLK=P3^6;	//时钟针脚 输入. SCLK 用来同步串行接口上的数据动作.此管脚内部有一个
						//40kΩ(典型值)的下拉电阻连接到地

sbit DS1302_IO=P3^4;	//数据针脚 推挽输出.I/O 管脚是三线接口的双向数据管脚.此管脚内部有
						//一个 40kΩ(典型值)的下拉电阻连接到地

sbit DS1302_CE=P3^5;	//重置位针脚  CE信号在读写时必须保持高电平.此管脚内部有一个 40kΩ(典
						//型值)的下拉电阻连接到地. 注意:先前的数据手册修正把 CE当作 
						//RST. 管脚的功能没有改变.

2.3 数据传输

寄存器地址

根据上面的寄存器说明,我们程序中要申明对应的寄存器地址

cpp 复制代码
//寄存器写入地址/指令定义
#define DS1302_YEAR			0x8C	//年
#define DS1302_MONTH		0x88	//月	
#define DS1302_DATE			0x86	//日
#define DS1302_HOUR			0x84	//时
#define DS1302_MINUTE		0x82	//分
#define DS1302_SECOND		0x80	//秒
#define DS1302_WEEK			0x8A	//星期
#define DS1302_WP			0x8E	//写保护位 
									//控制寄存器的位 7是写保护位,前 7位(位 0至位 6)被强制为 0且读取时总是读 0. 在任
									//何对时钟或 RAM的写操作以前,位 7必须为 0.当为高时,写保护位禁止任何寄存器的写操
									//作. 初始加电状态未定义. 因此,在试图写器件之前应该清除 WP位.

2.3.1 读数据

根据上面的时序图我们需要写一个函数,传入寄存器地址,返回对应的数据。

cpp 复制代码
/**
 * 函    数:DS1302读一个字节
 * 参    数:Addr 命令字/DS1302寄存器地址
 * 返 回 值:无
 */
unsigned char DS1302_ReadByte(unsigned char Addr)
{
	unsigned char i,Data=0x00;
	Addr|=0x01;		//将指令转换为读指令
	DS1302_CE=1;	//读写数据时都置高电平
	for(i=0;i<8;i++)
	{
		DS1302_IO=Addr&(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;
}

2.3.2 写数据

同样更具上面的时序图 ,写一个方法传入地址和需要写入的数据,然后将其操作写入到寄存器中。

cpp 复制代码
/**
 * 函    数:DS1302写一个字节
 * 参    数:Addr 命令字/DS1302寄存器地址
			 Data 要写入的数据
 * 返 回 值:无
 */
void DS1302_WriteByte(unsigned char Addr,Data)
{
	unsigned char i;
	DS1302_CE=1;//读写数据时都置高电平
	/*在IO上先写入寄存器地址*/
	for(i=0;i<8;i++)
	{
		DS1302_IO=Addr&(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;
}
 

2.4 BCD码

BCD码(Binary Coded Decimal‎),用4位二进制数来表示1位十进制数

最小000 表示0 ,最大1001 表示9

例:0001 0011表示13,1000 0101表示85,0001 1010不合法

在十六进制中的体现:0x13表示13,0x85表示85,0x1A不合法

BCD码转十进制:DEC=BCD/16*10+BCD%16; (2位BCD)

十进制转BCD码:BCD=DEC/10*16+DEC%10; (2位BCD)

补充完读写操作如下

cpp 复制代码
//时间数组,索引0~6分别为年、月、日、时、分、秒、星期
unsigned char DS1302_Time[]={24,6,26,18,59,55,3};
 
/**
 * 函    数:DS1302设置时间,调用之后,DS1302_Time数组的数字会被设置到DS1302中
 * 参    数:无
 * 返 回 值:无
 */
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_WEEK,DS1302_Time[6]/10*16+DS1302_Time[6]%10);
	DS1302_WriteByte(DS1302_WP,0x80);
}

/**
 * 函    数:DS1302读取时间,调用之后,DS1302中的数据会被读取到DS1302_Time数组中
 * 参    数:无
 * 返 回 值:无
 */
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_WEEK);
	DS1302_Time[6]=Temp/16*10+Temp%16;
}

2.5 可编程涓流充电器


绝对最大额定值
任何管脚与地之间的电压范围.......................................................-0.5V- +7.0V
工作温度范围,商用级................................................................0°C - +70°C
工作温度范围,工业级(IND)......................................................-40°C - +85°C
存储温度范围...........................................................................-55°C - +125°C
焊接温度(导线,10秒钟)............................................................................260°C
焊接温度(表面安装)......................................................见 IPC/JEDEC J-STD-020
超出绝对最大额定值表中列出的应力会使器件产生永久损坏.这些只是额定值,不包括处于或者超出明书 的工作区间所指明的状态的功能性操作. 长期处于绝对最大额定值会影响器件的可靠性

这一块后面做到定时开关机在详细补充。

2.6 时钟动态设置

要做可以设置的时间,首先需要操作按键,我们用独立按键模块来实现。

K1:切换模式(0-显示模式【默认】,1-设置模式)

K2:在设置模式下选中要设置的项【年、月、日、时、分、秒、星期】

K3:实现当前选中项的值-1

K4:实现当前选中项的值+1

cpp 复制代码
    if(KeyNum==2)//按键2按下
	{ 
        //设置索引加1
	}
	if(KeyNum==4)//按键3按下
	{
		//设置位数字+1
	}
	if(KeyNum==3)//按键3按下
	{
		//设置位数字-1
	} 



    	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;	//0 显示模式
			case 1:TimeSet();break;		//1 设置模式
		}

在设置模式下我们希望设置位闪烁(原理是500毫秒显示空,500毫秒显示数值

cpp 复制代码
void Timer1_Routine() interrupt 1
{
	static unsigned int T0Count;
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	T0Count++;
	if(T0Count>=500)//每500ms进入一次
	{
		T0Count=0;
		TimeSetFlashFlag=!TimeSetFlashFlag;//闪烁标志位取反
	}
}

三,创建Keil项目

详细参考:51单片机STC89C52RC------创建Keil项目-CSDN博客

四,代码

LCD1602.c

cpp 复制代码
#include <REGX52.H>

//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0

/**
 * 函    数:LCD1602延时函数,12MHz调用可延时1ms
 * 参    数:无
 * 返 回 值:无
 */
void LCD_Delay()
{
	unsigned char i, j;

	i = 2;
	j = 239;
	do
	{
		while (--j);
	} while (--i);
}
/**
 * 函    数:LCD1602写命令
 * 参    数:Command 要写入的命令
 * 返 回 值:无
 */
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();
}
/**
 * 函    数:LCD1602写数据
 * 参    数:Data 要写入的数据
 * 返 回 值:无
 */
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();
}
/**
 * 函    数:LCD1602设置光标位置
 * 参    数:Line 行位置,范围:1~2
 * 返 回 值:Column 列位置,范围:1~16
 */
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));
	}
}
/**
 * 函    数:LCD1602初始化函数
 * 参    数:无
 * 返 回 值:无
 */
void LCD_Init()
{
	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
	LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
	LCD_WriteCommand(0x01);//光标复位,清屏
}
/**
 * 函    数:在LCD1602指定位置上显示一个字符
 * 参    数:	Line 行位置,范围:1~2,
 *				Column 列位置,范围:1~16,
 *				Char 要显示的字符
 * 返 回 值:无
 */
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
	LCD_SetCursor(Line,Column);
	LCD_WriteData(Char);
}
/**
 * 函    数:在LCD1602指定位置开始显示所给字符串
 * 参    数:	Line 起始行位置,范围:1~2,
 * 				Column 起始列位置,范围:1~16,
 *				String 要显示的字符串
 * 返 回 值:无
 */
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]);
	}
}
/**
 * 函    数:返回值=X的Y次方
 * 参    数:X底数,Y指数
 * 返 回 值: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;
}
/**
 * 函    数:在LCD1602指定位置开始显示所给数字
 * 参    数:	Line 起始行位置,范围:1~2
 *				Column 起始列位置,范围:1~16
 *				Number 要显示的数字,范围:0~65535
 *				Length 要显示数字的长度,范围:1~5
 * 返 回 值:无
 */
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');
	}
}
/**
 * 函    数:在LCD1602指定位置开始以有符号十进制显示所给数字
 * 参    数:	Line 起始行位置,范围:1~2
 *				Column 起始列位置,范围:1~16
 *				Number 要显示的数字,范围:-32768~32767
 *				Length 要显示数字的长度,范围:1~5
 * 返 回 值:无
 */
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');
	}
}
/**
 * 函    数:在LCD1602指定位置开始以十六进制显示所给数字
 * 参    数:	Line 起始行位置,范围:1~2
 *				Column 起始列位置,范围:1~16
 *				Number 要显示的数字,范围:0~0xFFFF
 *				Length 要显示数字的长度,范围:1~4
 * 返 回 值:无
 */
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');
		}
	}
}
/**
 * 函    数:在LCD1602指定位置开始以二进制显示所给数字
 * 参    数:	Line 起始行位置,范围:1~2
 *				Column 起始列位置,范围:1~16
 *				Number 要显示的数字,范围:0~1111 1111 1111 1111
 *				Length 要显示数字的长度,范围:1~16
 * 返 回 值:无
 */
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

cpp 复制代码
#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

Delay.c

cpp 复制代码
/**
 * 函    数:延时函数 毫秒
 * 参    数:ms 延时多少毫秒
 * 返 回 值:无
 */
void Delay_ms(int ms)	//@12.000MHz
{
	unsigned char data i, j;
	while(ms--)
	{ 
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
	}
}
/**
 * 函    数:延时函数 毫秒
 * 参    数:ms 延时多少毫秒
 * 返 回 值:无
 */
void Delay_us(int ms)	//@12.000MHz
{
	unsigned char data i, j;
	while(ms--)
	{ 
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
	}
}
/**
 * 函    数:延时函数  10微秒
 * 参    数:无
 * 返 回 值:无
 */
void Delay_10us(int _10us)	//@11.0592MHz
{
	unsigned char data i;
	while(_10us--)
	{
		i = 2;
		while (--i);
	}
}

Delay.h

cpp 复制代码
#ifndef __DELAY_H_
#defind __DELAY_H_ 
void Delay_ms(int ms);
void Delay_10us(int _10us);
#endif

DS1302.c

cpp 复制代码
#include <REGX52.H>

//引脚定义
sbit DS1302_SCLK=P3^6;	//时钟针脚 输入. SCLK 用来同步串行接口上的数据动作.此管脚内部有一个
						//40kΩ(典型值)的下拉电阻连接到地

sbit DS1302_IO=P3^4;	//数据针脚 推挽输出.I/O 管脚是三线接口的双向数据管脚.此管脚内部有
						//一个 40kΩ(典型值)的下拉电阻连接到地

sbit DS1302_CE=P3^5;	//重置位针脚  CE信号在读写时必须保持高电平.此管脚内部有一个 40kΩ(典
						//型值)的下拉电阻连接到地. 注意:先前的数据手册修正把 CE当作 
						//RST. 管脚的功能没有改变.


//寄存器写入地址/指令定义
#define DS1302_YEAR			0x8C	//年
#define DS1302_MONTH		0x88	//月	
#define DS1302_DATE			0x86	//日
#define DS1302_HOUR			0x84	//时
#define DS1302_MINUTE		0x82	//分
#define DS1302_SECOND		0x80	//秒
#define DS1302_WEEK			0x8A	//星期
#define DS1302_WP			0x8E	//写保护位 
									//控制寄存器的位 7是写保护位,前 7位(位 0至位 6)被强制为 0且读取时总是读 0. 在任
									//何对时钟或 RAM的写操作以前,位 7必须为 0.当为高时,写保护位禁止任何寄存器的写操
									//作. 初始加电状态未定义. 因此,在试图写器件之前应该清除 WP位.

//时间数组,索引0~6分别为年、月、日、时、分、秒、星期
unsigned char DS1302_Time[]={24,6,26,18,59,55,3};

/**
 * 函    数:DS1302初始化
 * 参    数:无
 * 返 回 值:无
 */
void DS1302_Init(void)
{
	DS1302_CE=0;	//重置数据引脚,没有读写数据时给低电平
	DS1302_SCLK=0;	//时钟引脚,没有读写操作时给低电平
}

/**
 * 函    数:DS1302写一个字节
 * 参    数:Addr 命令字/DS1302寄存器地址
			 Data 要写入的数据
 * 返 回 值:无
 */
void DS1302_WriteByte(unsigned char Addr,Data)
{
	unsigned char i;
	DS1302_CE=1;//读写数据时都置高电平
	/*在IO上先写入寄存器地址*/
	for(i=0;i<8;i++)
	{
		DS1302_IO=Addr&(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;
}
 
/**
 * 函    数:DS1302读一个字节
 * 参    数:Addr 命令字/DS1302寄存器地址
 * 返 回 值:无
 */
unsigned char DS1302_ReadByte(unsigned char Addr)
{
	unsigned char i,Data=0x00;
	Addr|=0x01;		//将指令转换为读指令
	DS1302_CE=1;	//读写数据时都置高电平
	for(i=0;i<8;i++)
	{
		DS1302_IO=Addr&(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;
}
 
/**
 * 函    数:DS1302设置时间,调用之后,DS1302_Time数组的数字会被设置到DS1302中
 * 参    数:无
 * 返 回 值:无
 */
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_WEEK,DS1302_Time[6]/10*16+DS1302_Time[6]%10);
	DS1302_WriteByte(DS1302_WP,0x80);
}

/**
 * 函    数:DS1302读取时间,调用之后,DS1302中的数据会被读取到DS1302_Time数组中
 * 参    数:无
 * 返 回 值:无
 */
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_WEEK);
	DS1302_Time[6]=Temp/16*10+Temp%16;
}

DS1302.h

cpp 复制代码
#ifndef __DS1302_H__
#define __DS1302_H__

//外部可调用时间数组,索引0~6分别为年、月、日、时、分、秒、星期
extern unsigned char DS1302_Time[];

void DS1302_Init(void);
void DS1302_WriteByte(unsigned char Addr,Data);
unsigned char DS1302_ReadByte(unsigned char Addr);
void DS1302_SetTime(void);
void DS1302_ReadTime(void);

#endif

4.1 读写显示时间 main.c

cpp 复制代码
#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"

void main()
{
	LCD_Init();		//LCD1602 液晶显示屏初始化
	DS1302_Init();	//DS1302 时钟初始化
	LCD_ShowString(1,1,"    -  -   W: ");//静态字符初始化显示
	LCD_ShowString(2,1,"    :  :  ");
	
	DS1302_SetTime();//设置时间
	
	while(1)
	{
		DS1302_ReadTime();//读取时间
		LCD_ShowNum(1,1,DS1302_Time[0]+2000,4);//显示年
		LCD_ShowNum(1,6,DS1302_Time[1],2);//显示月
		LCD_ShowNum(1,9,DS1302_Time[2],2);//显示日
		LCD_ShowNum(2,3,DS1302_Time[3],2);//显示时
		LCD_ShowNum(2,6,DS1302_Time[4],2);//显示分
		LCD_ShowNum(2,9,DS1302_Time[5],2);//显示秒
		LCD_ShowNum(1,14,DS1302_Time[6],1);//显示星期
	}
}

4.2 手动设置时间、重新上电显示最后设置的时间 main.c

cpp 复制代码
#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Key.h"
#include "Timer1.h"
unsigned char KeyNum,MODE,TimeSetSelect,TimeSetFlashFlag;
/**
 * 函    数:显示时间
 * 参    数:无
 * 返 回 值:无
 */
void TimeShow(void)//时间显示功能
{
	DS1302_ReadTime();//读取时间
	LCD_ShowNum(1,1,DS1302_Time[0]+2000,4);//显示年
	LCD_ShowNum(1,6,DS1302_Time[1],2);//显示月
	LCD_ShowNum(1,9,DS1302_Time[2],2);//显示日
	LCD_ShowNum(2,3,DS1302_Time[3],2);//显示时
	LCD_ShowNum(2,6,DS1302_Time[4],2);//显示分
	LCD_ShowNum(2,9,DS1302_Time[5],2);//显示秒
	LCD_ShowNum(1,14,DS1302_Time[6],1);//显示星期
}

/**
 * 函    数:设置时间
 * 参    数:无
 * 返 回 值:无
 */
void TimeSet(void)//时间设置功能
{
	if(KeyNum==2)//按键2按下
	{
		TimeSetSelect++;//设置选择位加1
		TimeSetSelect%=7;//越界清零
	}
	if(KeyNum==4)//按键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(DS1302_Time[6]>6){DS1302_Time[6]=0;}//星期越界判断
	}
	if(KeyNum==3)//按键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;}//秒越界判断
		if(DS1302_Time[6]<0){DS1302_Time[6]=6;}//星期越界判断
	}
	//更新显示,根据TimeSetSelect和TimeSetFlashFlag判断可完成闪烁功能
	if(TimeSetSelect==0 && TimeSetFlashFlag==1){LCD_ShowString(1,3,"  ");}
	else {LCD_ShowNum(1,3,DS1302_Time[0],2);}
	if(TimeSetSelect==1 && TimeSetFlashFlag==1){LCD_ShowString(1,6,"  ");}
	else {LCD_ShowNum(1,6,DS1302_Time[1],2);}
	if(TimeSetSelect==2 && TimeSetFlashFlag==1){LCD_ShowString(1,9,"  ");}
	else {LCD_ShowNum(1,9,DS1302_Time[2],2);}
	if(TimeSetSelect==3 && TimeSetFlashFlag==1){LCD_ShowString(2,3,"  ");}
	else {LCD_ShowNum(2,3,DS1302_Time[3],2);}
	if(TimeSetSelect==4 && TimeSetFlashFlag==1){LCD_ShowString(2,6,"  ");}
	else {LCD_ShowNum(2,6,DS1302_Time[4],2);}
	if(TimeSetSelect==5 && TimeSetFlashFlag==1){LCD_ShowString(2,9,"  ");}
	else {LCD_ShowNum(2,9,DS1302_Time[5],2);}
	if(TimeSetSelect==6 && TimeSetFlashFlag==1){LCD_ShowString(1,14," ");}
	else {LCD_ShowNum(1,14,DS1302_Time[6],1);}	
}
void main()
{
	LCD_Init();		//LCD1602 液晶显示屏初始化
	DS1302_Init();	//DS1302 时钟初始化
	Timer1_Init();	//初始化定时器
	LCD_ShowString(1,1,"    -  -   W: ");//静态字符初始化显示
	LCD_ShowString(2,1,"    :  :  ");
	 
	
	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;	//0 显示模式
			case 1:TimeSet();break;		//1 设置模式
		}
	}
}


void Timer1_Routine() interrupt 1
{
	static unsigned int T0Count;
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	T0Count++;
	if(T0Count>=500)//每500ms进入一次
	{
		T0Count=0;
		TimeSetFlashFlag=!TimeSetFlashFlag;//闪烁标志位取反
	}
}

Timer1.c

cpp 复制代码
#include <REGX52.H> 
  /**
   * 函    数:定时器0初始化,1毫秒@12.000MHz
   * 参    数:无
   * 返 回 值:无
   */
void Timer1_Init(void)
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x18;			//设置定时初值
	TH0 = 0xFC;			//设置定时初值
	TF0 = 0;			//清除TF0标志
	TR0 = 1;			//定时器0开始计时
	ET0=1;				//允许时钟1中断
	EA=1;				//打开总中断
	PT0=0;				//优先级设置为最高(0-3)
}

/*定时器中断函数模板  关联较多,直接将其写入mian.c 中
void Timer1_Routine() interrupt 1
{
	static unsigned int T0Count;
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	T0Count++;
	if(T0Count>=1000)
	{
		T0Count=0;
		
	}
}
*/

Timer1.h

cpp 复制代码
#ifndef __TIMER1_H__
#define __TIMER1_H__

void Timer1_Init(void);

#endif

五,代码编译、下载到51单片机

代码编译请参考

51单片机STC89C52RC------代码编译-CSDN博客

代码下载请参考

《51单片机STC89C52RC------STCAI-ISP代码下载-CSDN博客

相关推荐
智商偏低2 小时前
单片机之helloworld
单片机·嵌入式硬件
青牛科技-Allen3 小时前
GC3910S:一款高性能双通道直流电机驱动芯片
stm32·单片机·嵌入式硬件·机器人·医疗器械·水泵、
森焱森5 小时前
无人机三轴稳定控制(2)____根据目标俯仰角,实现俯仰稳定化控制,计算出升降舵输出
c语言·单片机·算法·架构·无人机
白鱼不小白5 小时前
stm32 USART串口协议与外设(程序)——江协教程踩坑经验分享
stm32·单片机·嵌入式硬件
S,D5 小时前
MCU引脚的漏电流、灌电流、拉电流区别是什么
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·硬件工程
芯岭技术8 小时前
PY32F002A单片机 低成本控制器解决方案,提供多种封装
单片机·嵌入式硬件
youmdt9 小时前
Arduino IDE ESP8266连接0.96寸SSD1306 IIC单色屏显示北京时间
单片机·嵌入式硬件
嘿·嘘9 小时前
第七章 STM32内部FLASH读写
stm32·单片机·嵌入式硬件
Meraki.Zhang9 小时前
【STM32实践篇】:I2C驱动编写
stm32·单片机·iic·驱动·i2c
几个几个n11 小时前
STM32-第二节-GPIO输入(按键,传感器)
单片机·嵌入式硬件