關於實時時鐘模組DS1302使用心得
最近在做萬年曆,用到實時時鐘DS1302模組,花了兩天時間看資料和寫驅動,想記錄一下我的學習經過,順便做一下總結。
首先就是在圖書館查各種資料,於是查到的大多是這些,主要時硬體方面的資料:
其實能查到很多資料,但是能為我們所用的不是很多。在使用一個晶片時,我一般時按照一下步驟去學習:
1、晶片介紹;
2、檢視引腳定義;
3、外圍電路
4、分析時序圖;
5、模仿著編寫驅動程式,然後自己動手寫驅動。
6、實現功能。
下面我就按照這個順序去學習這款晶片;
一、晶片介紹
DS1302是DALLAS(達拉斯)公司出的一款涓流充電時鐘晶片,2001年DALLAS被MAXIM(美信)收購,因此我們看到的DS1302的資料手冊既有DALLAS的標誌,又有MAXIM的標誌;
DS1302實時時鐘晶片廣泛應用於電話、傳真、行動式儀器等產品領域,他的主要效能指標如下:
1、DS1302是一個實時時鐘晶片,可以提供秒、分、小時、日期、月、年等資訊,並且還有軟年自動調整的能力,可以通過配置AM/PM來決定採用24小時格式還是12小時格式。
2、擁有31位元組資料儲存RAM。
3、序列I/O通訊方式,相對並行來說比較節省IO口的使用。
4、DS1302的工作電壓比較寬,大概是2.0V~5.5V都可以正常工作。採用雙電源供電,當主電源比備用電源高0.2V時,由主電源供電,否則採用備用電源,一般是一個鈕釦電池。
5、DS1302這種時鐘晶片功耗一般都很低,它在工作電壓2.0V的時候,工作電流小於300nA。
6、DS1302共有8個引腳,有兩種封裝形式,一種是DIP-8封裝,晶片寬度(不含引腳)是300mil,一種是SOP-8封裝,有兩種寬度,一種是150mil,一種是208mil。
二、引腳定義
三、外圍電路
一般與微控制器IO口相連時要加上拉電阻,提高 IO 口的驅動能力,這樣訊號比較穩定,計時也比較準確。
四、分析時序圖
這是單位元組寫入的時序圖,可見,先拉高使能端,進行使能選擇,然後在時鐘上升沿寫入一個位元組。
DS1302在進行讀寫操作時最少讀寫兩個位元組,第一個是控制位元組,就是一個命令,說明是讀還是寫操作,第二個時需要讀寫的資料。
對於單位元組寫,只有在SCLK為低電平時才能將 CE 置高電平,所以剛開始將SCLK 置低,CE置高,然後把需要寫入的位元組送入 IO口,然後跳變SCLK,在SCLK下降沿時,寫入資料
五、編寫驅動程式
有了 上面的分析,我們就可以學著編寫驅動程式了,可以把驅動程式分為幾個模組來寫,由底層慢慢往上累加,比如,我們先編寫單個位元組的讀寫操作,在編寫整個資料的讀寫,
#include "DS1302.h" //******************* void ds1302_writebyte(uchar byte){ uint i; uint t = 0x01; for(i=0;i<8;i++){ SCIO = byte & t; t<<=1; DOWN(); //下降沿完成一個位的操作} SCIO = 1;//確保釋放io引腳 } //******************** void ds1302_writedata(uchar addr,uchar data_){ CE = 0; nop(); SCLK = 0; nop(); CE = 1; nop(); //使能片選訊號 ds1302_writebyte((addr<<1)|0x80); //方便後面寫入 ds1302_writebyte(data_); CE = 0; nop();//傳送資料結束 } //************************* uchar ds1302_readbyte(){ uint i; uchar data_ = 0; uint t = 0x01; for(i=0;i<7;i++){ //c51好像不支援直接在for迴圈裡面直接定義變數 if(SCIO){ data_ = data_ | t; //低位在前,逐位讀取,剛開始不對,估計是這個的問題 } t<<=1; DOWN(); } return data_; } //************************ uchar ds1302_readdata(uchar addr){ uchar data_ = 0; CE = 0; nop(); SCLK = 0; nop(); CE = 1; nop(); ds1302_writebyte((addr<<1)|0x81); data_ = ds1302_readbyte(); CE = 0; nop(); SCLK = 1; nop(); SCIO = 0; nop(); SCIO = 1; nop(); return data_; } //********************* void init_ds1302(){ uchar i; CE = 0; //初始化引腳 SCLK = 0; i = ds1302_readdata(0x00); //讀取秒暫存器,秒在最低位 if((i & 0x80 != 0)){ ds1302_writedata(7,0x00); //撤銷防寫,允許寫入資料 for(i = 0;i<7;i++){ ds1302_writedata(i,init_time[i]); } } } //************** void ds1302_readtime(){ //讀取時間 uint i; for(i = 0;i<7;i++){ init_time[i] = ds1302_readdata(i); } }
其中標頭檔案為:
#ifndef __DS1302_H #define __DS1302_H #include "reg52.h" #include "intrins.h" #define uint unsigned int #define uchar unsigned char #define nop() _nop_() #define UP() {SCLK = 0;nop();SCLK = 1;nop();} //上升沿 ,使用巨集定義函式時最後一定家分號 #define DOWN() {SCLK = 1;nop();SCLK = 0;nop();} //下降沿 //這個模組內沒有整合上拉電阻,使用時最好接上2 sbit CE = P2^5;//RET,使能輸入引腳,當讀寫時,置高位 sbit SCIO = P2^6;//IO ,雙向通訊引腳,讀寫資料都是通過這個完成 sbit SCLK = P2^7;//SCLK,時鐘訊號 //為什麼有時候好好的,也會出錯顯示少了分號呢?還氣人啊!!!! void ds1302_writebyte(uchar byte);//寫一個位元組; void ds1302_writedata(uchar addr,uchar data_);//給某地址寫資料,data是c51內部的關鍵字,表示將變數定義在資料儲存區,故此處用data_; uchar ds1302_readbyte();//讀一個位元組 uchar ds1302_readdata(uchar addr);//讀取某暫存器資料 ; void init_ds1302(); void ds1302_readtime(); extern uchar init_time[]; #endif
六、功能實現
功能實現就簡單了,就是加上主函式嘛,然後加上我們可以親眼看見並感知的模組,比如用數碼管顯示時間:
#include "DS1302.h" #define DIG P0 sbit LSA = P2^2; sbit LSB = P2^3; sbit LSC = P2^4; uchar init_time[] = {0x50,0x15,0x14,0x22,0x10,0x06,0x17};//初始化的時間 //秒 分 時 日 月 周 年 uchar code DIG_CODE[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; //數碼管數字表 uint disp[8]={0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f};//把要顯示的數字傳給他 uchar Num=0; uint count_flag = 0; //中斷溢位次數 void time0_init();//定時器0初始化 void display();//數碼管顯示時間 void main(void){ init_ds1302(); //初始化時寫入起始時間 time0_init(); while(1){ display(); } } //*************** void display(){ ds1302_readtime(); //讀取時間 disp[7] = DIG_CODE[init_time[0]&0x0f]; disp[6] = DIG_CODE[init_time[0]>>4]; disp[5] = 0X40; //顯示一個橫線 disp[4] = DIG_CODE[init_time[1]&0x0f]; disp[3] = DIG_CODE[init_time[1]>>4]; disp[2] = 0X40; disp[1] = DIG_CODE[init_time[2]&0x0f]; disp[0] = DIG_CODE[init_time[2]>>4]; } //****************** void time0_init(){ TMOD=0X02;//選擇為定時器模式,工作方式2,8位自動重灌模式,僅用TRX開啟啟動。 TH0=0X9C; //給定時器賦初值,定時100us,0x9c就是156,就是還需計數100次產生溢位,就是0.1ms TL0=0X9C; ET0=1;//開啟定時器0中斷允許 EA=1;//開啟總中斷 TR0=1;//開啟定時器 } void DigDisplay() interrupt 1 //中斷入口函式,掃描以實現動態顯示 { //定時器在工作方式二會自動重灌初,所以不用在賦值。 // TH0=0X9c;//給定時器賦初值,定時0.1ms // TL0=0X00; count_flag++; if(count_flag==1) { count_flag = 0; DIG=0; switch(Num) //位選,選擇點亮的數碼管, { case(7): LSA=0;LSB=0;LSC=0; break; case(6): LSA=1;LSB=0;LSC=0; break; case(5): LSA=0;LSB=1;LSC=0; break; case(4): LSA=1;LSB=1;LSC=0; break; case(3): LSA=0;LSB=0;LSC=1; break; case(2): LSA=1;LSB=0;LSC=1; break; case(1): LSA=0;LSB=1;LSC=1; break; case(0): LSA=1;LSB=1;LSC=1; break; } DIG=disp[Num]; //段選,選擇顯示的數字。 Num++; if(Num>7) Num=0; } }
總結一下:
這個晶片基本上不是很難,但是想要用的靈活,用的上手,還是得多練的,最好是先把上面的驅動程式對著時序圖自己分析一遍,然後自己親手編寫一下。
還有就是看資料手冊,一個晶片所能用到的資料,在資料手冊上基本都能查到。資料誰都能查到,就看怎麼用了。