1.简介
2.引脚定义和应用电路
3.原理图
原理图中VCC1没有接备用电池,所以掉电之后不能继续计时
4.相关寄存器
1)控制寄存器
红色的框框就是它的一个地址的值,比如秒寄存器,读的话,1000 0000 转换为十六进制为0x80
2)日历/时钟寄存器
5.时序
6.BCD码
其中0001 1010不合法是因为4位二进制表示1位十进制,1010表示10,不是1位十进制数了,要想正确表示十进制的10,应该0001 0000
举例:
BCD码转十进制:比如0010 0011,即0x23,转为十进制,0x23/16*10=35/16*10=20,0x23%16=35%16=3,那么十进制为23
十进制转BCD同理
注意的是:写操作时候,要将十进制转换为BCD码,然后写入到DS1302中
读操作时候,将读取的BCD码转换为十进制数字
7.DS1302时钟
将跳线帽如此插入,不然LCD1602显示有问题
步骤:
1)进行模块化DS1302
根据时序图我们来进行编写DS1302的初始化函数和读/写操作
值得注意的是,读操作是15个脉冲,最后一个上升沿紧接着就是下降沿的到来;
写操作是是十六个脉冲
DS1302初始化
DS1302写操作
DS1302读操作
设置写入的时间函数
设置读取时间的函数
宏定义和数组定义
头文件
2)模块化的代码如下:
也可评论区领取
DS1302.H
#ifndef __DS1302_H__
#define __DS1302_H__
//为什么加extern,因为我们在DS1302中定义的时候是全局的一个符号
//要是我们想在别的文件中使用,就需要使用extern来进行声明
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
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//写保护
//同时数据也需要变成外部可调用的,在头文件中进行声明
unsigned char DS1302_Time[]={24,2,20,9,34,46,2};//年,月,日,时,分,秒,星期
void DS1302_Init(void)//DS1302初始化
{
DS1302_CE=0;
DS1302_SCLK=0;
}
void DS1302_WriteByte(unsigned char Command,Data)//写操作
{
unsigned char i;
DS1302_CE=1;//使能端为1
//命令控制字的写入
for(i=0;i<8;i++)
{
DS1302_IO=Command&(0x01<<i);//第一次取Command的最低位,然后进行左移0000 0001->0000 0010,以此类推
DS1302_SCLK=1;//这里看DS1302的数据手册,看看是否需要延时
DS1302_SCLK=0;//在我们这个单片机中不需要延时
}
//DS1302的数据
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;
unsigned char Data=0x00;
Command|=0x01;
//因为读的时候最低为都是1,不管秒,还是分什么的
//因此我们在进行读操作时,只需要给写的地址,就是给上面宏定义的地址即可
//命令控制字写入的脉冲加上读取数据的脉冲加起来15个,可以根据下面的自己绘画下
DS1302_CE=1;
//命令控制字的写入
for(i=0;i<8;i++)
{
DS1302_IO=Command&(0x01<<i);
DS1302_SCLK=0;
DS1302_SCLK=1;
}
//读DS1302的数据
for(i=0;i<8;i++)
{
DS1302_SCLK=1;
DS1302_SCLK=0;
if(DS1302_IO)
{
Data|=(0x01<<i);
}//Data为从DS1302读取的值
}
DS1302_CE=0;//使能清零,结束
DS1302_IO=0;//读取后将IO设置为0,否则读出的数据会出错
return Data;
}
void DS1302_SetTime(void)//设置时间函数
{
DS1302_WriteByte(DS1302_WP,0x00);//芯片处于写保护的状态,解除写保护的状态
DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);//转换为BCD码存进去.十进制转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);//打开写保护,为什么是0x80,看写保护寄存器的D7-D0
}
void DS1302_ReadTime(void)//读取时间函数
{
unsigned char Temp;
Temp=DS1302_ReadByte(DS1302_YEAR);//因为把最低为置1了,所以直接用写操作的地址即可
//这里读取的是BCD码,要转换为十进制
DS1302_Time[0]=Temp/16*10+Temp%16;
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;
}
3)主函数
#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
void main()
{
LCD_Init();//LCD1602初始化
DS1302_Init();//DS1302初始化
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时钟显示
8.DS1302可调时钟
步骤:
1)布局
2)按键1的作用(第一次按下,时间暂停,进入时间修改函数,再按一下显示修改之后的时间值)
3)时钟的正常显示函数
4)修改时间函数
按键2、3、4的功能(分别是选择修改哪个、值+1、值-1)
其中包括闰年的判断,越界的处理,不同月份的天数
在修改时间时,被选择的模块上光屏闪烁
需要结合定时器,使得500ms闪烁在空格和正常显示下来回切换,需要用到定时器0
5)主函数
代码如下:
主函数:
#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Key.h"
#include "Timer0.h"
unsigned char KeyNum,MODE,TimeSetSelect,TimeSetFlash;//键值、模式、时钟设置选择、时间设置闪烁标志
void TimerShow(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 TimerSet(void)
{
if(KeyNum==2)
{
TimeSetSelect++;//TimeSetSelect是选择修改哪个,年、月还是日还是什么
if(TimeSetSelect>5)//或者TimeSetSelect%=6;
{
TimeSetSelect=0;
}
}
if(KeyNum==3)
{
DS1302_Time[TimeSetSelect]++;//所选择的年、月、日还是什么,按一次K3加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&&DS1302_Time[0]%100!=0)||DS1302_Time[0]%400==0)
{
if(DS1302_Time[2]>29){DS1302_Time[2]=1;}
}
else
{
if(DS1302_Time[2]>28){DS1302_Time[2]=1;}
}
}
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)
{
DS1302_Time[TimeSetSelect]--;//同理K3
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;}
//防止过大,在我们设置成12月31天时,再将12月变成11月,这时候就可以变成1,而不是11月31天
}
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&&DS1302_Time[0]%100!=0)||DS1302_Time[0]%400==0)
{
if(DS1302_Time[2]<1){DS1302_Time[2]=29;}
if(DS1302_Time[2]>29){DS1302_Time[2]=1;}
}
else
{
if(DS1302_Time[2]<1){DS1302_Time[2]=28;}
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(TimeSetSelect==0&&TimeSetFlash==1){LCD_ShowString(1,1," ");}
else {LCD_ShowNum(1,1,DS1302_Time[0],2);}
if(TimeSetSelect==1&&TimeSetFlash==1){LCD_ShowString(1,4," ");}
else {LCD_ShowNum(1,4,DS1302_Time[1],2);}
if(TimeSetSelect==2&&TimeSetFlash==1){LCD_ShowString(1,7," ");}
else {LCD_ShowNum(1,7,DS1302_Time[2],2);}
if(TimeSetSelect==3&&TimeSetFlash==1){LCD_ShowString(2,1," ");}
else {LCD_ShowNum(2,1,DS1302_Time[3],2);}
if(TimeSetSelect==4&&TimeSetFlash==1){LCD_ShowString(2,4," ");}
else {LCD_ShowNum(2,4,DS1302_Time[4],2);}
if(TimeSetSelect==5&&TimeSetFlash==1){LCD_ShowString(2,7," ");}
else {LCD_ShowNum(2,7,DS1302_Time[5],2);}
}
void main()
{
LCD_Init();//LCD1602初始化
DS1302_Init();//DS1302初始化
Timer0Init();//定时器0初始化
LCD_ShowString(1,1," - - ");
LCD_ShowString(2,1," : : ");
DS1302_SetTime();
while(1)
{
KeyNum=Key();//获取键码值
if(KeyNum==1)
{
if(MODE==0){MODE=1;}
else if(MODE==1){MODE=0;DS1302_SetTime();}//再按一下K1,将更改的时间更新,然后进入switch的case0,显示在LCD1602上
}
switch(MODE)
{
case 0:TimerShow();break;
case 1:TimerSet();break;
}
}
}
void Timer0_Routine() interrupt 1
{
static unsigned int Count;
//1ms
TL0 = 0x66; //设置定时初值,根据自己的晶振来进行stc的初值生成,或者自己计算
TH0 = 0xFC; //设置定时初值
Count++;
if(Count>=500)
{
Count=0;
TimeSetFlash=!TimeSetFlash;//!逻辑取反,~按位取反
//0逻辑取反就是1,1逻辑取反就是0,
//0按位取反在八位就是0xFF,1按位取反就是0xFE
}
}
其他.c和.h文件
评论区领取,也是前几章写的头文件
效果如下:
DS1302可调时钟