一,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();
}
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐