☛ 功能说明

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