一,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)可選工業級溫度範圍:-40C~+85C。
(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();
}