51单片机个人学习笔记9(DS1302实时时钟)

前言

本篇文章属于STC89C52单片机(以下简称单片机)的学习笔记,来源于B站教学视频。下面是这位up主的视频链接。本文为个人学习笔记,只能做参考,细节方面建议观看视频,肯定受益匪浅。

[1-1] 课程简介_哔哩哔哩_bilibili

一、DS1302介绍

单片机定时器计时较DS1302的缺点:

1.精度不高

2.占用单片机CPU时间

3.不能掉电继续运行

二、引脚定义和应用电路 

 

注意:VCC2才是主电源,VCC1是备用电池

由于该开发板没有接VCC1备用电池,不具备掉电时间继续前进的能力 

VCC2、VCC1、GND、X1、X2提供技术脉冲,芯片可以工作了

CE、IO、SCLK是通信端,用来设置时间,读取时间,通过这三个端口,把芯片内部的时间读出来

,也可以写入时间,其操作方式与前面讲的74HC595相似,通过一定的通信协议来操控

三、内部结构框图

CE使能起到一个开关的作用,当CE接高电平时,下面两个端口的操作才是有用的 ,如果是低电平,就无效

四、寄存器定义

 WP是writing protection 写入保护,当它为高电平时,无法写入,但可以读取

该图是命令字,用来操作在哪          读出(写入)         什么

字母上有横线代表低电平有效

7位默认为1,6位来选择操作RAM寄存器还是CK时钟,5-1位来选择操作的地址,0位来选择是读出还是写入

五、时序定义

SCLK时钟的上升沿写入数据,时钟的下降沿读出数据

写入数据: 先将命令字写入,第一步先将CE端置1,第二步I/O写入数据,先写最低位,最低位给0即是写入,第三步给时钟SCLK一个上升沿,即给1,I/O的数据就会进去,第四步将SCLK时钟一个下降沿,给回0,第五步,I/O口写入次低位,第六步给时钟SCLK1。按此步骤重复下去,命令字写完后,紧接着就可以用相似的操作写入要写的数据,同样是低位先写。

全部操作完后,再把CE置0,整个时序就完成了

读出数据: 前面操作与写入数据相同,先把命令字写入。写完命令字后,单片机不再操作I/O,只需给SCLK下降沿来读取数据即可。最后再把CE置0。

六、实例一(DS1302时钟)

注意:

1.时钟的上升沿和下降沿之间是否需要延时,要看手册的时间符不符合

2.Keil C51函数中的局部变量必须要在函数体的开头定义,否则会出现未定义的error

BCD码

即前4位表示十位上的数,后4位表示个位上的数

注意的是:写操作时候,要将十进制转换为BCD码,然后写入到DS1302中

                   读操作时候,将读取的BCD码转换为十进制数字

代码实例:

DS1302.h

#ifndef __DS1302_H__
#define __DS1302_H__

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,6,17,16,59,55,1};

//DS1302时钟初始化
void DS1302_Init(void)
{
	DS1302_CE=0;
	DS1302_SCLK=0;
}

//DS1302时钟写入一个字节
void DS1302_WriteByte(unsigned char Command,Data)
{
    //Keil C51函数中的局部变量必须要在函数体的开头定义,否则会出现未定义的error
	unsigned char i;
    //使能置1
	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;
	}
    //数据传输结束,使能置0
	DS1302_CE=0;
}

//DS1302时钟读出一个字节
unsigned char DS1302_ReadByte(unsigned char Command)
{
	unsigned char i,Data=0x00;
    //读出时最低位为1
	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;
    //读完后IO口要置0,不然会出错
	DS1302_IO=0;
	return Data;
}

//设置时间
void DS1302_SetTime(void)
{
    //将数组存储的日期写入DS1302时钟,写入要由十进制转为BCD码
	DS1302_WriteByte(DS1302_WP,0x00);//关闭写入保护
	DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);
	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);//打开写入保护
}

//读取时间
void DS1302_ReadTime(void)
{
    //将读出的时间放进数组中,读出要由BCD码转为十进制
	unsigned char Temp;
	Temp=DS1302_ReadByte(DS1302_YEAR);
	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;
}

main.c

#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"


void main()
{
	LCD_Init();    //LCD液晶屏初始化
	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时钟)

主要是在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()
{
	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()
{
    //按键2选择调节的时间位
	if (KeyNum==2)
	{
		TimeSetSelect++;
		TimeSetSelect%=6;
	}
    //按键3控制时间位的加,其中要有越界判断
	if (KeyNum==3)
	{
		DS1302_Time[TimeSetSelect]++;
		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;}
			}
			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;}
	}
    //按键4控制时间位的减,其中也要有越界判断
	if (KeyNum==4)
	{
		DS1302_Time[TimeSetSelect]--;
		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;}
				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;}
		
	}
    //设置时间位1s为周期闪烁
	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();
        //按键1切换模式
		if(KeyNum==1)
		{
			if(MODE==0){MODE=1;TimeSetSelect=0;}
			else if(MODE==1){MODE=0;DS1302_SetTime();}
		}
        //根据变量MODE的值来切换模式,进入到不同的函数中
		switch(MODE)
		{
			case 0:TimeShow();break;
			case 1:TimeSet();break;
		}

	}
}

//定时器中断系统
void Timer0_Rountine() interrupt 1
{
	static unsigned int T0Count;
	TH0=0xFC;   	    
	TL0=0x66;		   
	T0Count++;
	if (T0Count==500)       
	{
		T0Count=0;
		TimeSetFlashFlag=!TimeSetFlashFlag;
	}
}

  • 14
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值