實時時鐘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、X2 32.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();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章