普中51单片机教程-9.DS1302

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可调时钟

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值