DS1302;12.1 概述;DS1302實時時鐘芯片我真的為她著迷呀;12.2芯片介紹;VCC2;GND;VCC1;SCLK;I/O;RST工作電源電源地后備電源時鐘 信號數據輸入輸出;我在看視頻教程時,老時覺得很奇怪的,為什么老師會;SCLK是串行時鐘信號的輸入,而I/O則是串行數;12.3時序分析;以上是 DS1302一個字節寫入的時序圖;上升沿有效
第12章DS1302
12.1概述
DS1302 實時時鐘芯片我真的為她著迷呀。什么原因導致我為她著迷?自己接觸后您自然而然會明白的。本片筆記的手法與前幾章寫筆記的手法明顯而外的不同,因為介紹 DS1302芯片不像是介紹單片機內部的資源一樣,都有參考步驟可言,但設置DS1302的方法太多樣化了,讓新手很不容易。
12.2芯片介紹
VCC2
GND
VCC1
SCLK
I/O
RST工作電源電源地后備電源時鐘信號數據輸入輸出復位信號|片選信號
我 在看視頻教程時,老時覺得很奇怪的,為什么老師會把RST信號說成片選信號呢?這也難怪的,因為在DS1302的時序圖,RST信號的角色有如片選信號。 眾多的芯片中,往往最后的PIN都是工作電源的輸入。可是DS1302卻是很奇特,VCC2是工作電源的輸入而VCC1卻是后備電源的輸入,或者是充電的 電流的經過,更詳細的介紹在后面的內容會繼續介紹。
SCLK是串行時鐘信號的輸入,而I/O則是串行數據輸入輸出。X1,X2是晶振32.768kHz的輸入,數據手冊中有記錄晶振的兩端需無極電容,手冊中的記錄是6pf,但是在眾多的AVR學習板中會看到23pf,27pf的出現。
12.3時序分析
以上是DS1302一個字節寫入的時序圖。第一個字節是地址字節,第二個字節是數據字節。RST信號必須拉高,否則數據的輸入是無效的。換一句話說,RST信號控制數據|時間信號輸入的開始和結束。地址字節和數據字節的讀取時
上升沿有效,而且是由LSB開始讀入。
讀一個字節和寫一個字節有明顯的不一樣,先是寫地址字節,然后再讀數據字節,寫地址字節時上升沿有效,而讀數據字節時下降沿有效,當然前提是RST信號必須拉高。寫地址字節和讀數據字節同是LSB開始。
再 重申一次,讀一個字節和寫一個字節是不一樣,在寫一個字節的時候,AVR的IO口一直保持輸出狀態,相反的在讀一個字節的時候AVR的IO口先是輸出狀 態,然后是輸入狀態,且必須改變時鐘信號的順序。(補充一點題外話,我在編輯時序的時候,由于疏忽了一點“小錯誤”,后果卻是很嚴重。)
12.3DS1302時鐘|日期|控制|爆發寄存器
在介紹DS1302的時序圖中不都是,先地址字節,然后數據字節碼?那么地址字節和數據字節又有什么關系呢?(看看下面的圖)
上面的圖說明了每個寄存器的定義和地址字節,而每個地址字節的LSB可以是0或者1,邏輯0代表寫,邏輯1代表讀。如果忽略每個地址字節的LSB,十六進制則是0x80+i,而i每一次累加2。我們一個一個寄存器來看吧:
第一:秒鐘寄存器地址字節;0x80
其 實呀,我很佩服該芯片的設計人員,將芯片設計得很貼心,為什么呢?因為秒鐘寄存器,除了記錄秒鐘以外,還控制了DS1302的時鐘開關(晶振開始工作,或 者晶振禁止工作)。該位第7位CH,當寫入邏輯1時DS1302停止工作,時間的計時保持最后一次的狀態,如果寫入邏輯0DS1302則開始工作,時間從 最后一次狀態中繼續計時。
換成另一句話說,每一次寫入秒鐘,都會使DS1302工作,但這又是為什么呢?秒鐘寄存器是八位寄存器,高四位中 的BIT4~BIT6(BIT7除外)記錄十位,而低四位記錄個位。秒鐘的計算最多也是59,如果換成十六進制的話是0x59。所以呢,最高位基本上都是 用不到,但我們每一次向秒鐘寄存器進行初始化的時候,都會很自然的把最高位BIT7記錄成0,CH位為邏輯0,DS1302就開始工作。
第二:分鐘寄存器地址字節;0x82
八位寄存器,高四位記錄十位(BIT7除外),低四位記錄個位
第三:時鐘寄存器地址字節;0x84
八 位寄存器,高四位記錄十位(BIT7,BIT6除外),低四位記錄個位。時鐘寄存器的最高位,決定了時間是以24小時制,還是12小時制。邏輯1為12小 時致,邏輯0為24小時致。至于24小時致是默認的,為什么這么說呢?該解釋與秒鐘寄存器很相似,因為每一次我們為時鐘寄存器賦值的時候,由于時鐘最大值 是23小時(0x23),還是11小時(0x11),自然而然最高位我們都會賦0值。
第四:日寄存器地址字節:0x86
八位寄存器,高四位記錄十位(實際上僅有BIT4~5被使用),低四位記錄個位。第五:月寄存器地址字節:0x88
八位寄存器,高四位記錄十位(實際上僅有BIT4被使用),低四位記錄個位。第六:周寄存器地址字節:0x8A
八位寄存器,僅有低四位被使用(BIT0~3),用來記錄個位。
第七:年寄存器地址字節:0x8C
八位寄存器,高四位記錄十位,低四位記錄個位。
第八:控制寄存器地址字節:0x8E
八位寄存器,僅BIT7有用,BIT7亦即WP位(WriteProtect),邏輯0解除寫保護,邏輯1開啟寫保護。換一句話說,每一次寫其他寄存器WP位必須先置0。
第九:充電寄存器?(TrickleCharge)地址字節:0x90
這是開啟細流充電的寄存器,寫保留。后面有詳細的解釋。
第十:爆發寄存器?(BurstMode)地址字節:0x92
這個寄存器的功能可以用軟件來模擬,無視他把。
.4DS1302的RAM1212.4
DS1302真的很厚道,還設立了31個字節的RAM空間,RAM空間的開始地址字節是0x94。RAM空間可以讓使用著任意發揮,你可以把它當做外存儲器,但是前提DS1302必須一直供電。要訪問任意空間也很簡單,如下表:
嗯,使用RAM空間的方法很自由,自己發揮想象力吧。
12.5細流充電TrickleCharge
在前幾個Article,介紹了DS1302有后備供電的輸入,亦即VCC1引腳,DS1302允許透過控制內部的充電寄存器,經VCC2向VCC1
流入的充電細流(很小很小的電流)。我們看一看Hj-2G的硬件布局吧:
VCC2連接的5v是工作電壓,VCC1連接的3v是后備工作電壓。充電寄存器就控制VCC2流向VCC1之間的“阻值”和“二極管的降壓”。
在這里補充一些題外話:當VCC2有電源輸入時,VCC1是停止供電的,但同一時間也可以為VCC1進行細流充電(這要看VCC1是否連接著可充電電池)。一旦
VCC2停止供電,VCC1就開始工作,與此同時細流充電就變成沒有意義了。
以 上是TrickleCharge的概念圖,充電控制寄存器的高四位(BIT7~4)是TCS,TrickleChargerSelect位,僅1010才 會開啟充電功能。而BIT3~2是DS,DiodeSelect位,01選擇一個diode,10選擇兩個diode串聯。BIT1~0是 RS,ResistorSelect位,兩位組成了3個阻值的選擇。瀏覽下表:
RS位組合的阻值選擇表細流公式
渴求細流的公式可以由上右表求出,阻值位R1,diodedrop為二極管的降壓,0.7v(一個二極管),1.4(串聯二極管)。不過我比較有愛,將他分成六個等級,為了編程更方便。#defineLV6
#defineLV5
#defineLV4
#defineLV3
#defineLV2
#defineLV10xA50xA90xA60xAA0xA70xAB//0.7降壓,2k阻值,2.15mA//1.4降壓,2k阻值,1.80mA
//0.7降壓,4k阻值,1.07mA//1.7降壓,4k
阻值,0.90mA
//0.7降壓,8k阻值,0.50mA//1.4降壓,8k阻值,0.45mA
12.6DS1302的地址字節
DS1302的地址字節,也可以作為命令字節。A0~A4是用來選擇地址的位,而BIT0是制定該地址字節是寫入還是讀取,BIT6是作為選擇RAM還是CLOCK的區別。其實這也沒有什么困難的啦。
慧 爭電子免費共享資料、歡迎復制共享、沒有版權;12.7簡單歸納;控制DS1302的是RST信號,拉低無效,拉高有;12.8DS1302頭文件的理 解;自帶來的DS1302頭文件,沒有自己定義的好;首先是按寄存器的地址字節,聲明的宏定義;#defineCLKOFF0x80#define;以上 的宏定義,是作為配置時使用的;IO口的宏定義,可以使程式的可達性更高;接
慧爭電子免費共享資料、歡迎復制共享、沒有版權。HJ-2G多功能AVR/51二合一開發板學習筆記
12.7簡單歸納
控 制DS1302的是RST信號,拉低無效,拉高有效。而DS1302是串行輸入,串行讀入。寫入一個字節和讀取一個字節的時序不同。寫入一個字節都是上跳 沿有效,讀入一個字節先是上跳沿有效,然后下降沿有效。地址字節也可稱為命令字節,BIT6控制了對RAM/CLOCK的訪問控制,而BIT0決定了地 址|命令字節的寫入還是讀出的特性。DS1302有31個字節的RAM空間。除此之外,還有細流充電的功能。秒寄存器決定了DS1302開始工作與否,而 時鐘寄存器有12小時制和24小時制之分。每當要寫入任何一個寄存器|RAM空間,寫入控制寄存器的WP位必須置0,解除寫保護。
12.8DS1302頭文件的理解
自 帶來的DS1302頭文件,沒有自己定義的好。這一章筆記,主要是以頭文件的方式解釋。這是我認為,新手最簡單習得的辦法。//命令,地 址#defineSEC0x80#defineMIN0x82#defineHOUR0x84#defineDAY0x86#defineMONTH0x88#defineWEEK0x8A#defineYEAR0x8C#defineCTRL0x8E#defineCHRG0x90#defineBRUST0xBE#defineRAM0xC0
首先是按寄存器的地址字節,聲明的宏定義。SEC為開始,亦即0x80,隨后都是+2。宏定義中的地址字節沒有包括了,BIT0寫/讀的控制位。//配置
#defineCLKOFF0x80#defineCLKON0x00#defineT120x80#defineT240x00#defineLOCK0x80#defineUNLOCK0x00#defineLV60xA5#defineLV50xA9#defineLV40xA6#defineLV30xAA#defineLV20xA7#defineLV10xAB
以上的宏定義,是作為配置時使用的。自己看著明白吧,很簡單而已。
IO口的宏定義,可以使程式的可達性更高。
接下來要為寫一個字節函數,和讀一個字節函數做介紹。
//DS1302寫一個字節函數
voidDS1302_Write(unsignedcharAdd,unsignedcharData){
unsignedchari;
DDRB|=BIT(IO);
PORTB&=~BIT(SCLK);PORTC|=BIT(RST);Add&=0xfe;
//IO為輸出
//時鐘信號拉低<==這個很重要//拉高RST信號
//將Add亦即地址字節的LSB設置為0
for(i=0;i<8;i++)//寫地址|命令字節,上跳沿有效{
PORTB&=~BIT(SCLK); //時鐘信號拉低if(Add&0x01)PORTB|=BIT(IO);//判斷地址字節的最低位,1位拉高IOelsePORTB& amp;=~BIT(IO);//否則拉低IOPORTB|=BIT(SCLK);//時鐘信號拉高Add>>=1;//地址字節左移一位
}
for(i=0;i<8;i++)//寫數據字節,上跳沿有效{
PORTB&=~BIT(SCLK);
if(Data&0x01)PORTB|=BIT(IO);elsePORTB&=~BIT(IO);PORTB|=BIT(SCLK);Data>>=1;
}
PORTC&=~BIT(RST);//拉低RST信號,結束寫一個字節。}
//時鐘信號拉低
//判斷數據字節的最低位,1位拉高IO//否則拉低IO//時鐘信號拉高//數據字節左移一位
函 數完全是依照時序圖寫的,該函數帶有兩個參數,Add和Data,顧名思義就是如字面上的意思,地址字節和數據字節。地址字節需經過0xfe與云散,取得 xxxxxxx0寫字節|命令的特征。寫地址和寫數據字節都是下降沿有效。有一點需要注意的是:當RST信號還沒有拉高之前,必須把時鐘信號初始化|拉 第,這一點很重要請注意。(我就是在這一點犯了“小錯誤”)
這是讀一個字節的函數,帶unsignedchar返回型,但僅有一個參數,那 即是Add:地址字節。Add寫入之前必須經過0x01的與運算,為了就是取得xxxxxxx1的地址|命令的讀特征。在RST拉高之前,和寫一個字節函 數一樣的注意點,就是必須先將時鐘信號拉低,不然的話函數會失敗。
還有另一個注意點就是IO的狀態,在寫地址|命令字節的時候必須設置位輸出狀態,而當讀取數據之前必須將IO設置位高祖態輸入狀態。程式的最后就是返回從IO讀到的數據。
補充,寫一個字節函數,和讀一個字節函數是最基礎和最重要的函數,不允許有錯誤出現,不然的話,一切的設定都徒勞無功。
//模擬BURST函數,讀取數據|時間
voidDS1302_Get_Timer(unsignedchar*pTimer){
unsignedchari,j;
for(i=0,j=0;i<7;i++,j+=2)
pTimer[i]=DS1302_Read((0x80+j));
}
//i循環次數,j地址
//將數據一一讀入時間數組
上 面兩個函數是連續寫時間,和連續讀時間函數。只要明白數組傳遞的規則,基本上好似很簡單就看懂了。但是有一個條件,就是必須按照以下的數組順 序:unsignedcharTimer[]={0x40,0x59,0x23,0x31,0x12,0x07,0x09};//秒分時日月周年 unsignedcharBuffer[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00};//緩沖數組Timer數組是 用于連續寫入,而Buffer數組是用于連續讀取。
//開啟寫保護函數voidProtect(){
DS1302_Write(CTRL,LOCK);}
//解除寫保護函數voidUnprotect(){
DS1302_Write(CTRL,UNLOCK);}
啟動寫保護和關閉寫保護的函數也很容易明白。自己看著辦吧。
細流充電啟動和關閉函數,關閉方沒有什么特別,而啟動方比較特別,帶有Level的參數,實際上是對細流充電控制寄存器寫入相關的值而已,而Level已經宏定義了,就是Lv1~Lv6.
//寫入RAM函數
voidDS1302_RAM_Write(unsignedcharN,unsignedcharData){
if(N<32)//RAM地址不可以超過31
DS1302_Write(RAM+N,Data);
}
//讀取RAM函數
unsignedcharDS1302_RAM_Read(unsignedcharN){
if(N<32)//RAM地址不可以超過31
returnDS1302_Read(RAM+N);
}
依然是那句話,很容易明白的函數。部比較不同的是,if語句預防萬一了訪問無效地址字節和越界訪問。
DS1302 應用IO口的初始化函數,很簡單,而且;//DS1302啟動函數voidClock_On;DS1302_Write(SEC,CLKON);; //DS1302停止函數voidClock_Of;DS1302_Write(SEC,CLKOFF);啟動工作和關閉工作的函數;//設時間為12小 時制函數;//該函數沒有什么用處,24小時制為默認,為了演;DS1
DS1302應用IO口的初始化函數,很簡單,而且程式也注釋了。
//DS1302啟動函數voidClock_On(){
DS1302_Write(SEC,CLKON);}
//DS1302停止函數voidClock_Off(){
DS1302_Write(SEC,CLKOFF);}
啟動工作和關閉工作的函數。
//設時間為12小時制函數
//該函數沒有什么用處,24小時制為默認,為了演示voidSet12(){
DS1302_Write(HOUR,T12);}
//設時間為24小時制函數
//該函數沒有什么用處,24小時制為默認,為了演示voidSet24(){
DS1302_Write(HOUR,T24);}
該函數沒有實際的用處,編輯出來時為了演示·解釋·明白而已。
慧爭電子免費共享資料、歡迎復制共享、沒有版權。HJ-2G多功能AVR/51二合一開發板學習筆記
.9實例程式:1212.9
這里我就不編輯什么程式了,寫了幾個實例,自己看看吧~很簡單而已,因為使用DS1302的方法太自由了。
實例程式1:
==============================================================//1200-DS1302_Driver.c
//DS1302最基本的驅動程式//akuei202-01-10#include"iom16v.h"#include"macros.h"#include"DS1302.h"#include"USART.h"voidDelay(unsignedcharx){
while(x--);}
voidmain(){
unsignedcharTimer[]= {0x02,0x35,0x23,0x15,0x11,0x07,0x33};//秒分時日月周年unsignedcharBuffer[]= {0x00,0x00,0x00,0x00,0x00,0x00,0x00};unsignedcharTemp,Temp1,Temp2;inti;
DS1302_IO_Init();USART_Init();
//初始化DS1302的設置//Unprotect();Unprotect();Clock_Off();Set24();
//DS1302_Set_Timer(Timer);Protect();
//連續讀時間
/*//讀任意一個寄存器while(1){
Temp=DS1302_Read(YEAR);Temp1=((Temp>>4)&0x07);Temp2=(Temp&0x0f);
USART_Send('0'+Temp1);
//讀年寄存器//取十位//取個位//串口發送
一個很簡單的實例程式,介紹了很多的設置辦法。
慧爭電子免費共享資料、歡迎復制共享、沒有版權。HJ-2G多功能AVR/51二合一開發板學習筆記
實例程式2
==============================================================//1201-DS1302_RTC.c
//DS1302最基本的驅動程式//akuei204-01-10#include"iom16v.h"#include"macros.h"#include"DS1302.h"#include"USART.h"//延遲函數
voidDelay(unsignedcharx){
while(x--);}
//主函數voidmain(){
unsignedcharTimer[]= {0x40,0x59,0x23,0x31,0x12,0x07,0x09};//秒分時日月周年unsignedcharBuffer[]= {0x00,0x00,0x00,0x00,0x00,0x00,0x00};//緩沖數組unsignedcharTemp,Temp1,Temp2; //緩沖變量inti,j,k;//for循環變量
DS1302_IO_Init();USART_Init();
//初始化DS1302的設置Unprotect();Clock_Off();Set24();
DS1302_Set_Timer(Timer);//Charge_On(LV3);Clock_On();Protect();
//DS1302引腳初始化//USART初始化
//解除寫保護//時鐘關閉
//設置為24小時制
//設定時間
//充電開啟,等級3,大約1mA//時鐘啟動,秒種從00計數開始//開啟寫保護
while(1){
for(k=0,j=0;k<7;k++,j+=2)
年
{
Temp=DS1302_Read(0x80+j);Temp1=((Temp>>4)&0x07);Temp2=(Temp&0x0f);
//讀取時間//取十位//取個位
//循環7次,分別讀取秒分時日月周
這是另一個的實例程式。怎樣編寫程式都不重要,最重要是編程的目的。以上兩個實例程式我都使用了串口發送函數,而一般的實例程式都是使用1602液晶。見仁見智吧。
最后附上USART.h和完整的DS1302.h頭文件(注意:只是適合HJ-2G)
==============================================================
//DS1302.h//命令,地址#defineSEC#defineMIN#defineDAY#define
0x800x820x86
#defineHOUR0x84
MONTH0x88
#defineWEEK0x8A#defineYEAR0x8C#defineCTRL0x8E#defineCHRG0x90#defineBRUST0xBE#defineRAM//配置
#defineCLKOFF0x80#defineCLKON0x00#defineT12#defineT24#defineLOCK#defineLV6#defineLV5#defineLV4#defineLV3#defineLV2
0x800x000x800xA50xA90xA60xAA0xA7
//0.7降壓,2k阻值,2.15mA//1.4降壓,2k阻值,1.80mA//0.7降壓,4k阻值,1.07mA//1.7降壓,4k阻值,0.90mA//0.7降壓,8k阻值,0.50mA
0xC0
#defineUNLOCK0x00
//USART.h; //波特率,晶振;#defineBAUD9600;#defineFXTAL11059200;//串口接收完畢中斷觸發聲 明;#pragmainterrupt_handler;//變量定義:接收緩沖變量,接收標志位;unsignedcharRX_Buffer=0x; //函數聲明;voidUSART_Send(unsignedc;//串口IO初
//USART.h
//波特率,晶振
#defineBAUD9600
#defineFXTAL11059200
//串口接收完畢中斷觸發聲明
#pragmainterrupt_handlerUSART_Received_Ir:12
//變量定義:接收緩沖變量,接收標志位
unsignedcharRX_Buffer=0x00,RX_Flag=0;
//函數聲明
voidUSART_Send(unsignedchar);
//串口IO初始化函數
voidUSART_IO_Init()
{
DDRD|=BIT(PD1);//PD1:TX為輸出狀態
}