【藍橋杯單片機12】實時時鍾DS1302的基本操作
www.xmf393.com / 廣東職業技術學院 歐浩源
實時時鍾DS1302幾乎是藍橋杯“單片機設計與開發”每年必考的內容,雖然在競賽現場有提供一個底層讀寫寄存器的庫文件,但是作為備賽階段,你應該搞清楚底層讀寫時序的代碼實現。你會使用庫文件開發,不一定會自己寫底層;你會自己寫驅動,就一定會使用庫文件開發。你使用庫文件開發的過程中碰到問題,或者需要調整時序的時候,如果沒有過硬的功夫,那只能懵逼了。
1、什么是DS1302?
DS1302是美國DALLAS公司推出的高性能、低功耗的實時時鍾,附加31字節的靜態RAM,采用SP三線接口與MCU進行同步通信,並可采用突發方式一次傳送多個字節的時鍾參數和RAM數據。實時時鍾可提供秒、分、時、日、星期、月和年,一個月小於31天時可以自動調整,並具有潤年補償功能。
簡單來說,DS1302可以理解為一個電子手表,里面帶有一個31字節的內存。當然,基本的使用方法和我們平時使用電子手表差不多,你可以設定時間,也可以讀取時間,只不過這些工作是通過SPI接口有MCU去完成而已。
在DS1302中有兩塊存儲器:日歷時鍾寄存器和今天RAM存儲器。前者用於記錄實時時間,后者用於記錄其他數據。對於基本計時應用,重點關注的是日歷時鍾寄存器。設定時間參數就是往這些寄存器寫入內容,讀取實時時間也是從這些寄存器讀出數據。
2、日歷時鍾寄存器
DS1302有關日歷和時鍾的寄存器有12個,我們最常用的有7個。

什么是BCD碼?
就是用十六進制來表示十進制。什么意思?怎么理解?
例如,十六進制數0x13的值為整數19,但BCD碼表示的是整數13。
3、控制字的格式
DS1302將地址和讀寫控制放到一個字節里面,形成一個控制字,格式如下:

通過上面的控制字格式,大家就可以明白為什么DS1302讀寄存器和寫寄存器的地址是不一樣的了,因為這個地址包含了讀寫控制位。為了方便程序設計,我們把讀寄存器地址、寫寄存器地址和日歷時鍾寄存器方面用三個數組定義。

4、接口時序的實現
DS1302的基本操作實際上非常簡單,只有兩個操作:其一是設定時間參數,其二是讀取實時時間。不管是那個操作,MCU都要通過SPI接口進行數據交互,而SPI接口有其規定的時序,這個必須參考數據手冊。
控制字總是從最低位開始輸出。在控制字指令輸入后的下一個SCLK時鍾信號的上升沿,數據被寫入DS1302,數據的輸入從最低位開始;在控制字指令輸入后的下一個SCLK時鍾信號的下降沿,數據從DS1302讀出,數據的讀出也是從最低位到最高位。
<1> 單字節寫的時序

底層驅動代碼實現可參考如下:
void DS1302_WriteByte(unsigned char addr, unsigned char dat) { unsigned char n; RST = 0; _nop_(); SCLK = 0; _nop_(); RST = 1; _nop_(); for (n=0; n<8; n++) //發送要寫入數據的內存地址 { DSIO = addr & 0x01; addr >>= 1; SCLK = 1; _nop_(); SCLK = 0; _nop_(); } for (n=0; n<8; n++) //將指定內容寫入該地址的內存 { DSIO = dat & 0x01; dat >>= 1; SCLK = 1; _nop_(); SCLK = 0; _nop_(); } RST = 0; _nop_(); }
<2> 單字節讀的時序

底層驅動代碼實現可參考如下:
unsigned char DS1302_ReadByte(unsigned char addr) { unsigned char n,dat,tmp; RST = 0; _nop_(); SCLK = 0; _nop_(); RST = 1; _nop_(); for(n=0; n<8; n++) //發送要讀出數據的內存地址 { DSIO = addr & 0x01; addr >>= 1; SCLK = 1; _nop_(); SCLK = 0; _nop_(); } for(n=0; n<8; n++) //讀出該地址內存的數據 { tmp = DSIO; dat = (dat>>1) | (tmp<<7); SCLK = 1; _nop_(); SCLK = 0; _nop_(); } RST = 0; _nop_(); SCLK = 1; _nop_(); DSIO = 0; _nop_(); DSIO = 1; _nop_(); return dat; }
有了上面兩個底層的SPI接口數據讀寫代碼,那么DS1302的基本操作就很容易實現了。

5、單元實訓題目

6、實現源碼參考
#include "reg52.h" #include "intrins.h" sbit HC138_A = P2^5; sbit HC138_B = P2^6; sbit HC138_C = P2^7; sbit SCLK = P1^7; sbit RST = P1^3; sbit DSIO = P2^3; 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] = {0x30, 0x50, 0x23, 0x17, 0x02, 0x06, 0x18}; unsigned char code SMG_NoDot[18] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8, 0x80,0x90,0x88,0x80,0xc6,0xc0,0x86,0x8e, 0xbf,0x7f}; void DelaySMG(unsigned int time) { while(time--); } void Init74HC138(unsigned char n) { switch(n) { case 4: HC138_A = 0; HC138_B = 0; HC138_C = 1; break; case 5: HC138_A = 1; HC138_B = 0; HC138_C = 1; break; case 6: HC138_A = 0; HC138_B = 1; HC138_C = 1; break; case 7: HC138_A = 1; HC138_B = 1; HC138_C = 1; break; case 8: HC138_A = 0; HC138_B = 0; HC138_C = 0; break; } } void DispaySMG_Bit(unsigned char value, unsigned char pos) { Init74HC138(6); P0 = (0x01 << pos); Init74HC138(7); P0 = value; } void DS1302_WriteByte(unsigned char addr, unsigned char dat) { unsigned char n; RST = 0; _nop_(); SCLK = 0; _nop_(); RST = 1; _nop_(); for (n=0; n<8; n++) { DSIO = addr & 0x01; addr >>= 1; SCLK = 1; _nop_(); SCLK = 0; _nop_(); } for (n=0; n<8; n++) { DSIO = dat & 0x01; dat >>= 1; SCLK = 1; _nop_(); SCLK = 0; _nop_(); } RST = 0; _nop_(); } unsigned char DS1302_ReadByte(unsigned char addr) { unsigned char n,dat,tmp; RST = 0; _nop_(); SCLK = 0; _nop_(); RST = 1; _nop_(); for(n=0; n<8; n++) { DSIO = addr & 0x01; addr >>= 1; SCLK = 1; _nop_(); SCLK = 0; _nop_(); } for(n=0; n<8; n++) { tmp = DSIO; dat = (dat>>1) | (tmp<<7); SCLK = 1; _nop_(); SCLK = 0; _nop_(); } RST = 0; _nop_(); SCLK = 1; _nop_(); DSIO = 0; _nop_(); DSIO = 1; _nop_(); return dat; } void DS1302_Config() { unsigned char n; DS1302_WriteByte(0x8E,0x00); for (n=0; n<7; n++) { DS1302_WriteByte(WRITE_RTC_ADDR[n],TIME[n]); } DS1302_WriteByte(0x8E,0x80); } void DS1302_ReadTime() { unsigned char n; for (n=0; n<7; n++) { TIME[n] = DS1302_ReadByte(READ_RTC_ADDR[n]); } } void XMF_ShowRealTime() { DispaySMG_Bit(SMG_NoDot[TIME[2]/16],0); DelaySMG(500); DispaySMG_Bit(0xff,0); DispaySMG_Bit(SMG_NoDot[TIME[2]&0x0f],1); DelaySMG(500); DispaySMG_Bit(0xff,1); DispaySMG_Bit(SMG_NoDot[16],2); DelaySMG(500); DispaySMG_Bit(0xff,2); DispaySMG_Bit(SMG_NoDot[TIME[1]/16],3); DelaySMG(500); DispaySMG_Bit(0xff,3); DispaySMG_Bit(SMG_NoDot[TIME[1]&0x0f],4); DelaySMG(500); DispaySMG_Bit(0xff,4); DispaySMG_Bit(SMG_NoDot[16],5); DelaySMG(500); DispaySMG_Bit(0xff,5); DispaySMG_Bit(SMG_NoDot[TIME[0]/16],6); DelaySMG(500); DispaySMG_Bit(0xff,6); DispaySMG_Bit(SMG_NoDot[TIME[0]&0x0f],7); DelaySMG(500); DispaySMG_Bit(0xff,7); } main() { DS1302_Config(); while(1) { DS1302_ReadTime(); XMF_ShowRealTime(); } }
