☛ 功能說明

使用 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;
}