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++;//设置选择位加1TimeSetSelect%=7;//越界清零}if(KeyNum==4)//按键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(DS1302_Time[6]>6){DS1302_Time[6]=0;}//星期越界判断}if(KeyNum==3)//按键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;}//秒越界判断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博客》

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《数据赋能:一本书讲透数字化营销与运营》—— 从正确的数据观开始

基于数据打通的“全链路”营销是当下的“时髦”&#xff0c;应用它的前提是什么&#xff1f;深度营销和运营的关键数据如何获得&#xff1f;如何利用数据进行更精准的营销投放&#xff1f;如何利用数据优化投放的效果&#xff1f;如何促进消费者的转化&#xff0c;以及激活留存…

在 Postman 中使用 Body 进行 POST 请求

Postman 是开发者日常工具箱中不可缺少的一部分&#xff0c;特别是在 API 开发和调试环节中。 为什么使用 POST 请求 POST 请求用于向服务器发送数据&#xff0c;这些数据通常被处理后存储。与 GET 请求不同&#xff0c;POST 请求将数据嵌入请求体&#xff08;Body&#xff0…

算法刷题日志 hot100 数组分类

文章目录 [169. 多数元素](https://leetcode.cn/problems/majority-element/)旋转图像215. 数组中的第K个最大元素](https://leetcode.cn/problems/kth-largest-element-in-an-array/description/)238. 除自身以外数组的乘积](https://leetcode.cn/problems/product-of-array-e…

HQChart使用教程30-K线图如何对接第3方数据40-日K叠加股票增量更新

HQChart使用教程30-K线图如何对接第3方数据40-日K叠加股票增量更新 叠加股票叠加日K更新Request 字段说明Data.symbol 协议截图返回json数据结构stock HQChart代码地址交流 叠加股票 示例地址:https://jones2000.github.io/HQChart/webhqchart.demo/samples/kline_index_edit.…

等保相关总结

等级划分准则 等保2.0基本框架 等保2.0变化解读 等级测评 3保1评 分保工作简介 分保工作流程 等保 等保工作流程&#xff1a;定级 -》备案 -》整改 -》测评 -》复核 关保 密评

【自监督-MIM】系列方法学习二

Masked image modeling 是一种训练深度学习模型的技术,尤其是在视觉领域,类似于自然语言处理中的掩码语言建模(Masked Language Modeling)。它通过在输入图像中随机遮挡(或称为掩码)部分区域,然后训练模型来预测这些被遮挡部分的内容,从而提高模型的视觉理解能力。 Ma…

linux应用开发基础知识(七)——管道和消息队列进程通信

管道通信 匿名管道 #include <unistd.h> int pipe(int pfd[2]);pfd[0]用于读管道&#xff0c;而pdf[1]用于写管道。 注意&#xff1a;匿名管道只能用于亲缘关系的进程之间通信。管道通道是单向的&#xff0c;一边读&#xff0c;另一边写。管道可以用于大于两个进程共…

怎么使用python进行整除取余求幂

怎么使用python进行整除取余求幂&#xff1f; 整除法是//&#xff0c;称为地板除&#xff0c;两个整数的除法仍然是整数。 10//33 3 求模运算是%&#xff0c;相当于mod&#xff0c;也就是计算除法的余数。 5%2 1 求幂运算使用两个连续的*&#xff0c;幂运算符比取反的优先级高…

2024 最新推广服务 API 推荐,助力业务腾飞

在数字化营销的浪潮中&#xff0c;API 服务正以其强大的功能和高效的特性&#xff0c;成为企业和开发者们实现精准推广、优化营销效果的得力助手。2024 年的今天&#xff0c;各种创新的 API 服务层出不穷&#xff0c;为广告投放、数据洞察等领域带来了前所未有的机遇。在接下来…

数字设计的秘密:原来有这么多创意玩法!

数字产品设计涵盖了各种学科和角色&#xff0c;但主要是人与数字设备或数字界面之间的互动。数字设计的两个主要部分是用户体验设计&#xff08;UX&#xff09;和用户界面设计&#xff08;UI&#xff09;。如果把数字产品的设计想象成人体&#xff0c;那么骨架就是代码&#xf…

从0-1搭建一个web项目vue3+vite+ts+element-plus(脚手架分析)

本章分析从0-1的搭建脚手架依赖 ObJack-Admin一款基于 Vue3.3、TypeScript、Vite3、Pinia、Element-Plus 开源的后台管理框架。在一定程度上节省您的开发效率。另外本项目还封装了一些常用组件、hooks、指令、动态路由、按钮级别权限控制等功能。感兴趣的小伙伴可以访问源码点个…

结构体(二)

今天来继续介绍我们有关结构体的相关知识 结构体的自引用 结构体的自引用&#xff0c;顾名思义嘛&#xff0c;就是在我们的结构体中再次引用该结构体&#xff0c;这一点跟我们的函数递归有异曲同工之妙&#xff0c;不了解函数递归的小伙伴可以移步到我之前做过的一期&#xf…

GPTCache:革新大模型缓存,降低成本,提升效率

GPTCache介绍 随着应用程序越来越受欢迎并遇到更高的流量水平,与 LLM API 调用相关的费用可能会变得相当可观。此外,LLM 服务的响应时间可能会很慢,尤其是在处理大量请求时。GPTCache是一个致力于构建用于存储 LLM 响应的语义缓存的项目。 项目架构 数字人助力传统客服 1…

效率神奇分享

嘿宝贝们&#xff01;&#x1f44b; 知道吗&#xff1f;工作效率高不高&#xff0c;其实跟你用的工具有很大关系哦&#xff01;今天小红书种草专家来给大家安利五款超实用的国产工作app&#xff0c;让你的工作生活轻松又高效&#xff01;&#x1f680; 1️⃣ 【亿可达】&#…

植物大战僵尸杂交版技巧大全(附下载攻略)

《植物大战僵尸杂交版》为策略游戏爱好者带来了全新的挑战和乐趣。如果你是新手玩家&#xff0c;可能会对游戏中的植物和僵尸感到困惑。以下是一些实用的技巧&#xff0c;帮助你快速掌握游戏并享受其中的乐趣。 技巧一&#xff1a;熟悉基本玩法 游戏的基本玩法与原版相似&…

【Android】多种方式实现截图(屏幕截图、View截图、长图)

目录 一、截图原理二、实现方式1. View截图2. WebView截图3. 屏幕截图 三、格式转换方法 一、截图原理 我们的手机一般同时按下音量-键和电源键就会将当前屏幕显示的内容截取下来&#xff0c;那里面具体经过哪些流程呢&#xff1f; Android中每一个页面都是一个Activity&#…

Java洗鞋小程序预约系统源码

&#x1f4a5;洗鞋神器来袭&#xff01;轻松预约&#xff0c;让你的鞋子焕然一新&#x1f45f; &#x1f389; 告别洗鞋烦恼&#xff0c;洗鞋预约小程序来啦&#xff01; 你是不是常常为洗鞋而烦恼&#xff1f;手洗太累&#xff0c;送去洗衣店又贵又麻烦。现在&#xff0c;好…

std::enable_if和std::is_base_of

std::enable_if,其主要为了完成模板特偏化&#xff0c;有两个参数&#xff0c;第一个为布尔值类型&#xff0c;第二个如果布尔值为true&#xff0c;其为默认空值&#xff0c;如果已经赋值&#xff0c;则为对应的类型。 std::is_base_of&#xff0c;其一共存在两个参数&#xff…

外汇的基本面分析需要关注什么?

外汇基本面分析的核心在于关注可能影响单一货币供求及国家货币价值的经济、社会和地缘政治事件与趋势。但值得注意的是&#xff0c;这些事件和因素往往具有更广泛的影响力&#xff0c;不仅限于单一国家。它们可能是影响整个地区或国家集团的重要事件&#xff0c;甚至一些事件&a…

数学建模 —— MATLAB中的矩阵(下)

目录 矩阵的拼接 矩阵的重复 矩阵的重构和重新排列 (1)reshape 函数 (2)sort 函数 (3)sortrows 函数 (4)flip / fliplr / flipud 函数 (5)rot90 函数 矩阵的拼接 有时候我们需要对多个矩阵进行拼接&#xff0c;变成一个大的矩阵。根据矩阵拼接的方向&#xff0c;我们可 …