DS1302实时时钟模块
目录
0.单片机定时器时钟的几个缺点:
1.DS1302介绍
2.引脚定义和应用电路
3.原理图
4.内部结果框图,RAM(寄存器)
5.寄存器定义
6.时序定义
7.DS1302时钟代码
第一步:
第二步:
第三步:
第四步:
第五步:
第六步:
第七步:
第八步:
第九步:
第十步:
第十一步:
第十二步:
第十三步:
第十四步:
最终代码:
模块化:
DS1302.c
DS1302.h
main.c
8.可调时钟代码
第一步:
第二步:
第三步:
第四步:
第五步:
独立按键1:
独立按键2:
独立按键3:
独立按键4:
第六步:
第七步:
给定时器0初始化
复制定时器中断函数模板到主函数最下面,进行修改,让他1010(二进制)的变化
在TimeShow函数末尾修改为
最终代码:
模块化:
main.c
0.单片机定时器时钟的几个缺点:
1.精度不高,不如时钟芯片精度高
2.占用单片机CPU时间
3.不能断电,断电了就重新计时了。时钟芯片有备用电池,断电后还可以运行
1.DS1302介绍
DS1302是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它可以对年、月、日、周、时、分、秒进行计时,且具有闰年补偿等多种功能
RTC(Real Time Clock):实时时钟,是一种集成电路,通常称为时钟芯片
2.引脚定义和应用电路
DIP直插封装、so贴片封装、接时钟的晶振(脉冲)固定的。CE、I/O、SCLK通讯引脚,设置写入时间及读取时钟
3.原理图
4.内部结果框图,RAM(寄存器)
5.寄存器定义
寄存器、WP:读写写保护;地址:0s80
要操作RTC最高位给1,第6位操作时钟给0,操作秒地址位给0,第0位读写操作:WR:0写;即10000000->80h:写秒,读81h
6.时序定义
CE(操作使能)在整个操作过程中要给高电频,就是开始写之前给1,写完之后给0,这样整个写入操作才是有效的;对我们单片机来说,在时钟的上升沿(READ)向时钟芯片写入数据,在时钟的下降沿(WRITE)时钟芯片读出发出的数据;在SCLK时钟上升沿,IO口被写入,下降沿输出:R/W(控制读写,给1单片机输出数据,给0写完一个字节后继续写);在整个时序中只要D0~D7是1302操控,其他的部分都是单片机操控。
写入数据(在WRITE中):第一步,CE置1;第二步把命令制的最低位0/1,设置在I/O空上;第三步,时钟给个上升沿。然后命令制的最后一位就会被写入单片机。然后A0~1重复第二第三步,最后给低电频。到了发送信号给单片机时,发第一位D0给个上升沿,重复到D7,最后给CE置0。写入数据就完成了
读出数据(在READ中):D0之前的步骤和写入数据的D0之前的步骤一样;然后,要接受写入的数据,接受第一位数据给个下降沿数据到D0,重复到D7;然后,再给CE置0。读出数据就完成了
通过写入数据和读出数据的步骤,就能对时钟进行设置和读取了
7.DS1302时钟代码
第一步:
DS1302要用液晶屏显示,所以要把LCD1602液晶屏显示配置代码复制过来,导入项目中
第二步:
看原理图重新定义P35、P34、P36这三个引脚
第三步:
在DS1302.c里面定义这两个函数,有了这两个函数就很方便在的在某个地址下数据进行读写操作
第四步:
在DS1302.c里面初始化写操作,默认给0
第五步:
在DS1302.c时钟写字节
第六步:
看时序定义图,时钟读操作,先给下降沿,再上升沿,控制下面红色部分,上下区分开来:(&清零,|置1)
第七步:
头文件声明
第八步:
主函数调用
之后把读和显示的部分拉入循环
调用测试后发现的问题并解决
为什么,测试是,时钟会在9的时候直接到16,是因为BCD码的存在,BCD码存在方式:解读一下用4位二进制数来表示十进制数,0001 0011-->13为例,前面的0001(二进制)转换为1(十进制),后面的0011(二进制)转换位3(十进制),组合为13。0001(二进制)-->1(十进制),1010(二进制)-->10(十进制)组合超过两位不合法。十六进制就是先转换为二进制,在按照BCD码规则变成十进制数
第九步:
将BCD码转换为十进制显示,方法按照上述图片中的公式,注意:只能两位。这里是为了测试所以在主函数里面进行了BCD转换为十进制,后面为了方便,就模块化,然后在主函数里面调用,这样会方便很多
第十步:
定义数组优化模块 DS1302时钟&可调时钟
第十一步:
设置时间和读取时间(WP:写保护关闭,置0):写的地址或上0x01就是读地址
第十二步:
这里就是第九步中BCD转换为十进制的模块化
第十三步:
函数头文件里面声明
第十四步:
主函数调用自定义函数和设置时钟的形式
最终代码:
模块化:
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[]={25,5,9,18,00,00};/*** @brief DS1302初始化* @param 无* @retval 无*/
void DS1302_Init(void)
{DS1302_CE=0;DS1302_SCLK=0;
}/*** @brief DS1302写一个字节* @param Command 命令字/地址* @param Data 要写入的数据* @retval 无*/
void DS1302_WriteByte(unsigned char Command,Data)
{unsigned char i;DS1302_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;
}/*** @brief DS1302读一个字节* @param Command 命令字/地址* @retval 读出的数据*/
unsigned char DS1302_ReadByte(unsigned char Command)
{unsigned char i,Data=0x00;Command|=0x01; //将指令转换为读指令DS1302_CE=1;for(i=0;i<8;i++){DS1302_IO=Command&(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;
}/*** @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__//外部可调用时间数组,索引0~6分别为年、月、日、时、分、秒、星期
extern unsigned char DS1302_Time[];void DS1302_Init(void);
void DS1302_WriteByte(unsigned char Command,Data);
unsigned char DS1302_ReadByte(unsigned char Command);
void DS1302_SetTime(void);
void DS1302_ReadTime(void);#endif
main.c
#include <REGX52.H>
#include "LCD1602.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);//显示秒}
}
8.可调时钟代码
第一步:
将DS1302时钟复制整个工程
第二步:
把按键代码和定时器代码复制粘贴到工程里面
第三步:
在主函数包含头文件,其他的模块代码,不用动,写好直接用
第四步:
显示时间(最后2是显示宽度)
第五步:
设置时间设置函数(年,月(代码次复杂),日(最复杂),小时,分钟,秒)(越界判断,超出回归初始值)(调用函数Key)
独立按键1:
进入/退出设置时间模式
独立按键2:
按照年、月、日、小时、分钟、秒依此移动设置
独立按键3:
加时间
独立按键4:
减时间
第六步:
让设置的时间,可以写入时钟,在主函数加入DS1302_setTime
第七步:
进入时间设置模式让对应位闪烁(调用函数Timer0)
给定时器0初始化
复制定时器中断函数模板到主函数最下面,进行修改,让他1010(二进制)的变化
在TimeShow函数末尾修改为
最终代码:
模块化:
main.c
#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Key.h"
#include "Timer0.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)//按键4按下{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;//闪烁标志位取反}
}