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 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;//闪烁标志位取反}
}

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

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

相关文章

基于jsp+Servlet+mysql的汽车销售系统

基于jspServletmysql的汽车销售系统 一、系统介绍二、功能展示1.项目骨架2.登录界面3.首页4.购物车5.添加车辆6、编辑车辆信息 四、其它1.其他系统实现五.获取源码 一、系统介绍 项目类型&#xff1a;Java web项目 项目名称&#xff1a;基于JSPServlet的汽车销售系统 项目架…

WEB:Web_php_unserialize

背景知识 反序列化函数及绕过 正则表达式及绕过 题目 源码解析&#xff08;参考链接在最后&#xff09; <?php class Demo { //定义一个类private $file index.php; //变量属性public function __construct($file) { //类方法$this->file $file; …

ToT: 利用大语言模型进行有意识的问题解决(上)

ToT 摘要介绍利用大语言模型进行有意识的问题解决1. 思维分解2. 思维产生 G(p,s,k)3. 状态评估V(p,S)4. 搜索算法 实验相关工作讨论 原文&#xff1a; 摘要 语言模型正在迅速成为一般问题解决的部署&#xff0c;但在推理过程中仍然局限于 标记级别&#xff08;token-level&…

基于深度学习的高精度鸟类目标检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度鸟类目标&#xff08;鹦鹉&#xff08;Crested Myna&#xff09;、麻雀&#xff08;Eurasian Tree Sparrow&#xff09;、黑头文鸟&#xff08;Chestnut Munia&#xff09;、白领翡翠&#xff08;Collared Kingfisher&#xff09;、太阳鸟…

论文笔记:Deep Spatio-Temporal Residual Networks for Citywide Crowd FlowsPrediction

2017 AAAI 使用时空残差网络ST-ResNet 进行 城市区域流入流出客流量预测 1 研究对象 城市客流流入流出 根据经纬度将城市划分为网格 IJ 1.1 难点 空间依赖性 时间依赖性 外部影响 2 模型 3 实验 北京出租车数据纽约自行车数据 评价指标&#xff1a;RMSE

vue3.0之组合API有哪些(详解)

vue3.0之组合API有哪些 一、setup函数二、生命周期三、reactive函数四、toRef函数五、toRefs函数六、ref函数七、知识运用案例八、computed函数九、watch函数十、ref属性十一、父子通讯1.父传子2.子传父 十二、依赖注入十三、补充 v-model语法糖(简写)十四、补充 mixins语法 一…

【grasshopper】【犀流堂】【算法】Anemone雨水径流模拟-笔记

文章目录 Anemone雨水路径模拟available options可用选项grasshopper面切线几何原理 案例1&#xff1a;surface地形1. 拾取地形曲面surface2. 曲面上根据divide surface划分点points3.将曲面上的划分点用surface closest point投影到曲面上4.align plane旋转平面x轴与世界Z夹角…

OCPM和CPM有什么区别?

CPM和OCPM这两种收费模式的对比 Cpm&#xff1a;表示千次展示费用&#xff0c;是数据指标&#xff0c;也是一种出价方式。代表展现一千次的消费&#xff0c;也就是你展现1000次要给媒体多少钱 例如某企业广告曝光量是50万&#xff0c;总广告价格为10000元&#xff0c;那么千人…

mongodb练习---增删改查

环境&#xff1a; 1. 创建一个数据库 名字grade 2. 数据库中创建一个集合名字 class 3. 集合中插入若干数据 文档格式如下 &#xff5b;name:zhang,age&#xff1b;10,sex:m,hobby:[a,b,c]&#xff5d; hobby: draw sing dance basketball football pingpong compu…

计算机网络-应用层

目录 一、应用层概述 二、客户-服务器方式(C/S方式)和对等方式(P2P方式) &#xff08;一&#xff09;客户/服务器 (Client/Server&#xff0c;C/S) 方式 &#xff08;二&#xff09;对等(Peer-to-Peer&#xff0c;P2P) 方式 &#xff08;三&#xff09;总结 三、动态主机…

云原生TDengine-v3.0部署手册

云原生TDengine-v3.0部署手册 一、管理namespace1.1 创建namespace1.2 namespaces列表 二、配置3份yaml文件2.1 tdengine3-storage-class.yaml2.2 taosd-service.yaml2.3 taosd-tdengine.yaml 三、服务部署3.1 部署StorageClass3.2 部署Service3.3 部署StatefulSet3.4 查看启动…

记一次JVM调优过程

文档修订记录 版本 日期 撰写人 审核人 批准人 变更摘要 & 修订位置 JVM相关理论 JVM内存 可分配内存&#xff1a; JVM可以调度使用的总的内存数&#xff0c;这个数量受操作系统进程寻址范围、系统虚…