目录
一:介绍
1:具体介绍
2:DS1302总结
3:RTC
二:使用说明
1:电路图和内部结构
A:电路图
B:内部结构
C:CE
D:时钟/日历(SCLK)
E:写保护位
2:命令字节
3:寄存器地址/定义
4:时序图与数据读写
A:单字节写步骤代码(Write)
B: 单字节读步骤代码(Read)
5、BCD码
三:实例代码
1:不可调时钟
2:可调节时钟
一:介绍
1:具体介绍
2:DS1302总结
DS1302是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它可以对年、月、日、周、时、分、秒进行计时,且具有闰年补偿等多种功能
3:RTC
RTC(Real Time Clock):实时时钟,是一种集成电路,通常称为时钟芯片
二:使用说明
1:电路图和内部结构
A:电路图
B:内部结构
内部结构框图
作用说明
引脚 | 作用 | 说明 |
VCC2 | 主电源 | |
VCC1 | 备用电源 | 低功率工作在单电源和电池工作系统和低功率备用电池.在使用涓流 充电的系统中,这个管脚连接到可再充能量源. |
GND | 电源接地 | 电源地 |
X1,X2 | 32.768kHz晶振 | |
CE | 芯片使能 | 输入.CE信号在读写时必须保持高电平. |
IO | 数据输入/输出 | 输入/推挽输出.I/O 管脚是三线接口的双向数据管脚. |
SCLK | 串行时钟 | 输入. SCLK 用来同步串行接口上的数据动作 |
C:CE
D:时钟/日历(SCLK)
E:写保护位
2:命令字节
3:寄存器地址/定义
其中,第一行的CH表示时钟暂停控制位,置1表示时钟暂停,置0表示时钟静止;倒数第二行的WP表示write protect(写保护),置1表示写入操作无效;最后一行的TCS用于控制涓流充电,一般不进行设置
xxh(读或写的命令字) eg:80h写入秒的命令字 0x80=1000 0000
1------MSB (位 7)必须是逻辑 1. 如果是 0, 则禁止对 DS1302写入
0-----位 6 在逻辑 0时规定为时钟/日历数据,逻辑 1时为 RAM数据.
00 000----位 1 至 位 5 表示了输入输出的指定寄存器.
0:LSB (位 0) 在逻辑0时为写操作(输出),逻辑 1时为读操作(输入).命令字以 LSB (位 0)开始总是输入
4:时序图与数据读写
从上述时序图中可以看出,单字节写(Write)有16个脉冲,而单字节读(Read)只有15个脉冲,因为当最后一个命令字的上升沿之后的下降沿数据马上就读出来了
A:单字节写步骤代码(Write)
//一般来说&是用来清零的;
//一般来说|是用来值一的;
void DS1302_WriteByte(unsigned char Command,Data)
{ //Command为命令字(地址),Data为要写入的数据unsigned char i;DS1302_CE=1; //首先要给CE为1才可以进行写入读出操作for(i=0;i<8;i++){DS1302_IO=Command&(0x01<<i); //0000 00001//在时钟的上升沿,I/O口的数据将会被写入,在时钟的下降沿,时钟芯片的数据将会被读出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;
}
解释:
void DS1302_WriteByte(unsigned char Command,Data){//一般来说&是用来清零的;//一般来说|是用来值一的;DS1302_CE=1; //首先要给CE为1才可以进行写入读出操作DS1302_IO=Command&0x01; 0x01:0000 0001 取出最低位;将1~7位全部清零DS1302_SCLK=1;DS1302_SCLK=0;DS1302_IO=Command&0x02; 0x01:0000 0010 取出最次位;将0,2~7位全部清零DS1302_SCLK=1;DS1302_SCLK=0;DS1302_IO=Command&0x04; 0x01:0000 0100 取出最2位;将0,1,3~7位全部清零DS1302_SCLK=1;DS1302_SCLK=0;DS1302_CE=0;/*所以直接使用循环+位移解决全部的问题*/
}
B: 单字节读步骤代码(Read)
unsigned char DS1302_ReadByte(unsigned char Command){unsigned char i,Data=0x00;DS1302_CE=1; //首先要给CE为1才可以进行写入读出操作for(i=0;i<8;i++){DS1302_IO=Command&(0x01<<i);DS1302_SCLK=0; DS1302_SCLK=1;/*先给0后给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;}
第一个循环先0后1的原因:
需要把他分为2半, 如果先1后0在第段结束时已经读取到了第二段的开头
5、BCD码
BCD码转十进制:DEC=BCD/16*10+BCD%16; (2位BCD)
十进制转BCD码:BCD=DEC/10*16+DEC%10; (2位BCD)
三:实例代码
1:不可调时钟
main.c
#include <REGX52.H>
#include "LCD1602.h"
#include "Delay.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[]={23,7,16,11,35,55,7};
void DS1302_Init(void)
{DS1302_CE=0;DS1302_SCLK=0;
}//单字节写,Command为命令字(地址),Data为要写入的数据
void DS1302_WriteByte(unsigned char Command,Data){unsigned char i;DS1302_CE=1; //首先要给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;
}unsigned char DS1302_ReadByte(unsigned char Command){unsigned char i,Data=0x00;Command|=0x01; //最低位制1;确保它位读取的模式DS1302_CE=1; //首先要给CE为1才可以进行写入读出操作for(i=0;i<8;i++){DS1302_IO=Command&(0x01<<i);DS1302_SCLK=0; DS1302_SCLK=1;/*先给0后给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__//用户调用函数:extern unsigned char DS1302_Time[]; //声明数组位外部可以调用的
void DS1302_WriteByte(unsigned char Command,Data);
unsigned char DS1302_ReadByte(unsigned char Command);
void DS1302_Init(void);
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
Delay.c
void Delay(unsigned int xms)
{unsigned char i, j;while (xms--){i = 2;j = 239;do{while (--j);} while (--i);}
}
Delay.h
#ifndef __DElAY_H__
#define __DElAY_H__//用户调用函数:
void Delay(unsigned int xms);#endif
2:可调节时钟
主要是多了2个文件,和主函数的文件,其他的都一样
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;
}
Key.h
#ifndef __KEY_H__
#define __KEY_H__unsigned char Key();#endif
Timer.c
#include <REGX52.H>/*** @brief 定时器0初始化,1毫秒@12.000MHz 每一毫秒调用一次中段函数* @param 无* @retval 无*/void Timer0Init(void) //默认的为:定时器时钟12T模式
{ TMOD是不可位寻址,必须8位同时赋值 TMOD=0000 0001 TMOD &= 0xF0; //设置定时器模式 TMOD=TMOD & 1111 0000/*TMOD的低四位清零,高四位保持不变;TMOD=1010 00111010 0011 & 1111 0000(0xF0)= 1010 0000*/TMOD |= 0x01; //设置定时器模式 TMOD=TMOD | 0000 0001/*TMOD的最低位置1,高四位保持不变;TMOD=1010 00001010 0000 | 0000 0001(0x01)= 1010 0001*/TL0 = 0x18; //设置定时初值TH0 = 0xFC; //设置定时初值//TCON是可位寻址可以给一个位单独赋值TF0 = 0; //清除TF0标志;定时器0的溢出标志位TR0 = 1; //定时器0开始计时//中断的设置ET0=1; //打开中断允许位EA=1; //使能总开关PT0=0; //中断优先级默认为低
}
//void Timer0Init(void) //1毫秒@12.000MHz
//{
//
// TMOD &= 0xF0; //设置定时器模式
// TMOD |= 0x01; //设置定时器模式
// TL0 = 0x18; //设置定时初值
// TH0 = 0xFC; //设置定时初值
// TF0 = 0; //清除TF0标志
// TR0 = 1; //定时器0开始计时
//} stc自动生成/*
在这段代码中,定时器的计时和中断调用的过程如下:1. `Timer0Init()` 函数中的配置和启动操作使得定时器0开始计时。2. 定时器0以设定的频率进行计数,每次计数器增加一次。3. 当定时器0的计数器达到设定的初值(0xFC18)时,定时器0会发生溢出。4. 溢出会导致定时器0的溢出标志 `TF0` 被置位为1,表示发生了溢出事件。5. 此时,如果定时器0的中断使能位 `ET0` 为1,则中断系统会检测到定时器0的溢出事件。6. 中断系统会暂停当前的程序执行,跳转到中断向量表中对应定时器0中断的中断向量。7. 执行定时器0中断服务程序 `Timer0_Routine()`。8. 在 `Timer0_Routine()` 中,首先进行一些操作,如设置定时器的初值、更新计数变量等。9. 根据代码逻辑,在每秒(根据计数频率和初值设定)触发一次定时器0中断时,进行时间的更新操作,即将秒数 `Sec` 自增,如果秒数达到60,则将秒数清零,同时将分钟数 `Min` 自增,以此类推。10. 中断服务程序执行完毕后,返回到原来被中断的地方,继续执行被中断的程序。11. 上述步骤循环重复,定时器0不断计时并触发中断,中断服务程序周期性地更新时间变量,实现时钟的计时和更新。总结而言,定时器的调用过程是通过定时器的计数器不断进行计数,当计数器达到设定的初值时发生溢出,触发定时器的中断。中断系统检测到中断标志后,会执行相应的中断服务程序,在服务程序中进行一系列操作,如时间的更新。这样,定时器的计时和中断的调用形成了一个周期性的循环,实现了定时功能。
*//*
这是一个用于初始化定时器0的函数 `Timer0Init()` 的代码。让我们逐行解释它的执行过程:1. `TMOD &= 0xF0;` 是对定时器模式寄存器 `TMOD` 进行位操作,使用位与运算符和掩码 `0xF0`,将 `TMOD` 寄存器的低四位清零。这是为了清除原先的定时器模式设置。2. `TMOD |= 0x01;` 是对 `TMOD` 进行位操作,使用位或运算符和掩码 `0x01`,将 `TMOD` 寄存器的最低位设置为1。这是为了设置定时器0为模式1,即16位定时/计数模式。3. `TL0 = 0x18;` 将定时器0的低8位计数器 `TL0` 的值设置为0x18,作为定时器的初值。4. `TH0 = 0xFC;` 将定时器0的高8位计数器 `TH0` 的值设置为0xFC,作为定时器的初值。5. `TF0 = 0;` 将定时器0的溢出标志 `TF0` 清零,以确保定时器0初始状态下没有发生溢出。6. `TR0 = 1;` 启动定时器0,将其置为1,开始计时。7. `ET0 = 1;` 允许定时器0中断。将定时器0中断使能位 `ET0` 设置为1,以允许定时器0溢出时触发中断。8. `EA = 1;` 允许总中断。将总中断使能位 `EA` 设置为1,以允许所有中断的触发。9. `PT0 = 0;` 将定时器0的中断优先级位 `PT0` 设置为0,表示定时器0中断具有标准优先级。通过以上操作,`Timer0Init()` 函数完成了对定时器0的初始化配置,包括模式设置、初值设置、中断使能等。这样,在程序执行后续操作时,定时器0会按照设定的模式和初值进行计时,并在溢出时触发中断,从而实现定时功能。
*//*定时器中断函数模板
void Timer0_Routine() interrupt 1
{static unsigned int T0Count;TL0 = 0x18; //设置定时初值TH0 = 0xFC; //设置定时初值T0Count++;if(T0Count>=1000){T0Count=0;}
}
*/
Timer.h
#idndef __TIMER_H__
#define __TIMER_H__
void Timer0Init(void);
#endif
main.c
#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Key.h"
#include "Timer.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;//闪烁标志位取反}
}