☛ 功能說明
使用 Arduino 開發板讀取 DS1307 時鐘晶片應用電路的日期時間,並且以 Serial Monitor 視窗顯示日期時間。
時鐘晶片說明如下:
DS1307 時鐘晶片是一款低功耗即時時鐘晶元,可以提供秒、分、小時等資訊,而且每個月的天數能夠自動調整,並且有閏年補償功能。 DS1307 是 Maxim 的串行、I2C 即時時鐘晶元。 主要特性有:
⑴ 工作電壓:主電源電壓 4.5~5.5V,電池電壓 2.0~3.5V。
⑵ 功耗:電池供電、備份模式時<500nA。
⑶ 介面:I2C,最大速率 100kbps。
⑷ 可程式設計方波輸出。
⑸ 電源自動切換、失效檢測。
⑹ 內置56位元組大小、支援電池備份的 RAM。
⑺ 封裝:8-Pin SO / PDIP。
DS1307 時鐘晶片的典型應用電路如下:
☛ 使用材料
Arduino UNO R3 開發板 × 1、電阻 4.7KΩ × 3、DS 1307 晶片 × 1、32.768kHz 石英振蕩器 × 1 。
☛ 電路圖及麵包板接線圖
☛ 程式碼
範例程式碼 ㈠
#include <Wire.h> #include <TimeLib.h> #include <DS1307RTC.h> const char *monthName[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; tmElements_t tm; // tmElements_t 為保存日期和時間的結構體類型 void setup() { bool parse=false; bool config=false; if (getDate(__DATE__) && getTime(__TIME__)) // 獲取編譯時的日期和時間 { parse = true; if (RTC.write(tm)) // 將時間數據寫入 DS1307 RTC 晶片 { config = true; } } Serial.begin(9600); while (!Serial) ; // 等待 Arduino Serial Monitor 視窗 delay(200); if (parse && config) { Serial.print("DS1307 configured Time="); Serial.print(__TIME__); Serial.print(", Date="); Serial.println(__DATE__); } else if (parse) { Serial.println("DS1307 Communication Error :-{"); Serial.println("Please check your circuitry"); } else { Serial.print("Could not parse info from the compiler, Time=\""); Serial.print(__TIME__); Serial.print("\", Date=\""); Serial.print(__DATE__); Serial.println("\""); } } void loop() { tmElements_t tm; if (RTC.read(tm)) //讀出 DS1307 晶片中的時間數據 { Serial.print("Ok, Time = "); print2digits(tm.Hour); Serial.write(':'); print2digits(tm.Minute); Serial.write(':'); print2digits(tm.Second); Serial.print(", Date (D/M/Y) = "); Serial.print(tm.Day); Serial.write('/'); Serial.print(tm.Month); Serial.write('/'); Serial.print(tmYearToCalendar(tm.Year)); Serial.println(); } else { if (RTC.chipPresent()) { Serial.println("The DS1307 is stopped. Please run the SetTime"); Serial.println("example to initialize the time and begin running."); Serial.println(); } else { Serial.println("DS1307 read error! Please check the circuitry."); Serial.println(); } delay(9000); } delay(1000); } bool getTime(const char *str) // 獲取時間數據並存入tm 變數 { int Hour, Min, Sec; if (sscanf(str, "%d:%d:%d", &Hour, &Min, &Sec) != 3) return false; tm.Hour = Hour; tm.Minute = Min; tm.Second = Sec; return true; } bool getDate(const char *str) // 獲取日期數據並存入tm 變數 { char Month[12]; int Day, Year; uint8_t monthIndex; if (sscanf(str, "%s %d %d", Month, &Day, &Year) != 3) return false; for (monthIndex = 0; monthIndex < 12; monthIndex++) { if (strcmp(Month, monthName[monthIndex]) == 0) break; } if (monthIndex >= 12) return false; tm.Day = Day; tm.Month = monthIndex + 1; tm.Year = CalendarYrToTm(Year); return true; } void print2digits(int number) { if (number >= 0 && number < 10) { Serial.write('0'); } Serial.print(number); }
上述程式碼中的 __DATE__ 和 __TIME__ 是系統常量,當編譯程式時,它們會自動替換為當前的日期和時間,再通過 getTime() 和 getDate() 這兩個函數將日期和時間數據存入 tm 中。
而 tm 是一個 tmElements_t 類型的結構體,其定義在 TimeLib.h 中,內容如下:
typedef struct { uint8_t Second; uint8_t Minute; uint8_t Hour; uint8_t Wday; // day of week, sunday is day 1 uint8_t Day; uint8_t Month; uint8_t Year; // offset from 1970; } tmElements_t, TimeElements, *tmElementsPtr_t;
當日期和時間數據存入 tm 後,就可以通過 tm. Month、tm. Year 等操作得到數據了。
想要將數據寫入 DS1307 中儲存,還需調用 RTC.write(tm) 語句,如果返回 true,則說明數據寫入成功。
設置好 DS1307 的時間后,要想讀出其中的數據,可以使用 RTC.read(tm) 語句,讀出的時間數據會儲存在參數 tm 中。 當 read() 函數成功地讀取數據後會返回 true,讀取失敗則會返回 false。
程式中的 RTC.chipPresent() 是一個錯誤檢查語句,可以根據錯誤提示重新設置 DS1307 的數據,或是檢測硬體的電路連結是否正確。
範例程式碼 ㈡
/* real time clock using DS1307 function: set the time */ #include <Wire.h> #define ADDRESS_DS1307 0x68 byte timeDec[] = {15, 1, 15, 5, 19, 20, 0}; byte timeBcd[] = {0, 0, 0, 0, 0, 0, 0}; //time = {year, month, date, day, hours, minutes, seconds}; void setup() { Wire.begin(); Serial.begin(9600); //convert decimal to BCD code int i; for (i = 0; i < 7; i++) { timeBcd[i] = DecToBcd(timeDec[i]); } //set the time Wire.beginTransmission(ADDRESS_DS1307); Wire.write(0x00); for (i = 0; i < 7; i++) { Wire.write(timeBcd[6-i]); } Wire.endTransmission(); Serial.println("Time has been set."); } void loop() { //read the time Wire.beginTransmission(ADDRESS_DS1307); Wire.write(0x00); Wire.endTransmission(); Wire.requestFrom(ADDRESS_DS1307, 7); if (Wire.available() >= 7) { for (int i = 0; i < 7; i++) { timeBcd[6-i] = Wire.read(); } } //print Serial.print("20"); Serial.print(timeBcd[0], HEX); Serial.print("/"); Serial.print(timeBcd[1], HEX); Serial.print("/"); Serial.print(timeBcd[2], HEX); Serial.print(" "); Serial.print(BcdToDay(timeBcd[3])); Serial.print(" "); Serial.print(timeBcd[4], HEX); Serial.print(":"); Serial.print(timeBcd[5], HEX); Serial.print(":"); Serial.print(timeBcd[6], HEX); Serial.println(); delay(1000); } // Convert normal decimal numbers to binary coded decimal byte DecToBcd(byte val) { byte res; if ((val <= 99) && (val >= 0)) { res = ((val/10)<<4) | (val%10); } else { res = 0; Serial.println("Error"); } return res; } // Convert binary coded decimal to normal decimal numbers byte BcdToDec(byte val) { byte res; if (val <= 0x99) { res = (val >> 4)*10 + (val & 0x0F); } else { res = 0; Serial.println("Error"); } return res; } // Convert binary coded decimal to day String BcdToDay(byte val) { String res; switch(val) { case 1: res = "Sunday"; break; case 2: res = "Monday"; break; case 3: res = "Tuesday"; break; case 4: res = "Wednesday"; break; case 5: res = "Thursday"; break; case 6: res = "Friday"; break; case 7: res = "Saturday"; break; default: res = "Error!"; } return res; }