一些時(shí)候,在我們的嵌入式產(chǎn)品中需要記錄時(shí)間,所以我們就需要獲取時(shí)鐘,當(dāng)然實(shí)現(xiàn)的方式多種多樣,有的MCU本身就有這一功能,不過精度較低。當(dāng)我們的應(yīng)用要求較高時(shí)就需要使用專門的實(shí)時(shí)時(shí)鐘芯片,如DS17887。在本篇中,我們來實(shí)現(xiàn)DS17887的驅(qū)動(dòng)。
1 、功能概述
DS17287、DS17487和DS17887(以下簡(jiǎn)稱DS17x87)將石英晶體和鋰電源集成到一個(gè)24針的DIP封裝中。
1.1 、硬件設(shè)備
DS17x87電源控制電路允許系統(tǒng)由外部激勵(lì)供電,如鍵盤或時(shí)間和日期(喚醒)報(bào)警。PWR輸出引腳是由上述事件之一或其中之一觸發(fā),并用于打開外部電源。PWR引腳由軟件控制,因此當(dāng)一項(xiàng)工作完成時(shí),便可關(guān)掉系統(tǒng)電源。
對(duì)于所有設(shè)備,月底的日期將自動(dòng)調(diào)整為31天以下的月份,包括閏年的修正日期。它也運(yùn)行在24小時(shí)或12小時(shí)的格式與AM/PM指標(biāo)。一個(gè)精確的溫度補(bǔ)償電路監(jiān)控VCC的狀態(tài)。如果檢測(cè)到主電源故障,設(shè)備將自動(dòng)切換到備用電源。DS17x87包括一個(gè)VBAUX輸入,用于驅(qū)動(dòng)輔助功能。
1.2 、寄存器結(jié)構(gòu)
時(shí)間和日歷信息是通過讀取適當(dāng)?shù)募拇嫫髯止?jié)獲得的。通過編寫適當(dāng)?shù)募拇嫫髯止?jié)來設(shè)置或初始化時(shí)間、日歷和警報(bào)。
1.2.1 、寄存器地址分配
DS17887包括有12個(gè)時(shí)間、日期和報(bào)警寄存器、控制寄存器A到D,以及僅駐留在bank 1中的兩個(gè)擴(kuò)展寄存器。12個(gè)時(shí)間、日歷和警報(bào)字節(jié)的內(nèi)容可以是二進(jìn)制或二進(jìn)制編碼的十進(jìn)制(BCD)格式。
在控制寄存器B的DM位為0時(shí),12個(gè)時(shí)間、日歷和警報(bào)字節(jié)的內(nèi)容以BCD碼的格式輸出。各寄存器具體排布如下:
在控制寄存器B的DM位為1時(shí),12個(gè)時(shí)間、日歷和警報(bào)字節(jié)的內(nèi)容以二進(jìn)制碼的格式輸出。各寄存器具體排布如下:
1.2.2 、控制寄存器
DS17887有四個(gè)控制寄存器(A、B、C和D)同時(shí)位于bank 0和bank 1中。這些寄存器在任何時(shí)候都是可訪問的,甚至在更新周期期間也是如此。
控制寄存器A地址為0x0A,第7位為只讀,其它位可讀可寫??刂萍拇嫫鰽的結(jié)構(gòu)如下:
UIP是一個(gè)可以監(jiān)視的狀態(tài)標(biāo)志。狀態(tài)為1則表示最新發(fā)生了更新。DV2、DV1用于設(shè)置時(shí)鐘,DV0用于決定存儲(chǔ)區(qū)是bank0還是bank1,為0則是bank0,為1則是bank1。RS3到RS0幾位則用于設(shè)置速率。
控制寄存器B地址為0x0B,控制寄存器B可讀可寫??刂萍拇嫫鰾的結(jié)構(gòu)如下:
SET位為0允許更新,為0禁止更新。PIE為周期型中斷控制。AIE為報(bào)警中斷控制。UIE為更新結(jié)束中斷使能。SQWE為方波輸出使能。DM控制選擇存儲(chǔ)區(qū)(bank0或bank1)。
控制寄存器C地址為0x0C,控制寄存器C為只讀??刂萍拇嫫鰿的結(jié)構(gòu)如下:
控制寄存器C實(shí)際上是對(duì)控制寄存器B各中斷配置為的狀態(tài)指示。
控制寄存器D地址為0x0D,控制寄存器D為只讀??刂萍拇嫫鱀的結(jié)構(gòu)如下:
控制寄存器D只有VRT位有效,這個(gè)位表示連接到VBAT和VBAUX引腳的電池的狀態(tài)。如果任何一個(gè)電源高于內(nèi)部電壓閾值,VRT位將是高的。這個(gè)位是不可寫的,讀的時(shí)候應(yīng)該總是1。如果存在0,則表示內(nèi)部鋰電源耗盡,RTC數(shù)據(jù)和RAM數(shù)據(jù)的內(nèi)容都有問題。
2 、驅(qū)動(dòng)設(shè)計(jì)與實(shí)現(xiàn)
在上述介紹中,我們已經(jīng)清楚了DS17887實(shí)時(shí)時(shí)鐘芯片的基本功能及寄存器布置。接下來我們將據(jù)此實(shí)現(xiàn)器驅(qū)動(dòng)。
2.1 、對(duì)象定義
我們依然是采用基于對(duì)象的操作。所以我們首先需要獲得對(duì)象,并為這個(gè)對(duì)象按我們的需要設(shè)計(jì)驅(qū)動(dòng)。所以在這里我們首先要設(shè)計(jì)DS17887這個(gè)操作對(duì)象。
2.1.1 、抽象對(duì)象類型
我們首先來分析DS17887的特點(diǎn)。DS17887有4個(gè)控制寄存器用以配置操作和展示狀態(tài),我們將其作為屬性標(biāo)識(shí)DS17887當(dāng)前的狀態(tài)。DS17887最主要的返回值就是時(shí)間數(shù)據(jù),我們將其作為屬性返回時(shí)間數(shù)據(jù)。我們將控制引腳的控制,寄存器的讀寫、總線方向設(shè)定等作為DS17887對(duì)象的操作。由此我們抽象的DS17887對(duì)象類型如下:
/* 定義DS17887對(duì)象類型 */
typedef struct Ds17887Object{
uint8_t ctlReg[4]; //控制寄存器
uint16_t dateTime[6]; //讀取的系統(tǒng)時(shí)間
void(*SetCtlPin[6])(DS17887PinValue value);//控制引腳操作
void(*WriteByte)(uint16_t data); //寫一個(gè)字節(jié)
uint16_t(*ReadByte)(void); //讀一個(gè)字節(jié)
void(*SetBusDirection)(DS17887BusDirection direction);//設(shè)置總線方向
void(*Delayus)(volatile uint32_t nTime); //延時(shí)ms操作指針
}Ds17887ObjectType;
2.1.2 、對(duì)象初始化函數(shù)
對(duì)象必須先初始化才可使用,所以我們還需要設(shè)計(jì)對(duì)象的初始化函數(shù)。初始化函數(shù)除了為對(duì)象屬性賦初始值和給操作指定函數(shù)指針外,還需要檢測(cè)參數(shù)的合法性以及對(duì)硬件設(shè)備做必要的配置?;诖宋覀?cè)O(shè)計(jì)DS17887的初始化函數(shù)如下:
/*對(duì)DS17887進(jìn)行初始化配置*/
voidDs17887Initialization(Ds17887ObjectType *ds17887,
DS17887CtlPinOperation*SetCtlPin,
WriteByteToDs17887 WriteByte,
ReadByteFromDs17887 ReadByte,
Ds17887SetBusDirection SetBusDirection,
Ds17887Delayus Delayus)
{
if((ds17887==NULL)||(SetCtlPin==NULL)||(WriteByte==NULL)||(ReadByte==NULL)||(SetBusDirection==NULL)||(Delayus==NULL))
{
return;
}
for(inti=0;i<6;i++)
{
ds17887->dateTime[0]=0;
ds17887->SetCtlPin[i]=SetCtlPin[i];
}
ds17887->WriteByte=WriteByte;
ds17887->ReadByte=ReadByte;
ds17887->SetBusDirection=SetBusDirection;
ds17887->Delayus=Delayus;
/*將ALE、RD與WR復(fù)位*/
SetCtlPin[DS17887_ALE](Reset);
SetCtlPin[DS17887_WR](Reset);
SetCtlPin[DS17887_RD](Reset);
/*設(shè)置寄存器B和A的值,啟動(dòng)DS17887*/
WriteDataToDS17887(ds17887,DS17887_Reg_B,0x06);
WriteDataToDS17887(ds17887,DS17887_Reg_A,0x20);
//讀取DS17887的時(shí)間
GetDateTimeFromDs17887(ds17887);
}
2.2 、對(duì)象操作
我們定義一個(gè)對(duì)象的目的最終是為了操作這個(gè)對(duì)象獲取我們需要的數(shù)據(jù)。DS17887實(shí)時(shí)時(shí)鐘最基本的操作就是對(duì)個(gè)寄存器的讀寫,進(jìn)而引申為對(duì)事實(shí)時(shí)間的獲取及校準(zhǔn)。這一節(jié)我們就據(jù)此來實(shí)現(xiàn)DS17887對(duì)象的操作函數(shù)。
2.2.1 、讀數(shù)據(jù)操作
DS17887的讀操作時(shí)序如下圖所示:
我們根據(jù)以上時(shí)序圖來開發(fā)DS17887對(duì)象的讀操作函數(shù)如下:
/*從DS17887讀數(shù)據(jù)*/
static uint16_t ReadDataFromDS17887(Ds17887ObjectType*ds17887,uint16_t address)
{
/*將片選信號(hào)置位,失能片選*/
ds17887->SetCtlPin[DS17887_CS](Set);
/*將RD與WR置位*/
ds17887->SetCtlPin[DS17887_WR](Set);
ds17887->SetCtlPin[DS17887_RD](Set);
ds17887->Delayus(2);
/*置位ALE*/
ds17887->SetCtlPin[DS17887_ALE](Set);
/*將地址數(shù)據(jù)總線的模式改為輸出*/
ds17887->SetBusDirection(Out);
/*寫寄存器地址*/
ds17887->WriteByte(address);
/*將片選信號(hào)置位,使能片選*/
ds17887->SetCtlPin[DS17887_CS](Reset);
ds17887->Delayus(2);
/*復(fù)位ALE*/
ds17887->SetCtlPin[DS17887_ALE](Reset);
ds17887->Delayus(2);
/*復(fù)位RD*/
ds17887->SetCtlPin[DS17887_RD](Reset);
ds17887->Delayus(10);
/*將地址數(shù)據(jù)總線的模式改為輸入*/
ds17887->SetBusDirection(In);
ds17887->Delayus(40);
/*讀取數(shù)據(jù)*/
uint16_t readData=0;
readData=ds17887->ReadByte();
ds17887->Delayus(4);
/*將RD置位,并將CS信號(hào)置位,失能芯片*/
ds17887->SetCtlPin[DS17887_RD](Set);
ds17887->SetCtlPin[DS17887_CS](Set);
ds17887->Delayus(4);
/*將ALE置位*/
ds17887->SetCtlPin[DS17887_ALE](Set);
ds17887->Delayus(20);
return readData;
}
2.2.2 、寫數(shù)據(jù)操作
DS17887實(shí)時(shí)時(shí)鐘的寫操作時(shí)序如下圖所示:
我們根據(jù)以上時(shí)序圖來開發(fā)DS17887對(duì)象的寫操作函數(shù)如下:
/*向DS17887寫數(shù)據(jù)*/
static void WriteDataToDS17887(Ds17887ObjectType *ds17887,uint16_t address,uint16_t data)
{
/*將DS17887的片選信號(hào)失能*/
ds17887->SetCtlPin[DS17887_CS](Set);
/*將RD與WR置位*/
ds17887->SetCtlPin[DS17887_WR](Set);
ds17887->SetCtlPin[DS17887_RD](Set);
ds17887->Delayus(2);
/*將ALE信號(hào)置高*/
ds17887->SetCtlPin[DS17887_ALE](Set);
/*將地址數(shù)據(jù)總線的模式改為輸出*/
ds17887->SetBusDirection(Out);
/*寫寄存器地址*/
ds17887->WriteByte(address);
/*將片選信號(hào)置位,使能片選*/
ds17887->SetCtlPin[DS17887_CS](Reset);
ds17887->Delayus(4);
/*復(fù)位ALE信號(hào)*/
ds17887->SetCtlPin[DS17887_ALE](Reset);
ds17887->Delayus(4);
/*復(fù)位WR*/
ds17887->SetCtlPin[DS17887_WR](Reset);
/*寫數(shù)據(jù)*/
ds17887->WriteByte(data);
ds17887->Delayus(4);
/*將WR置位,并將CS信號(hào)置位,失能芯片*/
ds17887->SetCtlPin[DS17887_WR](Set);
ds17887->SetCtlPin[DS17887_CS](Set);
ds17887->Delayus(4);
/*將ALE置位*/
ds17887->SetCtlPin[DS17887_ALE](Set);
ds17887->Delayus(10);
}
2.2.3 、時(shí)間數(shù)據(jù)獲取
我們操作DS17887的根本目的就是獲取系統(tǒng)時(shí)鐘,在我們實(shí)現(xiàn)了對(duì)DS17887寄存器的讀寫操作后,我們可以據(jù)此得到時(shí)鐘數(shù)據(jù)。
/*從實(shí)時(shí)時(shí)鐘模塊讀取時(shí)間*/
void GetDateTimeFromDs17887(Ds17887ObjectType *ds17887)
{
/*讀取系統(tǒng)時(shí)間值*/
ds17887->dateTime[0]=ReadDataFromDS17887(ds17887,DS17887_Year);//系統(tǒng)時(shí)間年
ds17887->Delayus(5);
ds17887->dateTime[1]=ReadDataFromDS17887(ds17887,DS17887_Month);//系統(tǒng)時(shí)間月
ds17887->Delayus(5);
ds17887->dateTime[2]=ReadDataFromDS17887(ds17887,DS17887_Date);//系統(tǒng)時(shí)間日
ds17887->Delayus(5);
ds17887->dateTime[3]=ReadDataFromDS17887(ds17887,DS17887_Hour);//系統(tǒng)時(shí)間時(shí)
ds17887->Delayus(5);
ds17887->dateTime[4]=ReadDataFromDS17887(ds17887,DS17887_Minute);//系統(tǒng)時(shí)間分
ds17887->Delayus(5);
ds17887->dateTime[5]=ReadDataFromDS17887(ds17887,DS17887_Second);//系統(tǒng)時(shí)間秒
ds17887->Delayus(5);
}
2.2.4 、時(shí)間校準(zhǔn)
獲取的時(shí)鐘數(shù)據(jù)也許會(huì)存在偏差,這時(shí)就需要對(duì)系統(tǒng)的時(shí)鐘進(jìn)行校準(zhǔn)。停止時(shí)間更新后,修改時(shí)間寄存器的數(shù)據(jù),然后再開啟計(jì)時(shí)就完成了時(shí)間的校準(zhǔn)。
/*校準(zhǔn)DS17887的時(shí)間*/
void CalibrationDs17887DateTime(Ds17887ObjectType *ds17887,uint16_t * dateTime)
{
/*將ALE、RD與WR復(fù)位*/
ds17887->SetCtlPin[DS17887_ALE](Reset);
ds17887->SetCtlPin[DS17887_WR](Reset);
ds17887->SetCtlPin[DS17887_RD](Reset);
/*初始化控制寄存器,以便校準(zhǔn)時(shí)間*/
WriteDataToDS17887(ds17887,DS17887_Reg_A,0x20);
WriteDataToDS17887(ds17887,DS17887_Reg_B,0x06);
WriteDataToDS17887(ds17887,DS17887_Reg_B,0x80);
/*設(shè)置系統(tǒng)時(shí)間值*/
WriteDataToDS17887(ds17887,DS17887_Year,dateTime[0]);//系統(tǒng)時(shí)間年
WriteDataToDS17887(ds17887,DS17887_Month,dateTime[1]);//系統(tǒng)時(shí)間月
WriteDataToDS17887(ds17887,DS17887_Date,dateTime[2]);//系統(tǒng)時(shí)間日
WriteDataToDS17887(ds17887,DS17887_Hour,dateTime[3]);//系統(tǒng)時(shí)間時(shí)
WriteDataToDS17887(ds17887,DS17887_Minute,dateTime[4]);//系統(tǒng)時(shí)間分
WriteDataToDS17887(ds17887,DS17887_Second,dateTime[5]);//系統(tǒng)時(shí)間秒
/*設(shè)置寄存器B和A的值,啟動(dòng)DS17887*/
WriteDataToDS17887(ds17887,DS17887_Reg_B,0x06);
WriteDataToDS17887(ds17887,DS17887_Reg_A,0x20);
//讀取DS17887的時(shí)間
GetDateTimeFromDs17887(ds17887);
}
3 、驅(qū)動(dòng)的使用
我們實(shí)現(xiàn)了DS17887的驅(qū)動(dòng),那么如何使用這一驅(qū)動(dòng)呢?其實(shí)與我們?cè)诘诙?jié)中開發(fā)驅(qū)動(dòng)的流程是一致的,先定義對(duì)象,再操作對(duì)象。
3.1 、聲明并初始化對(duì)象
我們前面已經(jīng)定義了DS17887的對(duì)象類型。我們要得到一個(gè)DS17887對(duì)象,首先要使用Ds17887ObjectType聲明一個(gè)DS17887對(duì)象變量,如下:Ds17887ObjectType ds17887。
有了這個(gè)變量后并不能馬上使用它進(jìn)行操作,還需要先對(duì)它使用Ds17887Initialization初始化函數(shù)進(jìn)行初始化。這個(gè)函數(shù)有很多參數(shù),其中有5個(gè)參數(shù)是函數(shù)指針。
/* 定義DS17887控制引腳操作函數(shù)指針 */
typedef void(*DS17887CtlPinOperation)(Ds17887PinValueType value);
/* 定義DS17887寫數(shù)據(jù)操作函數(shù)指針 */
typedef void(*WriteByteToDs17887)(uint16_t data);
/* 定義DS17887讀數(shù)據(jù)操作函數(shù)指針 */
typedef uint16_t(*ReadByteFromDs17887)(void);
/* 定義設(shè)置數(shù)據(jù)地址總線方向函數(shù)指針 */
typedef void(*Ds17887SetBusDirection)(Ds17887BusDirectionType direction);
/* 定義延時(shí)操作函數(shù)指針類型 */
typedef void (*Ds17887Delayus)(volatile uint32_tnTime);
所以我們要譯者5個(gè)函數(shù)指針類型定義相應(yīng)的函數(shù),并將這些函數(shù)作為參數(shù)傳遞給初始化函數(shù)實(shí)現(xiàn)對(duì)DS17887對(duì)象的使用。調(diào)用初始化函數(shù)如下:
void Ds17887Initialization(&ds17887,SetCtlPin,WriteByte,ReadByte,SetBusDirection,Delayus)
需要說明一下的是第二個(gè)參數(shù)實(shí)際是一個(gè)函數(shù)指針數(shù)組,也就是說每一個(gè)控制引腳都需要定義一個(gè)DS17887CtlPinOperationType類型的操作函數(shù),并組成數(shù)組傳遞進(jìn)來。這個(gè)數(shù)據(jù)必須按照enum Ds17887CtlPins枚舉定義的順序,即:
/* 定義DS17887控制引腳的種類 */
typedef enum Ds17887CtlPins{
DS17887_CS,
DS17887_WR,
DS17887_RD,
DS17887_ALE,
DS17887_KS,
DS17887_RCLR
}Ds17887CtlPinsType;
3.2 、基于對(duì)象進(jìn)行操作
完成了對(duì)對(duì)象的初始化就可以實(shí)現(xiàn)對(duì)對(duì)象的操作了。其實(shí)對(duì)DS17887的操作比較簡(jiǎn)單無非就是獲取時(shí)間數(shù)據(jù)和校準(zhǔn)時(shí)間數(shù)據(jù)。
獲取時(shí)間數(shù)據(jù)就是調(diào)用GetDateTimeFromDs17887函數(shù)來實(shí)現(xiàn)就可以了。前面初始化完成的DS17887對(duì)象就是其參數(shù)。調(diào)用如下:
GetDateTimeFromDs17887(&ds17887);
而校準(zhǔn)時(shí)間數(shù)據(jù)就是調(diào)用CalibrationDs17887DateTime函數(shù)來校準(zhǔn)時(shí)間。前面初始化完成的DS17887對(duì)象就是其參數(shù),此外需要輸入標(biāo)準(zhǔn)時(shí)間數(shù)據(jù)作為第二個(gè)參數(shù)。
uint16_tdateTime[6]={year,month,day,hour,minute,second};
然后調(diào)用函數(shù)校準(zhǔn)時(shí)間:
CalibrationDs17887DateTime(&ds17887,dateTime);
4 、應(yīng)用總結(jié)
在一個(gè)項(xiàng)目我們需要在每十秒的時(shí)間間隔記錄一些列數(shù)據(jù)。所以我們需要以最小為1秒的精度讀取系統(tǒng)實(shí)時(shí)時(shí)鐘。我們將采用DS17877作為系統(tǒng)的實(shí)時(shí)時(shí)鐘,完全能夠符合我們的要求。
在我們這個(gè)驅(qū)動(dòng)程序中,我們默認(rèn)將DS17887對(duì)象二進(jìn)制數(shù)據(jù)格式、24小時(shí)制、并使用存儲(chǔ)區(qū)域bank0。如果需要不同的配置,則可以修改初始化函數(shù)中的配置。
還有對(duì)控制引腳的控制是一個(gè)6個(gè)元素的函數(shù)指針數(shù)組。這6個(gè)函數(shù)的順序必須按照枚舉Ds17887CtlPinsType的排布順序。
-
寄存器
+關(guān)注
關(guān)注
31文章
5392瀏覽量
121929 -
實(shí)時(shí)時(shí)鐘
+關(guān)注
關(guān)注
4文章
287瀏覽量
66215 -
時(shí)鐘芯片
+關(guān)注
關(guān)注
2文章
255瀏覽量
40149 -
驅(qū)動(dòng)設(shè)計(jì)
+關(guān)注
關(guān)注
1文章
111瀏覽量
15374
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
實(shí)時(shí)時(shí)鐘芯片DS1302應(yīng)用
【賣方案】DS17887-3IND+, 3V/5V實(shí)時(shí)時(shí)鐘
DS17487, DS17885, DS17887,pdf
實(shí)時(shí)時(shí)鐘模DS1302程序列子
DS17285, DS17287, DS17485, DS1

基于實(shí)時(shí)時(shí)鐘模塊 時(shí)鐘芯片DS1302

基于ARM和DS1307的實(shí)時(shí)時(shí)鐘系統(tǒng)設(shè)計(jì)

DS17887-3+ 時(shí)鐘/定時(shí) - 實(shí)時(shí)時(shí)鐘

DS17887-5+ 時(shí)鐘/定時(shí) - 實(shí)時(shí)時(shí)鐘

DS17887-3IND+ 時(shí)鐘/定時(shí) - 實(shí)時(shí)時(shí)鐘

DS17887-5IND+ 時(shí)鐘/定時(shí) - 實(shí)時(shí)時(shí)鐘

DS17887-5 時(shí)鐘/定時(shí) - 實(shí)時(shí)時(shí)鐘

DS17887-5IND 時(shí)鐘/定時(shí) - 實(shí)時(shí)時(shí)鐘

DS17887-3 時(shí)鐘/定時(shí) - 實(shí)時(shí)時(shí)鐘

DS17887-3-IND 時(shí)鐘/定時(shí) - 實(shí)時(shí)時(shí)鐘

評(píng)論