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

目录

目的/效果

一,STC单片机模块

二,DS1302计时器

2.1 特性/板子位置

2.1.1 特性 

2.1.2 板子上的位置

2.2 针脚定义

2.3 数据传输

2.3.1 读数据

 2.3.2 写数据

2.4 BCD码

2.5 可编程涓流充电器

2.6 时钟动态设置

三,创建Keil项目

四,代码 

4.1 读写显示时间  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 针脚定义


//引脚定义
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 数据传输

 

 寄存器地址

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




//寄存器写入地址/指令定义
#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 读数据

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


/**
 * 函    数: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 写数据

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


/**
 * 函    数: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) 

补充完读写操作如下

//时间数组,索引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

    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毫秒显示数值


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

#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

#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

/**
 * 函    数:延时函数 毫秒
 * 参    数: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

#ifndef __DELAY_H_
#defind __DELAY_H_ 
void Delay_ms(int ms);
void Delay_10us(int _10us);
#endif

 DS1302.c

#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

#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

#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

#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

#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

#ifndef __TIMER1_H__
#define __TIMER1_H__

void Timer1_Init(void);

#endif

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

代码编译请参考

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

代码下载请参考

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/747553.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode 102.目标和

给定一个正整数数组 nums 和一个整数 target 。 向数组中的每个整数前添加 ‘’ 或 ‘-’ &#xff0c;然后串联起所有整数&#xff0c;可以构造一个 表达式 &#xff1a; 例如&#xff0c;nums [2, 1] &#xff0c;可以在 2 之前添加 ‘’ &#xff0c;在 1 之前添加 ‘-’ &…

2024最新pd激活码 Parallels Desktop 19 激活秘钥分享

Parallels Desktop 19 for Mac 乃是一款适配于 Mac 的虚拟化软件。它能让您在 Mac 计算机上同时运行多个操作系统。您可借此创建虚拟机&#xff0c;并于其中装设不同的操作系统&#xff0c;如 Windows、Linux 或 macOS。使用 Parallels Desktop 19 mac 版时&#xff0c;您可在 …

【UE5.3】笔记2--资源导入

资源导入 方式一&#xff1a;内置资源--初学者内容包 方式二&#xff1a;虚幻商城 搜索免费资源&#xff1a; 添加到工程之后 搜素&#xff1a;虚幻学习工具包&#xff0c;需要注意的是支持的引擎版本 当然商城里包含了大量的免费的资源&#xff0c;初期学习不想投入太多可以…

OpenCL在移动端GPU计算中的应用与实践

一、引言 移动端芯片性能的不断提升为在手机上进行计算密集型任务&#xff0c;如计算机图形学和深度学习模型推理&#xff0c;提供了可能。在Android设备上&#xff0c;GPU&#xff0c;尤其是高通Adreno和华为Mali&#xff0c;因其卓越的浮点运算能力&#xff0c;成为了异构计…

OZON跨境卖家爆款产品有哪些

OZON跨境卖家爆款产品有哪些&#xff1f;国内的Ozon跨境卖家做这几个品&#xff0c;不爆都难&#xff01; Top1 太阳镜 Очки солнцезащитные 商品id&#xff1a;1556874194 月销量&#xff1a;1095 OZON跨境卖家爆款产品工具&#xff1a;D。DDqbt。COm/…

【Docker】Docker简介_运行原理

1、简介 1.1基本概念 容器&#xff1a;容器是Docker的基本部署单元。它是一个轻量级的、独立的运行时环境&#xff0c;包含应用程序及其相关依赖。容器利用Linux内核的命名空间和控制组技术&#xff0c;实现了隔离性和资源管理&#xff0c;使得应用程序在不同的容器中运行不会…

2024 最新运营小工具 API 推荐,助力高效工作

在当今数字化运营的时代&#xff0c;各种高效便捷的 API 服务成为了企业和个人提升运营效率、获取精准数据的得力助手。无论是进行市场调研、拓展业务&#xff0c;还是优化网络资源配置&#xff0c;都离不开这些强大的工具。本文将为您详细介绍一系列实用的运营小工具 API 服务…

使用API有效率地管理Dynadot域名,为文件夹更名

关于Dynadot Dynadot是通过ICANN认证的域名注册商&#xff0c;自2002年成立以来&#xff0c;服务于全球108个国家和地区的客户&#xff0c;为数以万计的客户提供简洁&#xff0c;优惠&#xff0c;安全的域名注册以及管理服务。 Dynadot平台操作教程索引&#xff08;包括域名邮…

【HarmonyOS4学习笔记】《HarmonyOS4+NEXT星河版入门到企业级实战教程》课程学习笔记(十八)

课程地址&#xff1a; 黑马程序员HarmonyOS4NEXT星河版入门到企业级实战教程&#xff0c;一套精通鸿蒙应用开发 &#xff08;本篇笔记对应课程第 28 节&#xff09; P28《27.网络连接-Http请求数据》 案例&#xff1a; 这里不懂后端假设服务器的前端小伙伴就需要课程源码资料了…

华三交换机的软件版本升级操作

升级操作很常见&#xff0c;掌握方法是关键 实验环境&#xff1a;1台华三S6520-EI交换机&#xff0c;版本从2432P03升级成2432P05。 整体思路&#xff1a; 1.先查验软件版本 2.官网下载对于设备型号的软件版本 3.配置交换机地址使得与电脑进行通信&#xff0c;使用TFTP/FTP工…

宿主机无法通过ip连接wsl2解决方案

文章目录 原因排查网络模式win11防火墙关闭wsl ubuntu防火墙 如果之前能连接现在连接不上可以参考该方案 原因排查 网络模式win11防火墙(win11新增了Hyper-V防火墙)wsl2 ubuntu防火墙 网络模式 wsl2的默认网络模式是NAT&#xff0c;建议修改为镜像模式。在C:\Users\<User…

深圳,不止是“搞钱之都”

深圳又结结实实火了一把。 “建议深圳人吃饭不要谈工作”&#xff0c;这条微博话题热度飙升&#xff0c;超过五百多万人围观&#xff0c;引来无数网友吐槽“深圳人饭局的真实写照”。 从高档粤菜包间到路边小摊&#xff0c;从茶餐厅到烧烤摊&#xff0c;深圳人吃饭似乎总绕不…

Objects and Classes (对象和类)

Objects and Classes [对象和类] 1. Procedural and Object-Oriented Programming (过程性编程和面向对象编程)2. Abstraction and Classes (抽象和类)2.1. Classes in C (C 中的类)2.2. Implementing Class Member Functions (实现类成员函数)2.3. Using Classes References O…

华为---VRRP基本配置(一)

10、VRRP 10.1 VRRP基本配置 10.1.1 原理概述 随着Internet的发展&#xff0c;人们对网络可靠性的要求越来越高。对于用户来说&#xff0c;能够时刻与外部网络保持通信非常重要&#xff0c;但内部网络中的所有主机通常只能设置一个网关IP地址&#xff0c;通过该出口网关实现…

前端打包配置+nginx配置实现部署及部署地址带特定前缀的几种方式

前端打包后要部署到服务器&#xff0c;在浏览器中可以通过url访问到我们开发的系统&#xff0c;通过nginx代理在工作中是一种很常用的方式。 这里以本地为例&#xff0c;把本地电脑当作一个服务器&#xff0c;实现普通部署、带特定前缀等 前端使用vue-clivue作为例子 以下内容…

电脑突然提示dll文件丢失,怎么选择正确的恢复方法?

电脑突然提示dll文件丢失&#xff1f;其实当你的电脑使用久了&#xff0c;出现这种dll文件丢失是非常的正常的&#xff0c;毕竟你总会有不恰当的操作吧&#xff1f;这些操作都是会导致dll文件丢失的。丢失了&#xff0c;我们直接进行相关的修复就好了&#xff0c;还是比较简单的…

Qt开发 | Qt控件 | QTabWidget基本用法 | QListWidget应用详解 | QScrollArea应用详解

文章目录 一、QTabWidget基本用法二、QListWidget应用详解1.列表模式1.1 基本操作1.2 添加自定义item1.3 如何添加右键菜单1.4 QListWidget如何删除item 2.图标模式 三、QScrollArea应用详解 一、QTabWidget基本用法 QTabWidget 是 Qt 框架中的一个类&#xff0c;它提供了一个选…

C++学习/复习18----迭代器/反向迭代器及在list/vector中的应用、list与vector模拟实现复习

迭代器是一个对象&#xff0c;可以循环访问 C 标准库容器中的元素&#xff0c;并提供对各个元素的访问。 C 标准库容器全都提供迭代器&#xff0c;以便算法可以采用标准方式访问其元素&#xff0c;而不必考虑用于存储元素的容器类型。 一、反向迭代器类 基于普通迭代器构建反…

【Chapter8】文件系统,计算机操作系统教程,第四版,左万利,王英

文章目录 [toc]一、文件与文件系统1.1 文件1.2 文件系统 二、文件的访问方式2.1 顺序访问2.2 随机访问 三、文件的组织3.1 文件的逻辑组织3.2 文件的物理组织3.2.1 顺序结构3.2.2 链接结构3.2.3 索引结构3.2.4 Hash 结构3.2.5 倒排结构 3.3 UNIX文件物理结构&#xff08;索引链…

HarmonyOS Next开发学习手册——进程模型线程模型

进程模型 系统的进程模型如下图所示&#xff1a; 应用中&#xff08;同一包名&#xff09;的所有PageAbility、ServiceAbility、DataAbility、FormAbility运行在同一个独立进程中&#xff0c;即图中绿色部分的“Main Process”。 WebView拥有独立的渲染进程&#xff0c;即图中…