DS1302时钟(实现时钟案例)


一、DS1302介绍

在这里插入图片描述

引脚定义与应用电路

在这里插入图片描述

官方电路图

在这里插入图片描述

内部结构框图

在这里插入图片描述

内部与时钟相关的寄存器

在这里插入图片描述
地址/命令 字节作用
5、4、3、2、1用于确定要读写的地址

时序图

在这里插入图片描述

二、具体操作

1.实现写入、读出功能

按照上面的寄存器与时序图一一配置,具体实现如下:

#include <REGX52.H>

// 初始化命名,方便操作
sbit DS1302_CE = P3^5;
sbit DS1302_IO = P3^4;
sbit DS1302_SCLK = P3^6;
// 读取数据
void DS1302_Init(){
	DS1302_CE = 0;
	DS1302_IO = 0;
	DS1302_SCLK = 0;	
}
// 写入数据
void DS1302_Write(unsigned char SET,Data){
	unsigned char i = 0;
	DS1302_CE = 1;
	for(i=0;i<8;i++){
		// 位运算结果非0即1
		DS1302_IO = SET & (0x01<<i);
		DS1302_SCLK = 1;
		DS1302_SCLK = 0;
	}
	for(i=0;i<8;i++){
		// 位运算结果非0即1
		DS1302_IO = Data & (0x01<<i);
		DS1302_SCLK = 1;
		DS1302_SCLK = 0;
	}
	DS1302_CE = 0;	
}
// 读取数据,比写入数据少一个脉冲
unsigned char DS1302_Read(unsigned char SET){
	unsigned char i = 0;
	unsigned char Data = 0x00;	// 局部变量需自己赋初值
	DS1302_CE = 1;
	for(i=0;i<8;i++){
		// 位运算结果非0即1
		DS1302_IO = SET & (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); // 得到I/O的值
		}
	}
	DS1302_IO = 0;	// 消影
	DS1302_CE = 0;
	return Data;
}

2.显示数据

通过LCD1602显示,以操作秒为例,具体如下:

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

unsigned char Data;
void main(){
	LCD_Init();
	DS1302_Init();
	LCD_ShowString(1,1,"CLK");
	DS1302_Write(0x8e,0x00);	// 需解除写保护(WP)
	DS1302_Write(0X80,0X03);	// 写入数据
	while(1){		
		Data = DS1302_Read(0X81);
		LCD_ShowNum(2,1,Data/16*10+Data%16,3);	// 转换BCD码
	}
}

3.BCD码

注意
时钟内部是使用BCD码来存储的
BCD码
即把十六进制与二进制的转换规则移到十进制与二进制上
在这里插入图片描述

三、可操作时钟案例

时钟相关内容DS1302.c

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

// 宏定义寄存器相关地址,方便使用
#define DS1302_SEC		0X80
#define DS1302_MIN		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

// 初始化命名,方便操作
sbit DS1302_CE = P3^5;
sbit DS1302_IO = P3^4;
sbit DS1302_SCLK = P3^6;

// 设置时间数组,存储要设置的时间(年月日时分秒星期)
char DS1302_Content[] = {
	20,10,27,5,20,30,6
};

// 读取数据
void DS1302_Init(){
	DS1302_CE = 0;
	DS1302_IO = 0;
	DS1302_SCLK = 0;	
}
// 写入数据
void DS1302_Write(unsigned char SET,Data){
	unsigned char i = 0;
	DS1302_CE = 1;
	for(i=0;i<8;i++){
		// 位运算结果非0即1
		DS1302_IO = SET & (0x01<<i);
		DS1302_SCLK = 1;
		DS1302_SCLK = 0;
	}
	for(i=0;i<8;i++){
		// 位运算结果非0即1
		DS1302_IO = Data & (0x01<<i);
		DS1302_SCLK = 1;
		DS1302_SCLK = 0;
	}
	DS1302_CE = 0;	
}
// 读取数据,比写入数据少一个脉冲
unsigned char DS1302_Read(unsigned char SET){
	unsigned char i = 0;
	unsigned char Data = 0x00;
	SET |= 0x01;	// 将最低位默认为1,即读取状态
	DS1302_CE = 1;
	for(i=0;i<8;i++){
		// 位运算结果非0即1
		DS1302_IO = SET & (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); // 得到I/O的值
		}
	}
	DS1302_IO = 0;	// 消影
	DS1302_CE = 0;
	return Data;
}
// 设置时间,修改时间数组
void DS1302_SetTime(void){
	DS1302_Write(DS1302_WP,0x00); // 关闭写保护
	// 设置年(十进制转BCD码)
	DS1302_Write(DS1302_YEAR,DS1302_Content[0]/10*16+DS1302_Content[0]%10);
	// 设置月(十进制转BCD码)
	DS1302_Write(DS1302_MONTH,DS1302_Content[1]/10*16+DS1302_Content[1]%10);
	// 设置日(十进制转BCD码)
	DS1302_Write(DS1302_DATE,DS1302_Content[2]/10*16+DS1302_Content[2]%10);
	// 设置时(十进制转BCD码)
	DS1302_Write(DS1302_HOUR,DS1302_Content[3]/10*16+DS1302_Content[3]%10);
	// 设置分(十进制转BCD码)
	DS1302_Write(DS1302_MIN,DS1302_Content[4]/10*16+DS1302_Content[4]%10);
	// 设置秒(十进制转BCD码)
	DS1302_Write(DS1302_SEC,DS1302_Content[5]/10*16+DS1302_Content[5]%10);
	// 设置星期(十进制转BCD码)
	DS1302_Write(DS1302_DAY,DS1302_Content[6]/10*16+DS1302_Content[6]%10);
	DS1302_Write(DS1302_WP,0x00); // 关闭写保护
}

// 确认读取时间,更新时间数组
void DS1302_ReadTime(void){
	unsigned char Temp;	   // 用于BCD码转十进制
	Temp = 	DS1302_Read(DS1302_YEAR);
	DS1302_Content[0] = Temp/16*10+Temp%16;
	Temp = 	DS1302_Read(DS1302_MONTH);
	DS1302_Content[1] = Temp/16*10+Temp%16;
	Temp = 	DS1302_Read(DS1302_DATE);
	DS1302_Content[2] = Temp/16*10+Temp%16;
	Temp = 	DS1302_Read(DS1302_HOUR);
	DS1302_Content[3] = Temp/16*10+Temp%16;
	Temp = 	DS1302_Read(DS1302_MIN);
	DS1302_Content[4] = Temp/16*10+Temp%16;
	Temp = 	DS1302_Read(DS1302_SEC);
	DS1302_Content[5] = Temp/16*10+Temp%16;
	Temp = 	DS1302_Read(DS1302_DAY);
	DS1302_Content[6] = Temp/16*10+Temp%16;		
}

主函数内容

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

unsigned char KeyNum,Mode; 		// 用Mode确定是设置状态还是非设置状态
unsigned char TimeSelectNum;	// 用于选择位
unsigned char FlashFlag;		// 闪烁标志
void Update_Show(void){
	// 更新显示内容
	LCD_ShowNum(1,1,DS1302_Content[0],2);
	LCD_ShowNum(1,4,DS1302_Content[1],2);
	LCD_ShowNum(1,7,DS1302_Content[2],2);
	LCD_ShowNum(2,1,DS1302_Content[3],2);
	LCD_ShowNum(2,4,DS1302_Content[4],2);
	LCD_ShowNum(2,7,DS1302_Content[5],2);
}
// 设置完成
void Time_Success(void){
	DS1302_ReadTime();
	LCD_ShowNum(1,1,DS1302_Content[0],2);
	LCD_ShowNum(1,4,DS1302_Content[1],2);
	LCD_ShowNum(1,7,DS1302_Content[2],2);
	LCD_ShowNum(2,1,DS1302_Content[3],2);
	LCD_ShowNum(2,4,DS1302_Content[4],2);
	LCD_ShowNum(2,7,DS1302_Content[5],2);	
}
// 设置时间
void Time_Set(void){
	// 用于判断要操作第几位
	if(KeyNum == 2){
		TimeSelectNum++;
		TimeSelectNum%=6;	// 大于5清0
	}
	
	// 用于加法操作
	if(KeyNum == 3){
	   DS1302_Content[TimeSelectNum]++;
	   // 对年判断
	   if(DS1302_Content[0]>99){DS1302_Content[0]=0;}
	   // 对月判断
	   if(DS1302_Content[1]>12){DS1302_Content[1]=1;}

	   // 对日判断
	   // 31天
	   if(	DS1302_Content[1] == 1 || DS1302_Content[1] == 3 || DS1302_Content[1] == 5 ||
	   		DS1302_Content[1] == 7 || DS1302_Content[1] == 8 || DS1302_Content[1] == 10 ||
			DS1302_Content[1] == 12){
			if(DS1302_Content[2]>31){DS1302_Content[2]=1;}
		}
		// 30天
		else if(DS1302_Content[1] == 4 ||DS1302_Content[1] == 6 ||
				DS1302_Content[1] == 9 || DS1302_Content[1] == 11){
			if(DS1302_Content[2]>30){DS1302_Content[2]=1;}
		}
		// 2月
		else if(DS1302_Content[1] == 2){
			// 闰年
			if(	DS1302_Content[0]%4==0 && DS1302_Content[0]%100!=0 ||
				DS1302_Content[0]%400 == 0){
				if(DS1302_Content[2]>29){DS1302_Content[2]=1;}	
			}
			else{
				if(DS1302_Content[2]>28){DS1302_Content[2]=1;}
			}
		}
	   
	   // 对时判断
	   if(DS1302_Content[3]>23){DS1302_Content[3]=0;}
	   // 对分判断
	   if(DS1302_Content[4]>59){DS1302_Content[4]=0;}
	   // 对秒判断
	   if(DS1302_Content[5]>59){DS1302_Content[5]=0;}
	   // Update_Show();
	}

	// 用于减法操作
	if(KeyNum == 4){
	   DS1302_Content[TimeSelectNum]--;
	   // 对年判断
	   if(DS1302_Content[0]<0){DS1302_Content[0]=99;}
	   // 对月判断
	   if(DS1302_Content[1]<1){
	   		DS1302_Content[1]=12;
	   }
	   // 对日判断
	   // 31天
	   if(	DS1302_Content[1] == 1 || DS1302_Content[1] == 3 || DS1302_Content[1] == 5 ||
	   		DS1302_Content[1] == 7 || DS1302_Content[1] == 8 || DS1302_Content[1] == 10 ||
			DS1302_Content[1] == 12){
			if(DS1302_Content[2]<1){DS1302_Content[2]=31;}
			if(DS1302_Content[2]>31){DS1302_Content[2]=1;}
		}
		// 30天
		else if(DS1302_Content[1] == 4 ||DS1302_Content[1] == 6 ||
				DS1302_Content[1] == 9 || DS1302_Content[1] == 11){
			if(DS1302_Content[2]<1){
				DS1302_Content[2]=30;
			}
			if(DS1302_Content[2]>30){DS1302_Content[2]=1;}
		}
		// 2月
		else if(DS1302_Content[1] == 2){
			// 闰年
			if(	DS1302_Content[0]%4==0 && DS1302_Content[0]%100!=0 ||
				DS1302_Content[0]%400 == 0){
				if(DS1302_Content[2]<1){DS1302_Content[2]=29;}
				if(DS1302_Content[2]>29){DS1302_Content[2]=1;}	
			}
			else{
				if(DS1302_Content[2]<1){DS1302_Content[2]=28;}
				if(DS1302_Content[2]>28){DS1302_Content[2]=1;}
			}
		}
	   
	   // 对时判断
	   if(DS1302_Content[3]<0){DS1302_Content[3]=23;}
	   // 对分判断
	   if(DS1302_Content[4]<0){DS1302_Content[4]=59;}
	   // 对秒判断
	   if(DS1302_Content[5]<0){DS1302_Content[5]=59;}
	   // Update_Show();
	}
	
	// 闪烁实现
	if(TimeSelectNum == 0 && FlashFlag == 1){
		LCD_ShowString(1,1,"  ");		
	}else{
		LCD_ShowNum(1,1,DS1302_Content[0],2);
	}
	if(TimeSelectNum == 1 && FlashFlag == 1){
		LCD_ShowString(1,4,"  ");		
	}else{
		LCD_ShowNum(1,4,DS1302_Content[1],2);
	}
	if(TimeSelectNum == 2 && FlashFlag == 1){
		LCD_ShowString(1,7,"  ");		
	}else{
		LCD_ShowNum(1,7,DS1302_Content[2],2);
	}
	if(TimeSelectNum == 3 && FlashFlag == 1){
		LCD_ShowString(2,1,"  ");		
	}else{
		LCD_ShowNum(2,1,DS1302_Content[3],2);
	}
	if(TimeSelectNum == 4 && FlashFlag == 1){
		LCD_ShowString(2,4,"  ");		
	}else{
		LCD_ShowNum(2,4,DS1302_Content[4],2);
	}
	if(TimeSelectNum == 5 && FlashFlag == 1){
		LCD_ShowString(2,7,"  ");		
	}else{
		LCD_ShowNum(2,7,DS1302_Content[5],2);
	}
	// LCD_ShowNum(1,10,TimeSelectNum,1);
	
}

void main(){
	LCD_Init();
	DS1302_Init();
	Time0_Init();
	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();	// 模式转换回来时更新时间数组
			}
		}
		switch(Mode){
			case 0: Time_Success();break;
			case 1: Time_Set();break;
		}		
		
	}
}

// 中断函数
void Timer0_Routine() interrupt 1
{	
	static unsigned int count;
	count++;
	// 中断后会归零,需重新赋值
	TL0 = 0x18;				//设置定时初始值
	TH0 = 0xFC;				//设置定时初始值
	// 每500ms改变闪烁状态
	if(count>=500){
	   count = 0;
	   FlashFlag = !FlashFlag;
	}	
}






注意内容

  • 如果keil没有破解,会有代码限制问题*** FATAL ERROR L250: CODE SIZE LIMIT IN RESTRICTED VERSION EXCEEDED,破解后重新创建项目即可。
  • 在使用LCD1602时,要解除之前LED点阵屏OE引脚的连接(即连接VCC)
  • 关于 keil编译时出现大量莫名其妙的 undefined identifier,这个错误是因为在C语言的旧版本中(尤其是在Keil编译器中编译8051微控制器的代码时),变量声明必须在函数或任何作用域的开头,而不能在语句之后。
  • 13
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值