今天,又花了差不多一天時間,從手冊看起,到寫完代碼,最后仿真。期間出現了一個細小的差錯,折騰了約1個鍾頭才解決掉(所以,最怕底層細小之處出現錯誤)。
DS1302是達拉斯公司出品的一款實時時鍾芯片。具體且詳細的資料介紹在其芯片手冊上面都有寫着。說起來自己讀DS1302芯片手冊,也花了不少精力,主要是對一個問題一直沒理解。
下圖是DS1302時鍾寄存器的結構。(注意左邊READ與WRITE兩列)
下圖是DS1302的命令字節(就是跟它通信的格式)

其中A4到A0可以代表寄存器的地址。我想,5bit表示的地址,翻遍了手冊,在上面也沒有說明5bit的地址是怎么表示的(手冊上只有讀寫地址,8bit)。就這樣,我納悶了,就去問問搜索引擎。網上掛出來最多的盡是些代碼(說實話,驅動代碼不難寫,難的是對芯片的理解),最終還是找到了一篇博文(寫了對DS1302的理解),而且我才煥然大悟,原來READ與WRITE兩列是什么的。
舉個例子:寫秒寄存器的地址是0x80,展開成8bit的就是1000,0000(結合命令字節看看),你會發現其實就是命令字節。
現在來說說DS1302的SPI接口(其實比標准的SPI接口少了一根線),它包含RST線、SCLK線、IO線(雙向傳送數據用,標准的SPI則將其分成兩根MISO與MOSI)3條接線。
上面這張圖片就是其時序圖,單字節讀取和單字節寫。相比1條線的單總線、2條線的IIC,這個SPI貌似是最簡單的。每次傳送時,需要先發送8bit命令字節,再發送/接收8bit數據。
再細小的看,CE(也就是RST)先拉至高電平,在IO線上事先要准備好數據,然后將SCLK拉高,一個上升沿,發完了1bit。如此往復,發完接下來的幾位。如果是發送數據的話,注意每次都是上升沿發送1bit。如果是接收數據的話,注意,在傳完最后命令字節的1(高電平)之后的第一個下降沿后DS1302發送數據。當然DS1302能夠每次傳送多個字節。
說完了通信機制,通信內容,基本上差不多了。值得提醒的是,DS1302內部有31字節RAM,可以用來保存數據。
今天寫的有點少。下面貼上調好的代碼:
頭文件部分:
#ifndef __hal_ds1302_h__ #define __hal_ds1302_h__ #include<reg52.h> #include"datatype.h" #include"delay.h" #include"hal.h" sbit rst=P3^2; sbit sclk=P3^3; sbit sda_ds1302=P1^7; #define RST rst #define SCLK sclk #define SIO sda_ds1302 #define WRITE_SECONDS 0x80//秒 #define READ_SECONDS 0x81 #define WRITE_MINUTES 0x82//分 #define READ_MINUTES 0x83 #define WRITE_HOUR 0x84//時 #define READ_HOUR 0x85 #define WRITE_DATE 0x86//日 #define READ_DATE 0x87 #define WRITE_MONTH 0x88//月 #define READ_MONTH 0x89 #define WRITE_DAY 0x8a//星期 #define READ_DAY 0x8b #define WRITE_YEAR 0x8c//年 #define READ_YEAR 0x8d #define WRITE_WP 0x8e//寫保護bit7為高時,不允許寫 #define READ_WP 0x8f//所以寫之前需將其設為0 //ds1302時間結構體類型 struct ds1302_time { //秒0-59 uchar seconds; //分0-59 uchar minutes; //時0-23(24小時模式) //如果需要12小時模式,跟寄存器格式需要一致 uchar hour; //星期1-7 enum{Sunday=1,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday} day; //日1-31 uchar date; //月1-12 uchar month; //年0-99 uchar year; }; //參數1:寫保護;參數0:取消寫保護 #define HAL_DS1302_WRITE_PROTECT(wp) hal_ds1302_single_byte_write(WRITE_WP,wp?0x80:0x00) void hal_ds1302_init(); void hal_ds1302_single_byte_write(uchar comm,uchar val); uchar hal_ds1302_single_byte_read(uchar comm); void hal_ds1302_init_time(struct ds1302_time time); void hal_ds1302_get_time(struct ds1302_time * time); #endif
C文件部分:
1 #include"hal_ds1302.h" 2 3 void hal_ds1302_init() 4 { 5 RST=0; 6 SCLK=0; 7 SIO=1; 8 } 9 10 //實質上傳送了16bit 11 void hal_ds1302_single_byte_write(uchar comm,uchar val) 12 { 13 //RST低電平 SCLK低電平 14 uchar i; 15 RST=1; 16 for(i=0;i<8;i++)//從低位開始傳送 17 { 18 if(comm&(0x01<<i)) 19 SIO=1; 20 else 21 SIO=0; 22 SCLK=1;//SCLK一個上升沿,讀取了SDA上的電平 23 //單片機時鍾周期1us情況下,幾乎不用考慮延時 24 SCLK=0; 25 } 26 for(i=0;i<8;i++) 27 { 28 if(val&(0x01<<i)) 29 SIO=1; 30 else 31 SIO=0; 32 SCLK=1;//上升沿讀取 33 SCLK=0; 34 } 35 RST=0; 36 //RST低電平 SCLK低電平 SDA電平未知 37 } 38 39 //實質上傳送了16bit 40 uchar hal_ds1302_single_byte_read(uchar comm) 41 { 42 //RST低電平 SCLK低電平 43 uchar i,tmp=0; 44 RST=1; 45 for(i=0;i<8;i++) 46 { 47 SCLK=0;//放在這兒是為了第8次SCLK依舊是高電平 48 if(comm&(0x01<<i)) 49 SIO=1; 50 else 51 SIO=0; 52 SCLK=1;//上升沿,DS1302讀取值 53 } 54 //現在SCLK依舊是高電平 55 //而且達拉斯考慮到了總線釋放問題,所以傳送的最后一位都為1 56 for(i=0;i<8;i++) 57 { 58 SCLK=1;//放在此處是為了i=7時候,SCLK仍為低電平 59 SCLK=0;//下降沿,DS1302輸出數據 60 if(SIO) 61 tmp=tmp|(0x01<<i); 62 } 63 RST=0; 64 return tmp; 65 //RST低電平 SCLK低電平 SDA電平狀態未知 66 } 67 68 //參數char類型16進制 69 //傳出2個4位BCD碼組成的8bit數據 70 uchar hal_ds1302_uchar2BCD(uchar val) 71 { 72 uchar shi=0,ge=0; 73 shi=val/10%10; 74 ge=val%10; 75 return (shi<<4)|ge; 76 } 77 78 uchar hal_ds1302_BCD2uchar(uchar val) 79 { 80 uchar shi=0,ge=0; 81 shi=val>>4; 82 ge=val&0x0f;//小心,就是這兒0x0f寫錯了,不是0xf0 83 return shi*10+ge; 84 } 85 86 //1.設置寫保護位為0 87 //2.設置秒字節,同時將bit7置1(暫停計時) 88 //3.設置年、月、日、星期、時、分 89 //4設置秒字節,晶振起震 90 //5.設置寫保護字節bit7為1 91 //6.之后可以讀取時間了 92 void hal_ds1302_init_time(struct ds1302_time time) 93 { 94 HAL_DS1302_WRITE_PROTECT(0); 95 delay_ms(1); 96 hal_ds1302_single_byte_write(WRITE_SECONDS,0x80); 97 delay_ms(1); 98 hal_ds1302_single_byte_write(WRITE_YEAR,hal_ds1302_uchar2BCD(time.year)); 99 delay_ms(1); 100 hal_ds1302_single_byte_write(WRITE_MONTH,hal_ds1302_uchar2BCD(time.month)); 101 delay_ms(1); 102 hal_ds1302_single_byte_write(WRITE_DATE,hal_ds1302_uchar2BCD(time.date)); 103 delay_ms(1); 104 hal_ds1302_single_byte_write(WRITE_DAY,hal_ds1302_uchar2BCD(time.day)); 105 delay_ms(1); 106 hal_ds1302_single_byte_write(WRITE_HOUR,hal_ds1302_uchar2BCD(time.hour)); 107 delay_ms(1); 108 hal_ds1302_single_byte_write(WRITE_MINUTES,hal_ds1302_uchar2BCD(time.minutes)); 109 delay_ms(1); 110 hal_ds1302_single_byte_write(WRITE_SECONDS,hal_ds1302_uchar2BCD(time.seconds)); 111 delay_ms(1); 112 HAL_DS1302_WRITE_PROTECT(1); 113 } 114 115 //注意對12小時模式沒有進行編寫,如有需要,需要額外修改 116 void hal_ds1302_get_time(struct ds1302_time * time) 117 { 118 time->year=hal_ds1302_BCD2uchar(hal_ds1302_single_byte_read(READ_YEAR)); 119 delay_ms(1); 120 time->month=hal_ds1302_BCD2uchar(hal_ds1302_single_byte_read(READ_MONTH)); 121 delay_ms(1); 122 time->date=hal_ds1302_BCD2uchar(hal_ds1302_single_byte_read(READ_DATE)); 123 delay_ms(1); 124 time->day=hal_ds1302_BCD2uchar(hal_ds1302_single_byte_read(READ_DAY)); 125 delay_ms(1); 126 time->hour=hal_ds1302_BCD2uchar(hal_ds1302_single_byte_read(READ_HOUR)); 127 delay_ms(1); 128 time->minutes=hal_ds1302_BCD2uchar(hal_ds1302_single_byte_read(READ_MINUTES)); 129 delay_ms(1); 130 time->seconds= hal_ds1302_BCD2uchar(hal_ds1302_single_byte_read(READ_SECONDS)); 131 }
