오늘 소개할 부품은 DS1302 칩을 사용한 RTC 모듈입니다. 아두이노로 시계와 같이 시간 관련 프로젝트를 진행한다면 꼭 필요한 부품이고, 이러한 제품들중 가장 저렴한 편이며, 라이브러리 및 예제가 아주 많기 때문에 아두이노에 적용하기도 쉽습니다.
아두이노에서 제공하는 millis() 함수는 현재 시간을 리턴해줍니다. 이 시간은 아두이노 보드가 부팅될 때부터 시작해서 전원이 유지되는 한, 계속해서 증가하는 시간입니다. 이러한 "time-keeping" 기능을 이용해서, millis(), micros() 등의 시간 관련 함수들을 제공하는 것입니다. 문제는, 전원 공급이 끊겨 보드 작동이 정지하면 "time-keeping"이 불가능하다는 것입니다. 예를 들어, 아두이노로 시계를 만들었다면, 전원을 켤 때마다, 시간 세팅을 다시 해주어야 합니다.
여기서 사용한 모듈입니다. 가장 저렴한 제품이고, 보드 구성도 단순합니다.
메인 전원이 차단돼도 시간 데이터를 유지하기 위해, 배터리를 삽입하였습니다. CR2032 배터리를 사용하면 되고, 당연히 따로 구매해야 합니다!
핀 구성은 위와 같습니다. 총 5핀중 전원용 2핀 빼면, 세 개의 핀을 아두이노와 연결해야 합니다. I2C도 아니고, SPI도 아니고, 그냥 비어있는 디지털 포트에 연결한 후, 소스코드에서 지정해주면됩니다.
전원까지 모두 아두이노와 연결한 상태입니다. 별도의 LED표시는 없기 때문에, 동작 여부를 눈으로 확인할 수는 없습니다.
배선은 위와 같습니다. 전원도 아두이노에서 가져왔고, D2, D3, D4에 차례로 CLK, DAT, RST를 연결하였습니다. RST는 Reset이고 Clokc Enable(CE)로도 부릅니다.
이제, 아두이노에서 사용할 수 있도록 라이브러리를 설치할 차례입니다. 아래 링크를 통해 다운로드하여 라이브러리에 추가해 줍니다.
zip 압축 파일로 받은 라이브러리는 압축을 해제하지 않고도 바로 설치할 수 있습니다. 아두이노 IDE "스케치"메뉴에서 ".ZIP 라이브러리 추가..." 항목을 찾아 클릭하고, 대화 상자에서 해당 파일을 선택만 해주면 됩니다.
또, 시간 및 날짜 데이터를 다루기 위해, Time.h 라이브러리를 이용하기 때문에, 이것도 찾아 설치해줘야 합니다. 다행히, 따로 찾아 설치하지 않고 "라이브러리 매니저"에서 설치할 수 있습니다.
라이브러리 매니저 화면입니다. 검색창에 "time"이라 입력하여 나온 결과에서 알파벳순으로 위 그림과 같은 Time 라이브러리를 찾아 설치해 줍니다.
#include <TimeLib.h>
#include <DS1302RTC.h>
우선, 필요한 헤더 파일을 포함시킵니다. TimeLib.h는 Time 라이브러리 헤더 파일로 시간, 날짜 관련 데이터를 다룰 수 있도록 해줍니다. 다른 하나는 DS1302 모듈용 라이브러리입니다. 라이브러리 기본 예제에선 Time.h 헤더 파일을 include 하고 있습니다. 만약, Time.h 를 포함한 후 실행시에 무엇인가 선언되지 않았다는 컴파일 오류가 뜨면, 위 코드와 같이 TimeLib.h 파일을 포함시키도록 수정해야 합니다.
#include <TimeLib.h>
#include <DS1302RTC.h>
//
// Set pins: CE, IO,CLK
DS1302RTC RTC(4, 3, 2);
연결된 핀 정보를 인수로 하여 DS1302RTC 개체를 생성합니다. 개체 이름은 RTC입니다.
DS1302RTC모듈은 프로그램 코드를 통해서, 모듈내 Clock의 실행을 멈추고 Standby mode로 진입시키거나 반대로 다시 실행을 시작시킬 수 있습니다. 모듈을 사용하기 위해서, 우선 시계가 작동하고 있는 지부터 체크하도록 하겠습니다.
void setup() {
// Setup Serial connection
Serial.begin(9600);
Serial.println("DS1302RTC Read Test");
Serial.println("-------------------");
// clock 정지 체크
if (RTC.haltRTC()) { // 만약 1 이면 정지된 상태
Serial.println("The DS1302 is stopped.");
} else {
Serial.println("The DS1302 is working.");
}
//
delay(5000);
}
첫 번째 예제는 아두이노가 시작되면, setup() 함수에서 현재 어떤 모드인지 체크하고, standby 모드라면 다시 시작하도록 하는 소스입니다. haltRTC() 함수는 인수없이 사용하면 현재 모드를 반환하는데, "1"이면 정지상태, "0"이면 실행 상태입니다. 반환값이 "1"이면 7번 행 IF문은 "true"이므로, 첫 번째 메시지를 출력하게 됩니다. 우선, 모드를 변경하지 않고 확인하는 코드만 실행해 보겠습니다. 위 코드와 그 위쪽 코드를 그대로 합쳐서 실행하면 됩니다. loop() 함수는 현재 아무 코드도 없습니다.
실행 결과입니다. 제가 사용한 모듈은 현재 정지 상태입니다. 먼저 깨워야겠네요!
RTC.haltRTC(0);
standby mode에서 깨우려면, 위와 같이 함수 인수로 "0"을 넘겨주도록 합니다. 인수가 없을 경우, 현재 상태를 반환하고, "1"이면 정지, "0"이면 실행 상태로 전환합니다.
#include <TimeLib.h>
#include <DS1302RTC.h>
//
// Set pins: CE, IO,CLK
DS1302RTC RTC(4, 3, 2);
//
void setup() {
Serial.begin(9600);
Serial.println("DS1302RTC Read Test");
Serial.println("-------------------");
// clock 정지 체크
if (RTC.haltRTC()) { // 만약 1 이면 정지된 상태
Serial.println("The DS1302 is stopped.");
RTC.haltRTC(0);
Serial.println("The DS1302 is started.");
delay(100);
} else { // 0 이면 실행 상태
Serial.println("The DS1302 is working.");
}
//
delay(5000);
}
void loop() {
// put your main code here, to run repeatedly:
}
위 예제는, standby mode일 때, clock을 깨우도록 코드를 추가하였습니다. 14번행에서 인수 "0"으로 haltRTC() 함수를 호출합니다. 위에서 처음 예제를 통해 이 모듈이 클럭 정지 상태임을 확인했습니다. 따라서, 위 예제를 실행하면 정지되었다는 메시지를 출력한 후, 클럭의 실행을 시작하고, 시작되었다는 메시지를 출력할 것입니다. 만약, 또 한번 더 실행한다면 "is working"라는 메시지가 출력되겠지요!
위 프로그램을 두 번 실행하였습니다. 첫 번째 수행에서, standy mode 이므로 메시지 출력 후 clock을 시작시켰습니다. 두 번째 수행에서는 이미 클럭이 시작되었으므로 두 번째 메시지를 출력합니다.
RTC 모듈은 기준이 되는 시간 데이터를 아두이노에게 제공하는데, 이 시간이 중간중간 변경된다면 문제가 생기겠죠? 그래서, RTC 모듈은 "쓰기 금지" 모드를 제공합니다. 필요한 경우에만 이 모드를 풀고 시간을 변경할 수 있습니다.
예제를 통하여 현재 어떤 모드인지 알아보겠습니다.
if (RTC.writeEN() == 0) {
Serial.println("The DS1302 is write protected.");
} else {
Serial.println("The DS1302 can write.");
}
위 코드를 예제 아래쪽에 추가하여 실행합니다. writeEN() 함수는 인수없이 사용하면 현재 상태를 리턴합니다.
결과 화면입니다. 제가 사용한 모듈은 "쓰기 가능" 상태입니다. RTC 모듈이 관리하는 시간데이터에는 "write protect" 체크 비트가 있습니다. 이 비트가 "1"이면 보호 모드이고, "0"이면 해제된 상태인데, writeEN() 함수는 이 비트값을 뒤집어서(not) 반환합니다. 따라서, 반환값이 "1"이면 해제, "0"이면 금지 모드입니다.
// write protected check
if (RTC.writeEN() == 0) {
Serial.println("The DS1302 is write protected.");
RTC.writeEN(true);
Serial.println("Write protected is released.");
} else {
Serial.println("The DS1302 can write.");
RTC.writeEN(false);
Serial.println("Write protected is started.");
}
쓰기 모드 변환까지 확인하기 위해서 코드를 위와 같이 수정하였습니다. 매번 실행할 때마다 모드가 토글되고 메시지를 출력합니다. 동작을 확인했으면 마지막으로 쓰기 모드가 활성화되도록 합니다. 평소에는 쓰기 금지모드로 두기 위함이고, 함수를 통하여 시간을 설정할 때는 자동으로 금지 모드를 해제하고, 세팅후에는 다시 쓰기 금지모드로 들어갑니다.
이제 RTC 모듈에서 시간 데이터를 불러오는 방법을 알아보겠습니다. RTC 모듈을 이용하여 시간을 유지하는 방법은 두 가지로 구분할 수 있습니다. 그중 하나는 매번 RTC 모듈에서 시간 데이터를 읽어오는 것이고, 다른 하나는 일정 시간마다 불러와 동기화하고 아두이노(Time 라이브러리)에서 이 시간을 관리하는 방법입니다.
time_t t;
tmElements_t tm
//
time_t DS1302RTC::get()
uint8_t DS1302RTC::set(time_t t)
//
uint8_t DS1302RTC::read(tmElements_t &tm)
uint8_t DS1302RTC::write(tmElements_t &tm)
우선, 위 코드를 먼저 이해하고 넘어가야 합니다. 두 개의 데이터형, 두개의 읽기 함수(get, read), 나머지 두 개의 쓰기 함수(set, write)를 보여주고 있는데, 각각 쌍을 이루고 있음을 알 수 있습니다. get() 함수와 set() 함수는 데이터형 time_t 와 관련이 있고, read()와 write() 함수는 tmElements_t type과 관련이 있습니다. 사용법만 약간 다를 뿐, 둘 중 어느 쪽을 사용해도 동일한 결과를 얻을 수 있습니다.
typedef unsigned long time_t;
"time_t"는 Time 라이브러리에서 위와 같이 선언하고 있습니다. 결국 부호없는 long 형 정수를 담는 변수인 것입니다. 따라서, get() 함수는 unsigned long type 정수를 결과로 반환하고, set() 함수는 unsigned long type 정수를 인수로 받습니다. 다시 말하면, get() 함수는 정수값을 반환합니다. 시, 분, 초, 년, 월, 일에 해당하는 데이터가 어떻게 하나의 정수값으로 표현될 수 있을 까요?
Serial.println(RTC.get());
위와 같이 get() 함수의 내용을 바로 시리얼 모니터로 출력해 보면 저의 경우에는 1521553821라는 숫자가 나왔습니다. 이는 기준 시점 이후에 흘러간 초를 카운팅한 숫자입니다. 그리고, 기준 시점은 1970년 1월 1일 0시 0분 0초이며, 이 때부터 1,521,553,821초가 지났다는 뜻입니다. 이 숫자를 바탕으로 연, 월, 일 등을 계산하여 사용하는 것이, get(), set() 함수입니다.
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
"tmElements_t" 타입은 Time 라이브러리의 헤더파일에 보면 위와 같이 정의된 구조체입니다. 각 시간 데이터를 멤버로 갖고 있으며, read(), write() 함수는 이 구조체형 변수를 인수로 받습니다. 즉, read() 함수는 RTC 모듈에서 시간 데이터를 받아, 이 구조체형 변수의 각 멤버에 적당한 값을 저장해주고, write() 함수는 이렇게 적당한 값이 저장된 구조체 변수를 통해 RTC 모듈의 시간을 세팅합니다. 이렇게, DS1302RTC 라이브러리는 Time 라이브러리의 구조를 기반으로 작성되었습니다.
tmElements_t tm; // 시간데이터를 저장하는 변수
//
RTC.read(tm) // 시간데이터를 불러오는 함수
우선, read() 함수부터 살펴보겠습니다. 이제까지 설명한 것처럼 첫 번째 행의 tmElements_t 는 Time 라이브러리에서 정의된 것으로 시간 데이터를 구분하여 담는 구조체입니다. 두 번째 행의 read() 함수는 RTC 모듈에서 시간 데이터를 읽어오는 함수입니다. read() 함수는 tmElements_t 구조체를 인수로 받아서, 시, 분, 초 등을 알아서 구분하여 해당 구조체 변수에 저장해주기 때문에 사용이 아주 편리합니다.
따라서, 위 두 코드만 실행하면, tm 변수에 시간 데이터가 구분되어 저장되고, 프로그램 내에서 쉽게 가져다 사용할 수 있습니다. read() 함수는 실행이 성공하면 "0"을 반환하고, 실패하면 "1"을 반환하므로, 리턴 값을 체크하는 부분이 필요합니다.
tmElements_t tm; // 시간데이터를 저장하는 변수
//
if (RTC.read(tm) == 0) {
Serial.print(tm.Hour);
Serial.print(" : ");
Serial.print(tm.Minute);
Serial.print(" : ");
Serial.println(tm.Second);
Serial.print(tmYearToCalendar(tm.Year));
Serial.print("/");
Serial.print(tm.Month);
Serial.print("/");
Serial.print(tm.Day);
}
시:분:초, 연/월/일 데이터를 출력하는 코드입니다. 위에서 작성한 예제의, setup() 함수내 아래쪽에 덧붙여 실행하였습니다. RTC.read(tm) 값이 "0"이면 읽기 성공이므로 데이터를 시리얼 모니터에 출력합니다.
위 코드중 연도 부분만 약간 다른데, RTC 모듈의 기본 시작 연도는 "1970"년입니다. 따라서, 읽어온 연도값에 항상 "1970"을 더해야만 하고, 이를 위해 tmYearToCalendar() 매크로를 Time 라이브러리에서 제공하고 있습니다. 연도는 RTC 모듈에서 입출력할 때, 꼭 변환을 해야 합니다. 읽어올 때는 1970을 더해주고, 기록할 때는 1970을 빼주는 식입니다. 연도는 뒤쪽 두 자리만 제공할 수도 있는데, 복잡해지므로 항상 2018 처럼 네 자리를 제공해주는 것이 훨씬 좋습니다.
위 예제처럼 매번 RTC 모듈에서 가져온 데이터를 출력할 때, 만약 읽기에 문제가 생길 경우 출력 자체가 불가능해집니다. 일정시간 마다 시간 동기화하여 Time 라이브러리에서 시간 관리를 하도록 한다면, RTC 모듈에서 가져오기가 실패하더라도 이전에 가져온 데이터가 있기 때문에 시간 출력이 가능합니다. Time 라이브러리가 "time-keeping"해오고 있기 때문에 큰 오차는 없을 테니까요!
RTC.get();
Serial.println(RTC.get());
setSyncProvider(RTC.get());
또 다른 읽기 함수 get()을 알아보겠습니다. 첫 번째 행의 RTC.get() 함수는 모듈에서 시간 데이터를 읽어 오는 함수인데, time_t형 데이터를 반환한다는 것이 RTC.read() 함수와는 다릅니다. 위에서 설명한대로 데이터를 자동으로 저장해주지 않으며, 두 번째 행처럼 그 값을 출력해보면 그냥 정수값만 출력됩니다. RTC.get() 함수가 반환하는 값은 1970년 1월 1일 0시 0분 0초를 기준으로 이제까지 지나간 "초"를 계산한 값이기 때문입니다.
세 번째 행이 중요한데, get() 함수로 가져온 정수값을 현재 Time 라이브러리의 시간과 동기화시키는 코드입니다. setSyncProvider() 함수를 이용하여 RTC.get() 함수로 읽어온 시간 데이터를 가지고 Time 라이브러리에서 관리하는 시간 데이터와 일치(Sync)시켜주는 것이며, 이것으로 Time 라이브러리의 시간 데이터도 RTC 모듈과 동일하게 됩니다. 실제로 적용할 때는, 일정 시간마다 싱크하여 그동안 생긴 오차를 잡아주도록 코딩합니다.
setSyncProvider(RTC.get);
if(timeStatus() == timeSet)
Serial.println(" Ok!");
else
Serial.println(" FAIL!");
시간 동기화후에 오류 여부를 체크하는 코드를 더하였습니다. setSyncProvider() 함수는 외부 시간(RTC 모듈의 시간)을 가져와서 동기화를 합니다. 이 때, 정상적으로 세팅된다면 내부적으로 상태값을 "timeSet"이라는 열거형 데이터로 설정합니다. timeStatus() 함수는 이 상태값을 읽어 오는 함수이고, 읽어온 상태값이 timeSet이면 시간 세팅이 정상적으로 이루어졌다고 판단합니다. 만약, RTC 모듈에 시간 세팅을 잘못했다면 이 코드에서 "FAIL"이 뜨게 됩니다.
위 코드를 실행하면, Time 라이브러리에서 동기화된 시간을 관리하고, 언제든 프로그램 내에서 참조할 수 있습니다.
Serial.print(hour());
Serial.print(":");
Serial.print(minute());
Serial.print(":");
Serial.println(second());
Serial.print(year());
Serial.print("/");
Serial.print(month());
Serial.print("/");
Serial.println(day());
시간 동기화 이후엔, 위와 같이 함수를 통하여 바로 참조할 수 있습니다. 연도의 경우도 따로 보정할 필요 없이 바로 참조 가능하구요! 또 위 함수들은 특정 시간 데이터를 인수로 제공하면 그 시간의 시, 분, 초, 연, 월, 일, 요일을 계산하여 반환해 줍니다.
Serial.println(monthStr(month()));
Serial.println(monthShortStr(month()));
Serial.println(dayStr(month()));
Serial.println(dayShortStr(month()));
위 코드는 월 명과 요일 명을 영문으로 제공하는 함수들입니다. 결과는 January, Jan, Sunday, Sun 입니다.
RTC 모듈을 새 시간 데이터로 세팅하기 위해서 우선 세팅할 시간 데이터를 준비해야 합니다. 역시 time_t 데이터를 인수로 하는 set() 함수와 tmElements_t 구조체를 인수로 하는 write() 함수가 있습니다.
//
tmElements_t tm;
우선 wirte()함수 먼저 살펴보겠습니다. RTC.read() 함수를 사용할 때와 마찬가지로 tmElements_t 형 변수를 생성합니다. RTC.read() 함수로 데이터를 읽어온 후, 이 변수에서 시간, 날짜 등을 저장했던 것처럼, 이제 반대로 이 변수에 시간 데이터를 저장한 후 RTC 모듈에 인수로 전달합니다.
tmElements_t tm;
//
tm.Year = CalendarYrToTm(2018);
tm.Month = 3;
tm.Day = 20;
tm.Wday = 3;
tm.Hour = 11;
tm.Minute = 35;
tm.Second = 30;
각 시간 데이터는 정수값으로 할당합니다. read() 일때는 연도 데이터에 1970을 더해 주었고(tmYearToCalendar), write 일때는 반대로 CalendarYrTm() 매크로를 이용해 1970을 빼주도록 합니다. 위와 같이 데이터가 준비되면 이제 RTC.write() 함수를 이용해 RTC 모듈에 기록하면 됩니다. 코드중 tm.Wday는 요일을 세팅하는 것이고, 일요일이 "1"이고 이후로 1씩 증가합니다. 예제에선 화요일이기에 "3"을 입력했습니다.
if (RTC.write(tm) == 0) {
Serial.println("Time is set.");
time_t t = makeTime(tm);
setTime(t);
}
작성한 tm변수를 인수로 하여 RTC.write(tm) 함수를 호출합니다. 리턴값이 "0"이면 성공이며, 시간 세팅이 성공하면 setTime() 함수를 이용해 Time 라이브러리의 시간도 같은 시간 데이터로 세팅합니다. setTime() 함수는 인수로 "time_t"형 데이터를 요구합니다. 그래서, 두 번째 행처럼 tmElements_t형 데이터를 time_t형 데이터로 변환하는 코드를 삽입하였습니다. makeTime() 함수가 바로 그런 기능을 합니다.
#include <TimeLib.h>
#include <DS1302RTC.h>
//
// Set pins: CE, IO,CLK
DS1302RTC RTC(4, 3, 2);
//
void setup() {
Serial.begin(9600);
Serial.println("DS1302RTC Read Test");
Serial.println("-------------------");
// clock 정지 체크
if (RTC.haltRTC()) { // 만약 1 이면 정지된 상태
Serial.println("The DS1302 is stopped.");
RTC.haltRTC(0);
Serial.println("The DS1302 is started.");
delay(100);
} else { // 0 이면 실행 상태
Serial.println("The DS1302 is working.");
}
// write protected check
if (RTC.writeEN() == 0) {
Serial.println("The DS1302 is write protected.");
} else {
Serial.println("The DS1302 can write.");
RTC.writeEN(false);
Serial.println("Write protected is started.");
}
//
tmElements_t tm;
//
tm.Year = CalendarYrToTm(2018);
tm.Month = 3;
tm.Day = 20;
tm.Wday = 3;
tm.Hour = 11;
tm.Minute = 35;
tm.Second = 30;
if (RTC.write(tm) == 0) {
time_t t = makeTime(tm);
setTime(t);
}
//
Serial.print(year());
Serial.print("/");
Serial.print(month());
Serial.print("/");
Serial.print(day());
Serial.print(" - ");
Serial.print(hour());
Serial.print(":");
Serial.print(minute());
Serial.print(":");
Serial.println(second());
//
setSyncProvider(RTC.get);
if(timeStatus() == timeSet)
Serial.println(" Ok!");
else
Serial.println(" FAIL!");
//
Serial.print(year());
Serial.print("/");
Serial.print(month());
Serial.print("/");
Serial.print(day());
Serial.print(" - ");
Serial.print(hour());
Serial.print(":");
Serial.print(minute());
Serial.print(":");
Serial.print(second());
}
void loop() {
// put your main code here, to run repeatedly:
}
위 소스는 write() 함수를 이용하여 시간 세팅을 한 후, setTime()에 의해 세팅된 Time 라이브러리의 시간과 RTC.get() 함수로 RTC 모듈의 시간을 다시 읽어와서 시리얼 모니터에 출력하는 코드입니다.
실행 결과입니다.
write() 함수와 set() 함수의 사용법은 거의 비슷하고 그 순서만 약간 바꾸면 됩니다.
tmElements_t tm;
//
tm.Year = CalendarYrToTm(2018);
tm.Month = 3;
tm.Day = 20;
tm.Hour = 18;
tm.Minute = 50;
tm.Second = 40;
세팅할 데이터 준비도 동일한데, 대신 위 데이터에는 요일(Wday) 설정이 빠져있습니다.
time_t t = makeTime(tm);
//
if (RTC.set(t) == 0) {
setTime(t);
}
위에서 언급했듯이 set() 함수는 time_t 타입 인수를 사용합니다. tm 변수를 makeTime(tm) 함수를 통해 변환하면 되는데, set() 함수 호출 이전에 변환해야 하므로, IF문안에 있던 makeTime() 함수를 위쪽으로 위치만 바꾸면 됩니다. 나머지는 이전 예제와 동일합니다.
#include <TimeLib.h>
#include <DS1302RTC.h>
//
// Set pins: CE, IO,CLK
DS1302RTC RTC(4, 3, 2);
//
void setup() {
Serial.begin(9600);
Serial.println("DS1302RTC Read Test");
Serial.println("-------------------");
// clock 정지 체크
if (RTC.haltRTC()) { // 만약 1 이면 정지된 상태
Serial.println("The DS1302 is stopped.");
RTC.haltRTC(0);
Serial.println("The DS1302 is started.");
delay(100);
} else { // 0 이면 실행 상태
Serial.println("The DS1302 is working.");
}
// write protected check
if (RTC.writeEN() == 0) {
Serial.println("The DS1302 is write protected.");
} else {
Serial.println("The DS1302 can write.");
RTC.writeEN(false);
Serial.println("Write protected is started.");
}
//
tmElements_t tm;
//
tm.Year = CalendarYrToTm(2018);
tm.Month = 3;
tm.Day = 20;
tm.Hour = 18;
tm.Minute = 50;
tm.Second = 40;
time_t t = makeTime(tm);
//
if (RTC.set(t) == 0) {
setTime(t);
}
//
Serial.print(year());
Serial.print("/");
Serial.print(month());
Serial.print("/");
Serial.print(day());
Serial.print(" - ");
Serial.print(hour());
Serial.print(":");
Serial.print(minute());
Serial.print(":");
Serial.println(second());
//
setSyncProvider(RTC.get);
if(timeStatus() == timeSet)
Serial.println(" Ok!");
else
Serial.println(" FAIL!");
//
Serial.print(year());
Serial.print("/");
Serial.print(month());
Serial.print("/");
Serial.print(day());
Serial.print(" - ");
Serial.print(hour());
Serial.print(":");
Serial.print(minute());
Serial.print(":");
Serial.print(second());
}
void loop() {
// put your main code here, to run repeatedly:
}
set() 함수를 이용한 전체 소스입니다.
결과 화면입니다. 데이터를 준비할 때, 요일 값을 일부러 빼버렸는데, 그래도 정상적으로 수행되었습니다. write() 함수는 요일 값이 빠지면 제대로 세팅되지 않습니다. write() 함수 호출시에는 에러가 없지만, 값을 읽어오면 엉뚱한 값이 세팅되는 것을 확인할 수 있습니다. 이와는 달리, set() 함수는 요일이 확실치 않을 때도 사용할 수 있습니다.
이상으로, DS1302 칩을 사용한 RTC 모듈에 대해서 알아보았습니다. 이외에 몇가지 기능이 있지만, 이제까지 설명한 부분만으로도 충분하다 봅니다. RTC 모듈에 대한 글은 나중에 시계를 만들어 보며 다시 써 보겠습니다. 이상입니다.