蓝桥杯单片机设计与开发⑪ --- DS1302

实时时钟 DS1302

一、实时时钟芯片DS1302

1、DS1302的主要性能指标
(1) DS1302实时时钟能够计算2100年之前的秒、分、时、日、日期、星期、月、年(闰年可自动调整)。 还可以通过配置AM/PM来决定采 用24小时格式还是12小时格式。
(2)内部含有31个字节静态数据存储RAM。
(3)串行I/O通信方式,使得管脚少,简单SPI仅3线接口。
(4)工作电压范围教宽:2.0~5.5V。
(5)工作电流小,功耗低:工作电压为2.0V时,小于300nA。
(6)时钟或RAM数据的读/写有两种传送方式:单字节传送和多字节传送方式。
(7)采用8脚DIP封装(双列直插式封装)或SOIC封装。
(8)供电电压Vcc=5V时,与标准TTL兼容,可与单片机通信。
(9)具有涓流充电能力。
(10)采用主电源和备份电源双电源供应。备用电源可以是电池或者大电容,确保在系统掉电的情况下,时钟还可以走。

DS1302封装图:
在这里插入图片描述
2、DS1302的硬件信息
DS1302引脚图:在这里插入图片描述
各引脚功能:

引脚编号引脚名称引脚功能
1Vcc2主电源引脚,当Vcc2比Vcc1高0. 2V以上时,DS1302由 Vcc2供电,当Vcc2低于Vcc1时,由Vcc1供电。
2X1这两个引脚需要接- -个32. 768K的晶振,给DS1302提供一个基准 。
3X2特别注意,要求这个晶振的引脚负载电容必须是6pF,而不是要加6pF的电容。如果使用有源晶振的话,接到X1上即可,X2悬空。
4GND接地即可
5CEDS1302的使能输入引脚。当读写DS1302的时候,这个引脚必须是高电平,DS1302这个引脚内部有一个40k的下拉电阻。
6I/O这个引脚是一个双向通信引脚,读写数据都是通过这个引脚完成。DS1302这个引脚的内部含有一个40k的下拉电阻。
7SCLK输入引脚。SCLK是用来作为通信的时钟信号。DS1302这个引脚.的内部含有- -个40k的下拉电阻。
8Vcc1备用电源引脚

其中只有5脚(CE使能)、6脚(I/O)、7脚(SCLK时钟)需要接到单片机的I/O口上。
DS1302的典型电路如下:
在这里插入图片描述

需要注意的是晶振电路。它使用的是32.768K的晶振,晶振外部不需要额外添加其他电容或者电阻。时钟的精度首先取决于晶振的精度以及晶振的引脚负载电容。如果晶振不准或者负载电容过大(小),就会导致时钟误差过大。其次需要考虑晶振的温漂。晶振的精度会随着温度的变化而变化。在实际系统中,常通过校对来减少这种误差的影响。

3、DS1302寄存器介绍
(1) 命令字节
DS1302一条指令一个字节共8位。第7位(最高位)固定为1(0无效);第6位是RAM/CLOCK选择端,这里主要学习CLOCK时钟的使用,所以第6位为0;第5位到第1位是寄存器的地址;第0位是读写控制位,1读0写。命令字节如下:
在这里插入图片描述
(2) 寄存器
DS1302有8个和时钟有关的寄存器,如下:
在这里插入图片描述
(表格从上到下依次为寄存器0到寄存器7)

  • 寄存器0:最高位CH是时钟停止标志位。通过该位判断时钟在单片机系统掉电后是否正常运行(0为有备用电源,正常运行)。剩余7位的高3位是秒的十位,低4位是秒的个位。由于DS1302内部是BCD码,秒的十位最大是5,所以3个二进制位足够。
  • 寄存器1:最高位未使用,剩余7位的高3位是分钟的十位,低4位是分钟的个位。
  • 寄存器2:bit7为1代表采用12小时制,为0表示24小时制;bit6固定为0;bit5在12小时制下,0表示上午,1表示下午,在24小时制下,和bit4一起表示小时的十位;低4位表示小时的个位。
  • 寄存器3:高两位固定是0,bit5 和 bit4是日期的十位,低4位是日期的个位。
  • 寄存器4:高3位固定是0,bit4 是月的十位,低4位是月的个位。
  • 寄存器5:高5位固定是0,低3位表示星期
  • 寄存器6:高4位表示年的十位,低4位表示年的个位。00 ~ 99指的是2000年~2099年。
  • 寄存器7:最高位为写保护位。如果此位为1,则禁止给任何其它的寄存器或者31个字节的RAM写数据。因此在写数据之前,此位必须写成0。

4、DS1302通信时序介绍
(1) 单字节写操作时序 (单片机向DS1302内写入)

  • 先写入寄存器地址,再写入待写字节
    在这里插入图片描述
    (2)单字节读操作时序(单片机从DS1302读取数据)
  • 先写入寄存器地址,再读取该寄存器的数据
    在这里插入图片描述

注:
①该时序图上的箭头方向都是针对DS1302而言的。
②应该先读取I/O线上的数据,再拉高SCK(时钟线)产生上升沿。

5、Burst 模式
DS1302寄存器中,与时间相关的寄存器有7个,分别依次对应秒、分、时、日期、月、星期、年。在读取这7个寄存器的过程中,无论怎样读,都会有时间差,在极端的情况下就会出现错误。例如当前时间是00:00:59,先读秒,读到的秒是59,然后再去读分钟。但是在读完秒准备去读分钟的这段时间内,刚好时间进位了,变成了00:01:59,这个错误就很明显了。

为了解决这个问题,芯片厂家提供了Burst的模式。这里主要学习时钟突发模式(RAM突发模式暂时忽略)。

当向DS1302内写入寄存器地址时,只要将写的5位地址都写1,即读操作用0xBF,写操作用0xBE,这时DS1302就能识别出是Burst模式,马上把所有的8个字节同时锁存到另外的8个字节的寄存器缓冲区内,这样时钟继续走,而我们读取的是另一个缓冲区内的数据。写数据同理,Burst模式下,我们是先把数据写到这个缓冲区内,最终DS1302会把这个缓冲区内的数据一次性送到它的时钟寄存器内。

从以上叙述不难看出,在DS1302时钟的Burst模式下,必须一次性读或者写8个字节,要把时钟的寄存器全部读出来或者完全写进去。

二、 注意:DS1302内部寄存器存储的数据类型是BCD码!!!

BCD码介绍

  • 我们时钟日历寄存器使用的是8421码型的BCD码,BCD码还有5421码、2421码等,其中8421码型的BCD码最最常用;
  • BCD码是用四位二进制数表示一位十进制数的0-9这十个数简称BCD码;
  • 8421码型BCD码最小值为0000 (二进制),最大值为1001 (二进制) : 9
  • 一个字节的8421码型BCD码中的低四位用于表示十进制的个位,高四位用于表示十进制的十位,如10 (十进制)的8421码型BCD码=00010000;

BCD码转换
例:把十进制数45转换为8421型的BCD码

unsigned char data1,data2 = 45;//声明两个无符号类型的char型变量data1和data2 并data2赋初值45
data1 = data2/10; // data1 = 4;
data2 = data2%10; // data2 = 5;
data2 = data2 + data1*16; // data2 = 5 + 4*16 = 69 即69为转换的BCD码	

把69这个8421型的BCD码换算回十进制数:

data1 = data2/16; // data1 = 4;
data2 = data2%16; // data2 = 5;
data2 = data2 + data1*10; // data2 = 5 + 4*10 = 45
三、简易时钟代码

ds1302.h

#ifndef _DS1302_H_
#define _DS1302_H_
#include <STC15F2K60S2.H>
#define u8 unsigned char 

struct sTime{
	unsigned char hour;
	unsigned char min;
	unsigned char sec;
};
sbit DS1302_IO = P2^3;
sbit DS1302_CLK = P1^7;
sbit DS1302_CE = P1^3;

void DS1302Write(unsigned char reg,unsigned char dat);
unsigned char DS1302Read(unsigned char reg);
void DS1302BurstWrite(unsigned char *dat);
void DS1302BurstRead(unsigned char *dat);
void BCD_TO_DAT(unsigned char *dat);
void DAT_TO_BCD(unsigned char *dat);
void GetRealTime(struct sTime *time);
void SetRealTime(struct sTime *time);
void DS1302Init();
#endif

ds1302.c

#include "ds1302.h"

//发送一个字节到DS1302通信总线上
void DS1302ByteWrite(u8 dat)
{    
	u8 i;
    for(i=0;i<8;i++)
    {
        DS1302_CLK = 0;
        DS1302_IO = dat & 0x01;
        DS1302_CLK = 1;
        dat>>=1;
    }
}

//从DS1302通信总线上读取一个字节
u8 DS1302ByteRead()
{
	u8 i;
	u8 dat = 0;
	for(i=0;i<8;i++)
	{
		DS1302_CLK = 0;//拉低时钟总线,写数据
		dat >>= 1;//先读的数据依次右移至最低位
		if(DS1302_IO)
		{
			dat |= 0x80;
		}
		DS1302_CLK = 1;//拉高时钟总线,让IO读走一位
	}
	return dat;
}

//向某一寄存器写入一个字节
void DS1302Write(u8 reg,u8 dat)
{
	DS1302_CLK = 0; //拉低使能端
	DS1302_CLK = 0; //拉低数据总线
	DS1302_CE = 1; //拉高使能端 产生上升沿 准备发送数据
	DS1302ByteWrite(reg);
	DS1302ByteWrite(dat);
	DS1302_CE = 0; //拉低使能端,以便下次产生上升沿
}
//从某一寄存器读取一个字节
u8 DS1302Read(u8 reg)
{
	u8 dat = 0;
	DS1302_CE = 0;
	DS1302_CLK = 0;
	DS1302_CE = 1; //产生上升沿 准备发送寄存器指令
	DS1302ByteWrite(reg);
	dat = DS1302ByteRead();
	DS1302_CE = 0;
	
	return dat;
}
//用Burst突发模式连续写入8个寄存器数据
void DS1302BurstWrite(u8 *dat)
{
	u8 i;
	DS1302_CE = 0;
	DS1302_CLK = 0;
	DS1302_CE = 1;
	DS1302ByteWrite(0xBE);
	for(i=0;i<8;i++)
	{
		DS1302ByteWrite(dat[i]);
	}
	DS1302_CE = 0;
}
//用Burst突发模式连续读取8个寄存器数据
void DS1302BurstRead(u8 *dat)
{
	u8 i;
	DS1302_CE = 0;
	DS1302_CLK = 0;
	DS1302_CE = 1;
	DS1302ByteWrite(0xBF);
	for(i=0;i<8;i++)
	{
		dat[i] = DS1302ByteRead();
	}
	DS1302_CE = 0;
}
//将BCD码转换为十进制数
void BCD_TO_DAT(u8 *dat)
{
	u8 i;
	unsigned char Temp = 0;
	for(i=0;i<sizeof(dat);i++)
	{
		 Temp = dat[i]/16;
		 dat[i] = dat[i]%16;
		 dat[i] = dat[i]+Temp*10;
	}
}
//将十进制数转换为BCD码
void DAT_TO_BCD(u8 *dat)
{
	u8 i;
	unsigned char Temp = 0;
	for(i=0;i<sizeof(dat);i++)
	{
		Temp = dat[i]/10;
		dat[i] = dat[i]%10;
		dat[i] = dat[i] + Temp*16;
	}
}

//获取时间
void GetRealTime(struct sTime *time)
{
	unsigned char buf[8];
	DS1302BurstRead(buf);
	BCD_TO_DAT(buf);
	time ->sec = buf[0];
	time ->min = buf[1];
	time ->hour = buf[2];
}
//设置时间
void SetRealTime(struct sTime *time)
{
	unsigned char buf[8];
	buf[0] = time -> sec;
	buf[1] = time -> min;
	buf[2] = time -> hour;
	DAT_TO_BCD(buf);
	DS1302BurstWrite(buf);
}
//DS1302初始化
void DS1302Init()
{
	struct sTime InitTime;
	//初始化设置 8点59分0秒
	InitTime.sec = 0;
	InitTime.min = 59;
	InitTime.hour = 8;
	DS1302Write(0x8e,0x00);//关闭写保护
	SetRealTime(&InitTime);
}

main.c

#include "sys.h"
#include "ds1302.h"
struct sTime time;
bit DS1302_READ_FLAG;
void main()
{
	ALL_Init();
	Timer0Init();
	DS1302Init();
	while(1)
	{
		DS1302Write(0x8e,0x00);//关闭写保护
		if(DS1302_READ_FLAG)//每100ms获取一次时ds1302数据
		{
			DS1302_READ_FLAG = 0;
			GetRealTime(&time);
			DS1302Write(0x8e,0x80);//打开写保护
		}
		Nixie_Drive(&time);//将ds1302数据通过数码管显示
	}
	
}

nixie.c

#include "sys.h"
#include "ds1302.h"

					// 0    1    2    3    4    5    6    7
uchar code nixie[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
				    // 8	9    a    b    c    d     e    f	u
					  0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xc1};	//共阳数码管码字

uchar NixieBuff[] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};

uchar smg1,smg2,smg3,smg4,smg5,smg6,smg7,smg8;
uchar code Symbol[] = {0xff,0xbf};	//全灭,-


void Nixie_Scan()
{
	static u8 index;
	HC138_Set(7);
	P0 = 0xff;
	HC138_Set(6);
	P0 = 0x01 << index;
	HC138_Set(7);
	P0 = NixieBuff[index];
	HC138_Set(0);
	index++;
	index &= 0x07;
}

void Nixie_Show()
{
	NixieBuff[0] = nixie[smg1];
	NixieBuff[1] = nixie[smg2];
	NixieBuff[2] = Symbol[smg3];
	NixieBuff[3] = nixie[smg4];
	NixieBuff[4] = nixie[smg5];
	NixieBuff[5] = Symbol[smg6];
	NixieBuff[6] = nixie[smg7];
	NixieBuff[7] = nixie[smg8];
}

void Nixie_Drive(struct sTime *time)
{
	smg1 = time->hour/10;
	smg2 = time->hour%10;
	smg4 = time->min/10;
	smg5 = time->min%10;
	smg7 = time->sec/10;
	smg8 = time->sec%10;
	if(time->sec%2==0)
	{
		smg3 = smg6 = 1;
	}else
	{
		smg3 = smg6 = 0;
	}
}

注意:
1、本程序采用了结构体、指针,让程序显得更为复杂,不采用也一样能实现该功能,代码也会更少。
2、 本程序是直接用的burst模式一次性读写7个寄存器

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值