实时时钟DS1302(SPI协议)

一,DS1302芯片


一.DS1302的主要性能指标


(1)DS1302实时时钟具有能计算2100年之前的秒、分、时、日、日期、星期、月、年的能力,还有闰年调整的能力。
(2)内部含有31个字节静态RAM,可提供用户访问。
(3)采用串行数据传送方式,使得管脚数量最少,简单SPI 3线接口。
(4)工作电压范围宽:2.0~5.5V。
(5)工作电流:2.0V时,小于300nA。
(6)时钟或RAM数据的读/写有两种传送方式:单字节传送和多字节传送方式。
(7)采用8脚DIP封装或SOIC封装。
(8)与TTL兼容,Vcc=5V。
(9)可选工业级温度范围:-40C~+85C。
(10)具有涓流充电能力。
(11)采用主电源和备份电源双电源供应。
(12)备份电源可由电池或大容量电容实现。


二.引脚功能


DS1302的引脚如图所示

在这里插入图片描述

引脚功能
X1、X232.768KHz晶振接入引脚
GND接地
RST复位引脚,低电平有效,操作时高电平
I/O数据输入/输出引脚,具有三态功能
SCLK串行时钟输入引脚
Vcc1工作电源引脚
Vcc2备用电源引脚。 接入电池断电时提供1302电源

三.DS1302的寄存器


DS1302有一个控制寄存器、12个日历、时钟寄存器和31个RAM,可读写.

在这里插入图片描述
DS1302有下列几组寄存器
① DS1302有关日历、时间的寄存器共有12个,其中有7个寄存器(读时81h~8Dh,写时80h~8Ch),存放的数据格式为BCD码形式

小时寄存器(85h、84h):位7用于定义DS1302是运行于12小时模式还是24小时模式。当为1时选12小时制,当为0时选24小时制。当12小时制时,D5位为1是上午,D5位为0是下午,D4为小时的十位。当24小时制时,D5、D4位为小时的十位。

秒寄存器(81h、80h):位7定义为时钟暂停标志(CH)。当该位置为1时,时钟振荡器停止,DS1302处于低功耗状态;当该位置为0时,时钟开始运行。

控制寄存器(8Fh、8Eh):位7是写保护位(WP),其它7位均置为0。写保护寄存器中的WP为写保护位,当WP=1,写保护,当WP=0未写保护,当对日历、时钟寄存器或片内RAM进行写时WP应清零,当对日历、时钟寄存器或片内RAM进行读时WP一般置1。

②DS1302有关RAM的地址
DS1302中附加31字节静态RAM的地址如图4所示。

在这里插入图片描述
DS1302片内有31个RAM单元,对片内RAM的操作有两种方式:单字节方式和多字节方式。当控制命令字为C0H ~ FDH时为单字节读写方式,命令字中的D5~D1用于选择对应的RAM单元,其中奇数为读操作,偶数为写操作
当控制命令字为FEH、FFH时为多字节操作(表中的RAM突发模式),多字节操作可一次把所有的RAM单元内容进行读写。FEH为写操作,FFH为读操作。

③ DS1302的工作模式寄存器
所谓突发模式是指一次传送多个字节的时钟信号和RAM数据。突发模式寄存器如图5所示。

+++++++
④此外,DS1302还有充电寄存器等。


四.数据输入输出(I/O)


在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。

在这里插入图片描述
在这里插入图片描述

DS1302是通过SPI串行总线跟单片机通信的,当进行一次读写操作时最少得读写两个字节,第一个字节是控制字节,就是一个命令,告诉DS1302是读还是写操作,是对RAM还是对CLOK寄存器操作。第二个字节就是要读或写的数据了

单字节读写:只有在SCLK为低电平时,才能将CE置为高电平。所以在进行操作之前先将SCLK置低电平,然后将CE置为高电平,接着开始在IO上面放入要传送的电平信号,然后跳变SCLK。数据在SCLK上升沿时,DS1302读写数据,在SCLK下降沿时,DS1302放置数据到IO上。

二,SPI总线概念

SPI接口的全称是“Serial Peripheral Interface”,意为串行外围接口,SPI接口主要应用在EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。 SPI接口是在CPU和外围低速器件之间进行同步串行数据传输,在主器件的移位脉冲下,数据按位传输,高位在前,地位在后,为全双工通信,数据传输速度总体来说比I2C总线要快,速度可达到几Mbps。SPI接口的一个缺点:没有指定的流控制,没有应答机制确认是否接收到数据。

三,代码实践

原理图

在这里插入图片描述

ds1302.h

#ifndef __DS1302_H__
#define __DS1302_H__

void delay(void);
void delay900ms(void);
void ds1302_write_reg(unsigned char addr, unsigned char value);
unsigned char ds1302_read_reg(unsigned char addr);
void ds1302_read_time(void);
void ds1302_write_time(void);
void debug_print_time(void);

#endif


ds1302.c

#include <reg51.h>
#include <intrins.h>
#include "uart.h"
#include "ds1302.h"

/**************  全局变量定义  *************************************/

// 定义SPI的三根引脚
sbit DSIO	= P3^4;
sbit RST	= P3^5;
sbit SCLK	= P3^6;

// 因为51单片机的设计本身RAM比较少而Flash稍微多一些,像这里定义的数组内部
// 的内容是不会变的(常量数组),我们就可以使用code关键字,让编译器帮我们
// 把这个数组放在flash中而不是ram中,这样做可以省一些ram。
unsigned char code READ_RTC_ADDR[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d}; 
unsigned char code WRITE_RTC_ADDR[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};
unsigned char time[7];		// 用来存储读取的时间的,格式是:秒分时日月周年




// 有用函数
void delay(void)
{
	unsigned char i;
	for (i=0; i<3; i++);
}


void delay900ms(void)   //误差 -0.000000000205us
{
    unsigned char a,b,c;
    for(c=127;c>0;c--)
        for(b=128;b>0;b--)
            for(a=24;a>0;a--);
}

// 向ds1302的内部寄存器addr写入一个值value
void ds1302_write_reg(unsigned char addr, unsigned char value)
{
	unsigned char i = 0;
	unsigned char dat = 0;

	// 第1部分: 时序起始
	SCLK = 0;
	delay();
	RST = 0;
	delay();
	RST = 1;  		// SCLK为低时,RST由低变高,意味着一个大的周期的开始
	delay();
	// 第2部分: 写入第1字节,addr
	for (i=0; i<8; i++)
	{
		dat = addr & 0x01;	 	// SPI是从低位开始传输的
		DSIO = dat;	 			// 把要发送的bit数据丢到IO引脚上去准备好
		SCLK = 1;		 		// 制造上升沿,让DS1302把IO上的值读走
		delay();				// 读走之后,一个小周期就完了
		SCLK = 0;				// 把SCLK拉低,是为了给下一个小周期做准备
		delay();
		addr >>= 1;	   			// 把addr右移一位
	}
	// 第3部分: 写入第2字节,value
	for (i=0; i<8; i++)
	{
		dat = value & 0x01;	 	// SPI是从低位开始传输的
		DSIO = dat;	 			// 把要发送的bit数据丢到IO引脚上去准备好
		SCLK = 1;		 		// 制造上升沿,让DS1302把IO上的值读走
		delay();				// 读走之后,一个小周期就完了
		SCLK = 0;				// 把SCLK拉低,是为了给下一个小周期做准备
		delay();
		value = value >> 1;	   	// 把addr右移一位
	}
	// 第4部分: 时序结束
	SCLK = 0;				  	// SCLK拉低为了后面的周期时初始状态是对的
	delay();
	RST = 0;					// RST拉低意味着一个大周期的结束
	delay();
}


// 从ds1302的内部寄存器addr读出一个值,作为返回值
unsigned char ds1302_read_reg(unsigned char addr)
{
	unsigned char i = 0;
	unsigned char dat = 0; 		// 用来存储读取到的一字节数据的
	unsigned char tmp = 0;

	// 第1部分: 时序起始
	SCLK = 0;
	delay();
	RST = 0;
	delay();
	RST = 1;  		// SCLK为低时,RST由低变高,意味着一个大的周期的开始
	delay();
	// 第2部分: 写入要读取的寄存器地址,addr
	for (i=0; i<8; i++)
	{
		dat = addr & 0x01;	 	// SPI是从低位开始传输的
		DSIO = dat;	 			// 把要发送的bit数据丢到IO引脚上去准备好
		SCLK = 1;		 		// 制造上升沿,让DS1302把IO上的值读走
		delay();				// 读走之后,一个小周期就完了
		SCLK = 0;				// 把SCLK拉低,是为了给下一个小周期做准备
		delay();
		addr >>= 1;	   			// 把addr右移一位
	}
	// 第3部分: 读出一字节DS1302返回给我们的值
	dat = 0;
	for (i=0; i<8; i++)
	{
	// 在前面向ds1302写入addr的最后一个bit后,ds1302就会将读取到的寄存器值
	// 的第一个bit放入到IO引脚上,所以我们应该先读取IO再制造下降沿然后继续
	// 读取下一个bit
		tmp = DSIO;
		dat |= (tmp << i);		// 读出来的数值是低位在前的
		SCLK = 1;  				// 由于上面SCLK是低,所以要先拉到高
		delay();
		SCLK = 0;				// 拉低SCLK制造一个下降沿
		delay();
	}
	// 第4部分: 时序结束
	SCLK = 0;				  	// SCLK拉低为了后面的周期时初始状态是对的
	delay();
	RST = 0;					// RST拉低意味着一个大周期的结束
	delay();

	// 第5部分:解决读取时间是ff的问题
	DSIO = 0;

	return dat;
}


void ds1302_read_time(void)
{
	unsigned char i = 0;
	for (i=0; i<7; i++)
	{
		time[i] = ds1302_read_reg(READ_RTC_ADDR[i]);
	}
}

void ds1302_write_time(void)
{
	unsigned char i = 0;

	// 准备好要写入的时间
	time[0] = 0x24;				// 对应 24s
	time[1] = 0x39;				// 对应 39m
	time[2] = 0x11;				// 对应 11h
	time[3] = 0x11;				// 对应 6日
	time[4] = 0x06;				// 对应 12月
	time[5] = 0x02;				// 对应 星期2
	time[6] = 0x20;				// 对应 2016年

	ds1302_write_reg(0x8E, 0x00);	// 去掉写保护
	for (i=0; i<7; i++)
	{
		ds1302_write_reg(WRITE_RTC_ADDR[i], time[i]);
	}
	ds1302_write_reg(0x8E, 0x80);	// 打开写保护
}




// 通过串口将7个时间以二进制方式输出在串口助手上
void debug_print_time(void)
{
	unsigned char i = 0;

	while (1)
	{
		// 1 从DS1302读取时间
		ds1302_read_time();

		// 2 for循环内打印一组7个时间
		for (i=0; i<7; i++)
		{
			uart_send_byte(time[i]);	
		}

		// 3 延时900ms后再继续下个周期
	   	delay900ms();
	}	
}

uart.h

#ifndef __UART_H__
#define __UART_H__

#include <reg51.h>

void uart_init(void);
void uart_send_byte(unsigned char c);

#endif




uart.c

#include "uart.h"


// 串口设置为: 波特率9600、数据位8、停止位1、奇偶校验无
// 使用的晶振是11.0592MHz的,注意12MHz和24MHz的不行
void uart_init(void)
{
	// 波特率9600
	SCON = 0x50;   	// 串口工作在模式1(8位串口)、允许接收
	PCON = 0x00;	// 波特率不加倍

	// 通信波特率相关的设置
	TMOD = 0x20;	// 设置T1为模式2
	TH1 = 253;
	TL1 = 253;	   	// 8位自动重装,意思就是TH1用完了之后下一个周期TL1会
					// 自动重装到TH1去

	TR1 = 1;		// 开启T1让它开始工作
	ES = 1;
	EA = 1;
}

// 通过串口发送1个字节出去
void uart_send_byte(unsigned char c)
{
   // 第1步,发送一个字节
   SBUF = c;

   // 第2步,先确认串口发送部分没有在忙
   while (!TI);

   // 第3步,软件复位TI标志位
   TI = 0;
}


main.c

#include "uart.h"
#include "ds1302.h"


void main(void)
{
	uart_init();
	ds1302_write_time();
	debug_print_time();
}
  • 8
    点赞
  • 75
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C51单片机是一种经典的8位单片机,而DS1302是一种实时时钟芯片,常用于单片机系统中。DS1302具有时钟、日历和RAM功能,可以提供准确的时间和日期信息,并且可以通过C51单片机与其他外设进行通信。 DS1302时钟芯片的主要特点包括: 1. 时钟和日历功能:DS1302可以提供准确的时间和日期信息,包括秒、分、时、日、月、年等。 2. 低功耗设计:DS1302采用低功耗设计,可以在电池供电下长时间运行。 3. 串行接口:DS1302通过串行接口与C51单片机进行通信,可以方便地读取和设置时钟和日历信息。 4. RAM功能:DS1302还具有31个字节的RAM,可以用于存储一些临时数据。 使用C51单片机与DS1302进行通信,可以实现对时钟和日历的读取和设置。具体步骤如下: 1. 初始化DS1302:设置DS1302的控制寄存器,使其进入读写模式。 2. 读取时钟和日历信息:通过串行接口从DS1302读取秒、分、时、日、月、年等信息。 3. 设置时钟和日历信息:通过串行接口向DS1302写入秒、分、时、日、月、年等信息,实现对时钟和日历的设置。 下面是一个示例代码,演示了如何使用C51单片机与DS1302进行通信: ```c #include <reg52.h> sbit DS1302_SCLK = P1^0; // DS1302的串行时钟引脚 sbit DS1302_IO = P1^1; // DS1302的串行数据引脚 sbit DS1302_RST = P1^2; // DS1302的复位引脚 // DS1302写入一个字节数据 void DS1302_WriteByte(unsigned char dat) { unsigned char i; for(i=0; i<8; i++) { DS1302_IO = dat & 0x01; // 写入最低位 DS1302_SCLK = 1; // 上升沿时钟 DS1302_SCLK = 0; // 下降沿时钟 dat >>= 1; // 右移一位 } } // DS1302读取一个字节数据 unsigned char DS1302_ReadByte() { unsigned char i, dat = 0; for(i=0; i<8; i++) { dat >>= 1; // 右移一位 if(DS1302_IO) // 读取数据 dat |= 0x80; // 最高位为1 DS1302_SCLK = 1; // 上升沿时钟 DS1302_SCLK = 0; // 下降沿时钟 } return dat; } // 初始化DS1302 void DS1302_Init() { DS1302_RST = 0; // 复位DS1302 DS1302_SCLK = 0; // 初始化时钟引脚 DS1302_RST = 1; // 结束复位 DS1302_WriteByte(0x8E); // 写入控制寄存器,使DS1302进入读写模式 DS1302_WriteByte(0x00); // 关闭写保护 } // 读取DS1302的时钟和日历信息 void DS1302_ReadTime(unsigned char *time) { unsigned char i; DS1302_WriteByte(0xBF); // 写入读命令 for(i=0; i<7; i++) { time[i] = DS1302_ReadByte(); // 读取时钟和日历信息 } } // 设置DS1302的时钟和日历信息 void DS1302_SetTime(unsigned char *time) { unsigned char i; DS1302_WriteByte(0xBE); // 写入写命令 for(i=0; i<7; i++) { DS1302_WriteByte(time[i]); // 设置时钟和日历信息 } } void main() { unsigned char time[7]; // 存储时钟和日历信息 DS1302_Init(); // 初始化DS1302 DS1302_ReadTime(time); // 读取时钟和日历信息 // 在这里可以对time数组进行处理或输出 DS1302_SetTime(time); // 设置时钟和日历信息 while(1); } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值