概述
在許多系統(tǒng)當(dāng)中都需要精確的時(shí)鐘功能,因此時(shí)鐘芯片孕育而生。其中美國(guó)達(dá)拉斯 DALLAS 公司設(shè)計(jì)的 DS1302 是一款非常流行的數(shù)字時(shí)鐘芯片。DS1302 是一款具有涓細(xì)電流充電能力的低功耗實(shí)時(shí)時(shí)鐘芯片。它可以對(duì)年、月、日、星期、時(shí)、分、秒進(jìn)行計(jì)時(shí),并且具有閏年功能。年計(jì)數(shù)可達(dá)到 2100 年。
15.1 DS1302 功能簡(jiǎn)介
DS1302 內(nèi)部包含 31 字節(jié)的通用 RAM,實(shí)現(xiàn)設(shè)置備用電池功能。采用 3 線制的串行數(shù)據(jù)通信接口,并且適用于大多數(shù)的微處理器。工作電壓范圍達(dá)到 2~5V,與 5VTTL 電平完全兼容。支持單字節(jié)或多字節(jié)時(shí)鐘、RAM 數(shù)據(jù)讀、寫操作。當(dāng)工作電壓為 2V 時(shí),工作電流低至 320nA。工業(yè)級(jí) DS1302 正常工作溫度范圍為:-40℃ ~85℃,芯片包括直插和貼片兩種封裝模式,封裝示意圖如下所示:
DS1302 典型通信電路如下圖所示:
如上圖所示,只需 3 根線 CE、I/O、SCLK 便可實(shí)現(xiàn)處理器與 DS1302 之間通信,上圖中 X1,X2 之間為外接時(shí)鐘晶振,VCC2 為電源供電端,VCC1 為備用電池端。各管腳定義及功能如下所示:
15.2 單字節(jié)操作模式
與 DS1302 進(jìn)行數(shù)據(jù)通信時(shí),首先得向 DS1302 傳輸一個(gè)字節(jié)的控制指令,控制指令位定義如下所示。
字節(jié)的最高位 bit7 必須為 1,否則無法向 DS1302 寫入數(shù)據(jù)。Bit6 為 1 時(shí),表示后續(xù)對(duì) 31 字節(jié)的 RAM 進(jìn)行讀寫操作,為 0 時(shí),表示后續(xù)將對(duì)時(shí)鐘寄存器進(jìn)行讀寫操作。Bit5~bit1 為后續(xù)操作的時(shí)鐘寄存器或 RAM 的地址。最低位 bit0 為 1 時(shí),表示讀取 DS1302 的數(shù)據(jù),為 0 時(shí),表示向 DS1302 寫入數(shù)據(jù)。對(duì) DS1302 進(jìn)行單字節(jié)模式的讀、寫操作時(shí)序如下圖所示。
單字節(jié)寫操作(Single-Byte Write)時(shí)序?yàn)槭紫认?DS1302 寫入控制指令,緊接著寫入一個(gè)字節(jié)的數(shù)據(jù)。寫入的順序?yàn)榈臀辉谇?,高位在后的傳輸方式,要求在時(shí)鐘上升沿準(zhǔn)備好數(shù)據(jù)。單字節(jié)讀操作(Single-Byte Read)時(shí)序?yàn)槭紫认?DS1302 寫入控制指令,緊接著 SCLK 的的下降沿 DS1302 有數(shù)據(jù) D0 輸出,因此在接下來的上升沿前讀取穩(wěn)定的 D0 值,依次類推至 D7。根據(jù)上圖時(shí)序要求,往 DS1302 寫入一個(gè)字節(jié)和讀取一個(gè)字節(jié)的函數(shù)如下所示:
//寫字節(jié)
void WrByte_1302(uchar dat)
{
uchar j;
bit flag;
for(j=1;j<=8;j++)
{ //從低到高依次將1Byte數(shù)據(jù)寫入DS1302
flag = dat&0x01;
IO_1302 = flag;//將要寫的位放到總線
SCLK_1302 = 0;
SCLK_1302 = 1;//產(chǎn)生一個(gè)上升沿,完成1位數(shù)據(jù)寫入
dat=dat >>1;//將數(shù)據(jù)移到下一位
}
}
//讀字節(jié)
uchar RdByte_1302(void)
{
uchar dat,flag,j;
for(j=1;j<=8;j++)
{
SCLK_1302 = 1;//產(chǎn)生一個(gè)下降沿
SCLK_1302 = 0;
flag = IO_1302;//讀取DS1302發(fā)出的一位數(shù)據(jù)
dat=(dat >>1)|(flag< 7);//讀出的值最低位在前面
}
return dat;
}
如上所示,寫字節(jié)函數(shù) WrByte_1302()中,要求單片機(jī)在時(shí)鐘 SCLK_1302 上升沿前將數(shù)據(jù)放到數(shù)據(jù)總線 IO_1302 上,然后產(chǎn)生一個(gè) SCLK_1302 上升,完成一 bit 數(shù)據(jù)的寫入,同時(shí)要求 1Byte 的數(shù)據(jù)低位在前,高位在后依次發(fā)送。讀字節(jié)函數(shù) RdByte_1302()中,要求在時(shí)鐘 SCLK_1302 下降沿之后,將總線 IO_1302 數(shù)據(jù)讀出,同時(shí)要求 1Byte 的數(shù)據(jù)低位在前,高位在后依次讀取。
在上述函數(shù)的基礎(chǔ)上,單字節(jié)操作模式的讀、寫函數(shù)如下所示:
//單字節(jié)寫模式
void WrSingle_1302(uchar addr,uchar dat)
{
CE_1302 = 1;//拉高片選
WrByte_1302(addr);//寫入地址及控制指令
WrByte_1302(dat);//寫入數(shù)據(jù)
CE_1302 = 0;//拉低片選
SCLK_1302 = 0;//釋放始終總線,滿足下次操作時(shí)序要求(非常重要)
}
//單字節(jié)讀模式
uchar RdSingle_1302(uchar addr)
{
uchar dat;
CE_1302 = 1;//拉高片選
WrByte_1302(addr);//寫入地址及控制指令
dat = RdByte_1302();//讀取一個(gè)字節(jié)數(shù)據(jù)
CE_1302 = 0;//拉低片選
return dat;
}
我們這里重點(diǎn)講述 DS1302 的時(shí)鐘功能,因此與涓流充電有關(guān)的 31 字節(jié) RAM 操作這里不做詳細(xì)的介紹。與實(shí)時(shí)時(shí)鐘有關(guān)的寄存器如圖所示。
與時(shí)鐘有關(guān)的寄存器總共有 9 個(gè)如上圖所示,前 7 個(gè)分別為:秒、分、時(shí)、日、月、星期、年,均為 8 位寄存器。以秒寄存器為例介紹時(shí)間的表示法,其中 bit6-bit4 為秒的十位,bit3-bit0 為秒的個(gè)位。59 秒時(shí),bit6-bit4=“101”,bit3-bit0=“1001”,其它依此類推。另外,設(shè)置“時(shí)”寄存器的 bit7 可以設(shè)置為 12 小時(shí)或 24 小時(shí)制。上述寄存器的讀寫控制指令字節(jié)分別如圖左側(cè)兩列所示。
秒寄存器的 CH(bit7)定義為時(shí)鐘運(yùn)行標(biāo)志位,當(dāng)該位被設(shè)置成 1 時(shí),時(shí)鐘計(jì)時(shí)停止,并且 DS1302 進(jìn)入低功耗模式。當(dāng)設(shè)置為 0 時(shí),啟動(dòng)計(jì)時(shí)。上電初始狀態(tài)時(shí),該位狀態(tài)不定,因此在時(shí)鐘初始化時(shí)確保該位被清 0,保證后續(xù)時(shí)鐘芯片正常運(yùn)行。
第 8 個(gè)寄存器為控制寄存器,WP(bit7)為寫保護(hù)位,當(dāng)設(shè)置為 1 時(shí),無法向 DS1302 寫入數(shù)據(jù),上電時(shí)該位狀態(tài)不定,因此,需要對(duì) DS1302 其它寄存器進(jìn)行寫操作之前,務(wù)必先將 WP 設(shè)置為 0。第 9 個(gè)寄存器不影響實(shí)現(xiàn)時(shí)鐘功能,暫不做介紹。
因此,在 DS 1302 應(yīng)用時(shí)要對(duì)它進(jìn)行初始化,首先解除寫保護(hù),然后將與時(shí)間有關(guān)的 7 個(gè)寄存器賦初值,將初始化的內(nèi)容放到函數(shù) Init_1302(Uchar *SetTime)中。另外,我們將從 DS1302 讀取 7 個(gè)時(shí)間值的操作放到函數(shù) GetTime(*CurrentTime)中,如下代碼所示。
//1302初始化
void Init_1302(uchar *SetTime)
{
uchar j;
CE_1302 = 0;//初始化通信引腳
SCLK_1302 = 0;
WrSingle_1302(0x8E,0x00);//解除寫保護(hù)(WP=0)
for(j=0;j<=6;j++)
{
WrSingle_1302(0x80+2*j,SetTime[j]);//寫入7個(gè)時(shí)鐘數(shù)據(jù)
}
//WrBurst_1302(SetTime);//當(dāng)采用Burst模式時(shí),使用此語句替代上面for循環(huán)語句
}
//獲取當(dāng)前時(shí)間值
void GetTime(uchar *CurrentTime)
{
uchar j;
CE_1302 = 0;//初始化通信引腳
SCLK_1302 = 0;
for(j=0;j<=6;j++)
{
*CurrentTime = RdSingle_1302(0x81+2*j);//讀取7個(gè)時(shí)鐘數(shù)據(jù)
CurrentTime++;
}
//RdBurst_1302(CurrentTime); //當(dāng)采用Burst模式時(shí),使用此語句替代上面for循環(huán)語句
}
到目前為止,我們已經(jīng)學(xué)習(xí)了時(shí)鐘芯片 DS1302 的功能介紹,以及初始化和時(shí)鐘獲取函數(shù)的編寫,RY-51 單片機(jī)開發(fā)板上 DS1302 電路原理圖如下所示,三根通信線分別接 4.7K 上拉電阻,分別與單片機(jī)的 I/O 口相連接。
按照慣例我們將和 DS1302 有關(guān)的函數(shù)打包放入"Drive_DS1302.h","Drive_DS1302.c"中,方便后續(xù)調(diào)用及移植。"Drive_DS1302.h"代碼如下:
#ifndef __DS1302_H__
#define __DS1302_H__
//1302初始化
extern void Init_1302(unsigned char *SetTime);
//獲取時(shí)間
extern void GetTime(unsigned char *CurrentTime);
//單字節(jié)模式寫
void WrSingle_1302(unsigned char addr,unsigned char dat);
//單字節(jié)模式讀
unsigned char RdSingle_1302(unsigned char addr);
//突發(fā)模式寫
void WrBurst_1302(unsigned char *SetTime);
//突發(fā)模式讀
void RdBurst_1302(unsigned char *CurrentTime);
#endif
"Drive_DS1302.c"代碼如下:
#include< reg52.h >
#include"Drive_DS1302.h"
#define uchar unsigned char
#define uint unsigned int
sbit CE_1302 = P1^2; //DS1302通信引腳CE,I/O,SCLK定義
sbit IO_1302 = P1^1;
sbit SCLK_1302 = P1^0;
//寫字節(jié)
void WrByte_1302(uchar dat)
{
uchar j;
bit flag;
for(j=1;j<=8;j++)
{ //從低到高依次將1Byte數(shù)據(jù)寫入DS1302
flag = dat&0x01;
IO_1302 = flag;//將要寫的位放到總線
SCLK_1302 = 0;
SCLK_1302 = 1;//產(chǎn)生一個(gè)上升沿,完成1位數(shù)據(jù)寫入
dat=dat >>1;//將數(shù)據(jù)移到下一位
}
}
//讀字節(jié)
uchar RdByte_1302(void)
{
uchar dat,flag,j;
for(j=1;j<=8;j++)
{
SCLK_1302 = 1;//產(chǎn)生一個(gè)下降沿
SCLK_1302 = 0;
flag = IO_1302;//讀取DS1302發(fā)出的一位數(shù)據(jù)
dat=(dat >>1)|(flag< 7);//讀出的值最低位在前面
}
return dat;
}
//單字節(jié)寫模式
void WrSingle_1302(uchar addr,uchar dat)
{
CE_1302 = 1;//拉高片選
WrByte_1302(addr);//寫入地址及控制指令
WrByte_1302(dat);//寫入數(shù)據(jù)
CE_1302 = 0;//拉低片選
SCLK_1302 = 0;//釋放始終總線,滿足下次操作時(shí)序要求(非常重要)
}
//單字節(jié)讀模式
uchar RdSingle_1302(uchar addr)
{
uchar dat;
CE_1302 = 1;//拉高片選
WrByte_1302(addr);//寫入地址及控制指令
dat = RdByte_1302();//讀取一個(gè)字節(jié)數(shù)據(jù)
CE_1302 = 0;//拉低片選
return dat;
}
//突發(fā)寫模式
void WrBurst_1302(uchar *SetTime)
{
uchar j;
CE_1302 = 1;//拉高片選
WrByte_1302(0xBE);//Burst模式寫專用指令
for(j=0;j<=6;j++)
{
WrByte_1302(SetTime[j]);//寫入7位時(shí)鐘數(shù)據(jù)
}
CE_1302 = 0;//拉低片選
}
//突發(fā)讀模式
void RdBurst_1302(uchar *CurrentTime)
{
uchar j;
CE_1302 = 1;//拉高片選
WrByte_1302(0xBF);//Burst模式讀專用指令
for(j=0;j<=6;j++)
{
*CurrentTime = RdByte_1302();//讀取一個(gè)字節(jié)數(shù)據(jù);
CurrentTime++;
}
CE_1302 = 0;//拉低片選
}
//1302初始化
void Init_1302(uchar *SetTime)
{
uchar j;
CE_1302 = 0;//初始化通信引腳
SCLK_1302 = 0;
WrSingle_1302(0x8E,0x00);//解除寫保護(hù)(WP=0)
for(j=0;j<=6;j++)
{
WrSingle_1302(0x80+2*j,SetTime[j]);//寫入7個(gè)時(shí)鐘數(shù)據(jù)
}
//WrBurst_1302(SetTime);//當(dāng)采用Burst模式時(shí),使用此語句替代上面for循環(huán)語句
}
//獲取當(dāng)前時(shí)間值
void GetTime(uchar *CurrentTime)
{
uchar j;
CE_1302 = 0;//初始化通信引腳
SCLK_1302 = 0;
for(j=0;j<=6;j++)
{
*CurrentTime = RdSingle_1302(0x81+2*j);//讀取7個(gè)時(shí)鐘數(shù)據(jù)
CurrentTime++;
}
//RdBurst_1302(CurrentTime); //當(dāng)采用Burst模式時(shí),使用此語句替代上面for循環(huán)語句
}
下面我們利用上面編寫的函數(shù)以及學(xué)習(xí)的單片機(jī)的知識(shí),開始編寫一個(gè)小的時(shí)鐘顯示綜合應(yīng)用程序。程序的功能為:上電時(shí)由單片機(jī)對(duì) DS1302 進(jìn)行初始化,設(shè)置時(shí)間為 2017 年、星期日、12 月 31 日、23 時(shí) 58 分 56 秒,初始化完成后,每隔 500ms 獲取 DS1302 的時(shí)間,并將時(shí)間顯示到 1602 液晶顯示器上,主函數(shù)程序如下所示:
#include< reg52.h >
#include"Drive_1602.h"
#include"Drive_DS1302.h"
#define uchar unsigned char
#define uint unsigned int
#define FOSC 11059200 //單片機(jī)晶振頻率
#define T_1ms (65536 - FOSC/12/1000) //定時(shí)器初始值計(jì)算
sbit DU = P2^7;//數(shù)碼管段選、位選引腳定義
sbit WE = P2^6;
sbit DU_L = P2^3;
uchar T_flag = 0;//定時(shí)500ms標(biāo)志位
uchar str[23]=0; //字符臨時(shí)存儲(chǔ)變量
unsigned char code SetTime[7]={//2017年,星期日,12月31日,23時(shí)58分56秒,時(shí)間初始值
0x56,0x58,0x23,0x31,0x12,0x07,0x17};
uchar CurrentTime[7]={0};//存儲(chǔ)時(shí)間變量
void main()
{
Init_1602();//1602初始
P0 = 0xff;//關(guān)閉所有數(shù)碼管
WE = 1;
WE = 0;
DU_L = 0;
TMOD = 0x01; //定時(shí)器工作模式配置
TL0 = T_1ms; //裝載初始值
TH0 = T_1ms >>8;
TR0 = 1; //啟動(dòng)定時(shí)器
ET0 = 1; //允許定時(shí)器中斷
EA = 1; //開總中斷
Init_1302(SetTime);//1302初始化
while(1)
{
if(T_flag)//500ms定時(shí)
{
T_flag = 0;
GetTime(CurrentTime);//獲取時(shí)間
str[0] = '2';
str[1] = '0';
str[2] = (CurrentTime[6] >>4)+'0'; //年
str[3] = (CurrentTime[6]& 0x0F)+'0';
str[4] = '-';
str[5] = (CurrentTime[4] >>4)+'0'; //月
str[6] = (CurrentTime[4]& 0x0F)+'0';
str[7] = '-';
str[8] = (CurrentTime[3] >>4)+'0'; //日
str[9] = (CurrentTime[3]& 0x0F)+'0';
str[10] = '?';
str[11] = (CurrentTime[2] >>4)+'0'; //時(shí)
str[12] = (CurrentTime[2]& 0x0F)+'0';
str[13] = ':';
str[14] = (CurrentTime[1] >>4)+'0'; //分
str[15] = (CurrentTime[1]& 0x0F)+'0';
str[16] = ':';
str[17] = (CurrentTime[0] >>4)+'0'; //秒
str[18] = (CurrentTime[0]& 0x0F)+'0';
str[19] = ' ';
str[20] = (CurrentTime[5] >>4)+'0'; //星期
str[21] = (CurrentTime[5]& 0x0F)+'0';
str[22] = '?';
Disp_1602_str(1,4,str); //將獲得的時(shí)間分別顯示到1602的第一二行
Disp_1602_str(2,3,str+11);
}
}
}
//定時(shí)器0中斷子程序,定時(shí)1ms
void timer0() interrupt 1
{
static uint T_500ms = 0;
TL0 = T_1ms;//重裝初始值
TH0 = T_1ms >>8;
T_500ms++;
if(T_500ms >=500)//500ms,置位T_flag
{
T_500ms = 0;
T_flag = 1;
}
}
將程序下查看結(jié)果是否與預(yù)想的一致吧。
15.3 突發(fā)操作模式
上面我們講解的是以單字節(jié)的模式,從 1302 中連續(xù)讀取時(shí)間數(shù)據(jù)。仔細(xì)的同學(xué)可能會(huì)發(fā)現(xiàn)一個(gè)問題,就是我們連續(xù)讀 7 個(gè)時(shí)間寄存器是有先后順序的,會(huì)有讀錯(cuò)數(shù)據(jù)的風(fēng)險(xiǎn)。例如我們要讀的時(shí)間為 23 時(shí) 59 分 59 秒,最開始時(shí)我們把 59 秒讀出來了,如果剛好在你讀完的時(shí)候 59 秒變成了 00 秒,59 分變成了 00 分,23 時(shí)變成了 00 時(shí),接下來把分、時(shí)依次讀出來,因此我們讀出來的時(shí)間為 00 時(shí) 00 分 59 秒,很顯然這個(gè)時(shí)間是不對(duì)的。下面我們講解的突發(fā)操作模式有效的解決了這個(gè)問題。
在突發(fā)操作讀模式下,當(dāng) DS1302 收到突發(fā)讀數(shù)據(jù)指令,DS1302 首先會(huì)把 8 個(gè)時(shí)間寄存器的數(shù)據(jù)同時(shí)讀出存放在 8 個(gè)二級(jí)時(shí)間寄存器中,然后依次把 8 個(gè)二級(jí)時(shí)間寄存器的數(shù)據(jù)輸出給單片機(jī),突發(fā)讀專用指令為 0xBF。同樣,當(dāng)我們需要寫 DS1302 時(shí),當(dāng)收到突發(fā)寫指令后,DS1302 將接收到的 8 個(gè)連續(xù)數(shù)據(jù)存儲(chǔ)到 8 個(gè)二級(jí)時(shí)間寄存器中,然后同時(shí)將 8 個(gè)數(shù)據(jù)寫到時(shí)間寄存器中,突發(fā)寫專用指令為 0xBE。根據(jù)上述原理,編寫突發(fā)寫模式和突發(fā)讀模式函數(shù)如下代碼所示。
//突發(fā)寫模式
void WrBurst_1302(uchar *SetTime)
{
uchar j;
CE_1302 = 1;//拉高片選
WrByte_1302(0xBE);//Burst模式寫專用指令
for(j=0;j<=6;j++)
{
WrByte_1302(SetTime[j]);//寫入7位時(shí)鐘數(shù)據(jù)
}
CE_1302 = 0;//拉低片選
}
//突發(fā)讀模式
void RdBurst_1302(uchar *CurrentTime)
{
uchar j;
CE_1302 = 1;//拉高片選
WrByte_1302(0xBF);//Burst模式讀專用指令
for(j=0;j<=6;j++)
{
*CurrentTime = RdByte_1302();//讀取一個(gè)字節(jié)數(shù)據(jù);
CurrentTime++;
}
CE_1302 = 0;//拉低片選
}
如上圖所示,首先為向 DS1302 寫入突發(fā)讀或者寫指令,然后緊接著是讀取或?qū)懭?7 個(gè)時(shí)間數(shù)據(jù)。前面講解的都是 8 個(gè)連續(xù)的數(shù)據(jù),我們這里寫 7 個(gè)的原因是,時(shí)間顯示這 7 個(gè)就足夠了。上述完整代碼詳見完整代碼"Drive_DS1302.h"、"Drive_DS1302.c"中。
突發(fā)模式的應(yīng)用與單字節(jié)模式類似,只需將如下代碼中的for循環(huán)語句替換成“RdBurst_1302(CurrentTime)”即可。
//獲取當(dāng)前時(shí)間值
void GetTime(uchar *CurrentTime)
{
uchar j;
CE_1302 = 0;//初始化通信引腳
SCLK_1302 = 0;
for(j=0;j<=6;j++)
{
*CurrentTime = RdSingle_1302(0x81+2*j);//讀取7個(gè)時(shí)鐘數(shù)據(jù)
CurrentTime++;
}
//RdBurst_1302(CurrentTime); //當(dāng)采用Burst模式時(shí),使用此語句替代上面for循環(huán)語句
}
15.4 本章小結(jié)
本章詳細(xì)介紹了實(shí)時(shí)時(shí)鐘芯片DS1302的工作原理,驅(qū)動(dòng)程序的編寫,以及DS1302的簡(jiǎn)單應(yīng)用。
-
單片機(jī)
+關(guān)注
關(guān)注
6026文章
44452瀏覽量
630797 -
微處理器
+關(guān)注
關(guān)注
11文章
2229瀏覽量
82201 -
時(shí)鐘芯片
+關(guān)注
關(guān)注
2文章
242瀏覽量
39775 -
DS1302
+關(guān)注
關(guān)注
8文章
448瀏覽量
50543
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論