STC89C52--实时时钟(DS1302)

目录

一:介绍

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:具体介绍

        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: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

        所有数据传输开始驱动CE输入高.CE输入实现两个功能.第一,CE开启允许对地址/命令序
列的移位寄存器进行读写的控制逻辑.第二CE信号为单字节和多字节CE数据传输提供了
终止的方法.
        
        一个时钟周期是一系列的上升沿伴随下降沿.要输入数据在时钟的上升沿数据必须有效,而
且在下降沿要输出数据位.如果CE输入为低电平,则所有数据传输终止,并且I/O口成高阻
抗状态.

D:时钟/日历(SCLK)

        读取适当的寄存器字节可以得到时间和日历信息.表3说明了RTC寄存器.写入适当的寄
存器字节可以设置或初始化时间和日历.时间和日历寄存器的内容是二进制编码的十进制(BCD)格式的. 在时钟的上升沿,I/O口的数据将会被写入,在时钟的下降沿,时钟芯片的数据将会被读出

E:写保护位

        控制寄存器的位7是写保护位,前7位(位0至位6)被强制为0且读取时总是读0.在任
何对时钟或RAM的写操作以前,位7必须为0.当为高时,写保护位禁止任何寄存器的写操
作.初始加电状态未定义.因此,在试图写器件之前应该清除WP位

2:命令字节

        显示的是命令字.命令字启动每一次数据传输. MSB (位 7)必须是逻辑 1. 如果是 0,
则禁止对 DS1302写入. 位 6 在逻辑 0时规定为时钟/日历数据,逻辑 1时为 RAM数据.
位 1 至 位 5 表示了输入输出的指定寄存器.LSB (位 0) 在逻辑0时为写操作(输出),逻辑
1时为读操作(输入).命令字以 LSB (位 0)开始总是输入   (上面有一个横线表示低电频)
DS1302作用: 在哪 写入 什么
                      在哪 读出  什么     命令字完成了:在哪 写入和 在哪 读出

3:寄存器地址/定义

RTC

         其中,第一行的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 0011
    1010 0011 & 1111 0000(0xF0)= 1010 0000
    */
	TMOD |= 0x01;		//设置定时器模式   TMOD=TMOD | 0000 0001
    /*
    TMOD的最低位置1,高四位保持不变;
    TMOD=1010 0000
    1010 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++;//设置选择位加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;//闪烁标志位取反
	}
}

猜你喜欢

转载自blog.csdn.net/m0_74739916/article/details/131738157