Arduino와 DS3231 정밀 RTC 모듈 인터페이스
프로젝트에 사용하는 대부분의 MCU는 시간에 구애받지 않습니다. 간단히 말해 그들은 주변의 시간을 알지 못합니다. 대부분의 프로젝트에서는 괜찮지 만 시간을 유지하는 것이 주요 관심사라는 아이디어를 발견하면 DS3231 Precision RTC 모듈이 구세주가됩니다. 데이터 로깅, 시계 구축, 타임 스탬프, 타이머 및 알람이 포함 된 프로젝트에 적합합니다.
DS3231 RTC 칩
모듈의 핵심은 Maxim – DS3231의 저비용, 매우 정확한 RTC 칩입니다 . 모든 시간 기록 기능을 관리하고 원하는 마이크로 컨트롤러와 쉽게 인터페이스 할 수있는 간단한 2 선 I2C 인터페이스를 갖추고 있습니다.
이 칩은 초, 분,시, 일, 날짜, 월 및 연도 정보를 유지합니다. 월말의 날짜는 윤년 수정 (최대 2100 년까지 유효)을 포함하여 31 일 미만의 달로 자동 조정됩니다.
시계는 AM / PM 표시기가있는 24 시간 또는 12 시간 형식으로 작동합니다. 또한 두 개의 프로그래밍 가능한 시간 알람을 제공합니다.
이 보드의 다른 멋진 기능은 1Hz, 4kHz, 8kHz 또는 32kHz에서 멋진 구형파를 출력하고 프로그래밍 방식으로 처리 할 수있는 SQW 핀과 함께 제공됩니다. 이것은 많은 시간 기반 애플리케이션에서 알람 조건으로 인한 인터럽트로 더 사용될 수 있습니다.
온도 보상 형 수정 발진기 (TCXO)
대부분의 RTC 모듈은 시간 유지를 위해 외부 32kHz 크리스탈과 함께 제공됩니다. 그러나 이러한 수정의 문제는 외부 온도가 진동 주파수에 영향을 미칠 수 있다는 것입니다. 이 빈도의 변화는 무시할 수 있지만 확실히 합산됩니다.
크리스털에서 이러한 약간의 드리프트를 피하기 위해 DS3231은 32kHz 온도 보상 크리스털 발진기 (TCXO) 에 의해 구동됩니다 . 외부 온도 변화에 매우 영향을받지 않습니다.
TCXO는 RTC 칩 내부에 패키징되어 전체 패키지가 부피가 커집니다. 통합 크리스탈 바로 옆에는 온도 센서가 있습니다.
이 센서는 클럭 틱을 추가하거나 제거하여 주파수 변화를 보정하여 시간이 제대로 유지되도록합니다.
이것이 TCXO가 안정적이고 정확한 기준 클록을 제공하고 RTC를 연간 ± 2 분의 정확도로 유지하는 이유입니다.
DS3231 대 DS1307
DS3231과 DS1370의 주요 차이점은 시간 측정의 정확성입니다.
DS1307은 외부 온도의 영향을 받기 쉬운 발진 주파수를 갖는 시간 유지를 위해 외부 32kHz 크리스탈과 함께 제공됩니다. 일반적으로 시계가 한 달에 약 5 분 정도 떨어져서 발생합니다.
그러나 DS3231은 온도에 영향을받지 않는 내부 온도 보상 수정 발진기 (TCXO)가 포함되어있어 훨씬 더 정확하여 1 년에 최대 몇 분까지 정확합니다.
DS1307은 여전히 훌륭한 가치의 RTC이며 사용자에게 잘 제공되지만보다 정확한 시간 유지가 필요한 프로젝트의 경우 DS3231이 권장됩니다.
배터리 백업
DS3231은 배터리 입력을 통합하고 장치의 주 전원이 중단 될 때 정확한 시간을 유지합니다.
내장 된 전력 감지 회로는 VCC의 상태를 지속적으로 모니터링하여 정전을 감지하고 자동으로 백업 전원으로 전환합니다. 따라서 정전에 대해 걱정할 필요가 없으며 MCU는 여전히 시간을 추적 할 수 있습니다.
보드의 아래쪽에는 20mm 3V 리튬 코인 셀용 배터리 홀더가 있습니다. 모든 CR2032 배터리는 잘 맞습니다.
220mAh 용량의 완전히 충전 된 CR2032 배터리를 사용하고 칩이 최소 3µA를 소비한다고 가정하면 배터리는 외부 5V 전원 공급 장치 없이도 RTC를 최소 8 년 동안 계속 실행할 수 있습니다.
220mAh / 3µA = 73333.34 시간 = 3055.56 일 = 8.37 년
온보드 24C32 EEPROM
DS3231 RTC 모듈은 무제한 읽기-쓰기주기를 갖는 Atmel의 32 바이트 24C32 EEPROM 칩과 함께 제공됩니다. 설정 등을 저장하는 데 사용할 수 있습니다.
24C32 EEPROM은 통신을 위해 I2C 인터페이스를 사용하고 DS3231과 동일한 I2C 버스를 공유합니다.
EEPROM의 I2C 주소는 뒷면에있는 3 개의 A0, A1 및 A2 솔더 점퍼를 사용하여 쉽게 변경할 수 있습니다. 이들 각각은 주소를 하드 코딩하는 데 사용됩니다. 점퍼가 땜납으로 단락되면 주소가 설정됩니다.
24C32의 데이터 시트에 따라이 3 비트는 읽기 / 쓰기 비트 바로 앞의 7 비트 I2C 주소 끝에 배치됩니다.
HIGH / LOW의 2 가지 상태를 취할 수있는 3 개의 주소 입력이 있으므로 8 (2 3 ) 개의 서로 다른 조합 (주소)을 생성 할 수 있습니다 .
팁
기본적으로 3 개의 주소 입력은 모두 온보드 풀업을 사용하여 HIGH로 풀링되어 24C32에 기본 I2C 주소 인 1010111 Binary 또는 0x57 Hex를 제공 합니다.
솔더 점퍼를 단락시킴으로써 주소 입력은 LOW가됩니다. 아래 표에 따라 I2C 주소를 설정할 수 있습니다.
온보드 24C32 EEPROM 읽기 / 쓰기를위한 코드 는 튜토리얼 끝에 제공됩니다 .
DS3231 RTC 모듈 핀아웃
DS3231 RTC 모듈에는 외부 세계와 인터페이스하는 총 6 개의 핀이 있습니다. 연결은 다음과 같습니다.
32K 핀은 안정적이고 (온도 보상 된) 정확한 레퍼런스 클럭을 출력합니다.
SQW핀은 1Hz, 4kHz, 8kHz 또는 32kHz에서 멋진 구형파를 출력하며 프로그래밍 방식으로 처리 할 수 있습니다. 이것은 많은 시간 기반 애플리케이션에서 알람 조건으로 인한 인터럽트로 더 사용될 수 있습니다.
SCL I2C 인터페이스 용 직렬 클록 핀입니다.
SDA I2C 인터페이스 용 직렬 데이터 핀입니다.
VCC핀은 모듈에 전원을 공급합니다. 3.3V에서 5.5V 사이의 어느 곳이든 가능합니다.
GND 접지 핀입니다.
Arduino UNO에 DS3231 RTC 모듈 배선
RTC를 Arduino에 연결해 보겠습니다.
연결은 매우 간단합니다. VCC 핀을 Arduino의 5V 출력에 연결하고 GND를 접지에 연결하여 시작하십시오.
이제 I2C 통신에 사용되는 핀이 남아 있습니다. 각 Arduino 보드에는 그에 따라 연결되어야하는 다른 I2C 핀이 있습니다. R3 레이아웃이있는 Arduino 보드에서 SDA (데이터 라인) 및 SCL (클록 라인)은 AREF 핀에 가까운 핀 헤더에 있습니다. A5 (SCL) 및 A4 (SDA)라고도합니다.
메가가 있다면 핀이 다릅니다! 디지털 21 (SCL) 및 20 (SDA)을 사용하는 것이 좋습니다. 빠른 이해를 위해 아래 표를 참조하십시오.
보드종류 | SCL | SDA |
Arduino Uno | A5 | A4 |
Arduino Nano | A5 | A4 |
Arduino Mega | 21 | 20 |
레오나르도 / 마이크로 | 삼 | 2 |
다음 다이어그램은 모든 것을 연결하는 방법을 보여줍니다.
Arduino와 DS3231 RTC 모듈 배선
RTClib 라이브러리 설치
RTC 모듈과 통신하는 것은 많은 작업입니다. 다행히 RTClib 라이브러리 는 모든 복잡성을 숨기도록 작성되어 RTC 데이터를 읽는 간단한 명령을 실행할 수 있습니다.
라이브러리를 설치하려면 Sketch> Include Library> Manage Libraries…로 이동하여 Library Manager가 라이브러리 색인을 다운로드하고 설치된 라이브러리 목록을 업데이트 할 때까지 기다립니다.
' rtclib ' 를 입력하여 검색을 필터링하십시오 . 몇 개의 항목이 있어야합니다. Adafruit의 RTClib를 찾으십시오. 해당 항목을 클릭 한 다음 설치를 선택합니다.
Arduino 코드 – 날짜 및 시간 읽기
다음 스케치는 DS3231 RTC 모듈에서 날짜 및 시간을 설정 / 읽는 방법에 대한 완전한 이해를 제공하며보다 실용적인 실험 및 프로젝트의 기반이 될 수 있습니다.
#include <Wire.h>
#include "RTClib.h"
RTC_DS3231 rtc;
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
void setup ()
{
Serial.begin(9600);
delay(3000); // wait for console opening
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1);
}
if (rtc.lostPower()) {
Serial.println("RTC lost power, lets set the time!");
// Comment out below lines once you set the date & time.
// Following line sets the RTC to the date & time this sketch was compiled
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
// Following line sets the RTC with an explicit date & time
// for example to set January 27 2017 at 12:56 you would call:
// rtc.adjust(DateTime(2017, 1, 27, 12, 56, 0));
}
}
void loop ()
{
DateTime now = rtc.now();
Serial.println("Current Date & Time: ");
Serial.print(now.year(), DEC);
Serial.print('/');
Serial.print(now.month(), DEC);
Serial.print('/');
Serial.print(now.day(), DEC);
Serial.print(" (");
Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);
Serial.print(") ");
Serial.print(now.hour(), DEC);
Serial.print(':');
Serial.print(now.minute(), DEC);
Serial.print(':');
Serial.print(now.second(), DEC);
Serial.println();
Serial.println("Unix Time: ");
Serial.print("elapsed ");
Serial.print(now.unixtime());
Serial.print(" seconds/");
Serial.print(now.unixtime() / 86400L);
Serial.println(" days since 1/1/1970");
// calculate a date which is 7 days & 30 seconds into the future
DateTime future (now + TimeSpan(7,0,0,30));
Serial.println("Future Date & Time (Now + 7days & 30s): ");
Serial.print(future.year(), DEC);
Serial.print('/');
Serial.print(future.month(), DEC);
Serial.print('/');
Serial.print(future.day(), DEC);
Serial.print(' ');
Serial.print(future.hour(), DEC);
Serial.print(':');
Serial.print(future.minute(), DEC);
Serial.print(':');
Serial.print(future.second(), DEC);
Serial.println();
Serial.println();
delay(1000);
}
다음은 직렬 모니터에서 출력이 어떻게 보이는지입니다.
코드 설명 :
스케치는 모듈과의 통신을 위해 wire.h 및 RTClib.h 라이브러리를 포함하는 것으로 시작됩니다. 그런 다음 RTClib 라이브러리의 객체를 만들고 daysOfTheWeek요일 정보를 저장할 2D 문자 배열을 정의 합니다.
코드의 설정 및 루프 섹션에서 다음 함수를 사용하여 RTC 모듈과 상호 작용합니다.
begin () 함수는 RTC 모듈이 연결되었는지 확인합니다.
lostPower () 함수는 DS3231의 내부 I2C 레지스터를 읽어 칩이 시간 추적을 잃었는지 확인합니다. 함수가 true를 반환하면 날짜와 시간을 설정할 수 있습니다.
adjust () 함수는 날짜 및 시간을 설정합니다. 이것은 오버로드 된 함수입니다.
- 오버로드 된 방법 중 하나 DateTime(F(__DATE__), F(__TIME__))는 스케치가 컴파일 된 날짜와 시간을 설정합니다.
- 두 번째 오버로드 된 메서드 DateTime(YYYY, M, D, H, M, s)는 명시적인 날짜 및 시간으로 RTC를 설정합니다. 예를 들어 2017 년 1 월 27 일을 12:56으로 설정하려면 다음을 호출합니다.rtc.adjust(DateTime(2017, 1, 27, 12, 56, 0));
now () 함수는 현재 날짜 및 시간을 반환합니다. 반환 값은 일반적으로 DateTime 데이터 유형의 변수에 저장됩니다.
year () 함수는 현재 연도를 반환합니다.
month () 함수는 현재 월을 반환합니다.
day () 함수는 현재 날짜를 반환합니다.
dayOfTheWeek () 함수는 현재 요일을 반환합니다. 이 함수는 일반적으로 위 프로그램에서 정의한 것과 같은 요일 정보를 저장하는 2D 문자 배열의 인덱스로 사용됩니다.daysOfTheWeek
hour () 함수는 현재 시간을 반환합니다.
minute () 함수는 현재 분을 반환합니다.
second () 함수는 현재 초를 반환합니다.
unixtime () 함수는 유닉스 시간을 초 단위로 반환합니다. Unix 시간은 특정 시점을 설명하는 시스템입니다. 00:00:00 (협정 세계시 – 1970 년 1 월 1 일 목요일) 이후 경과 된 초 수입니다.
TimeSpan () 함수는 현재 시간에서 시간을 더하거나 빼는 데 사용됩니다. 일, 시간, 분 및 초를 더하거나 뺄 수 있습니다. 오버로드 된 함수이기도합니다.
- now() + TimeSpan(seconds) 현재 시간에 초를 더한 미래 시간을 반환합니다.
- now() - TimeSpan(days,hours, minutes, seconds) 과거 시간을 반환합니다.
Arduino 코드 – 24C32 EEPROM에서 읽기 / 쓰기
DS3231 RTC 모듈을 사용하면 32 바이트의 전기적으로 지울 수있는 ROM을 얻을 수 있습니다. 장치의 주전원이 차단 되어도 내용은 지워지지 않습니다.
다음 프로그램은 24C32 EEPROM에서 메시지를 쓰고 읽습니다. 이 프로그램을 사용하여 설정이나 암호 등을 저장할 수 있습니다.
#include <Wire.h>
void setup()
{
char somedata[] = "lastminuteengineers.com"; // data to write
Wire.begin(); // initialise the connection
Serial.begin(9600);
Serial.println("Writing into memory...");
// write to EEPROM
i2c_eeprom_write_page(0x57, 0, (byte *)somedata, sizeof(somedata));
delay(100); //add a small delay
Serial.println("Memory written");
}
void loop()
{
Serial.print("Reading memory: ");
int addr=0; //first address
// access the first address from the memory
byte b = i2c_eeprom_read_byte(0x57, 0);
while (b!=0)
{
Serial.print((char)b); //print content to serial port
addr++; //increase address
b = i2c_eeprom_read_byte(0x57, addr); //access an address from the memory
}
Serial.println(" ");
delay(2000);
}
void i2c_eeprom_write_byte( int deviceaddress, unsigned int eeaddress, byte data ) {
int rdata = data;
Wire.beginTransmission(deviceaddress);
Wire.write((int)(eeaddress >> 8)); // MSB
Wire.write((int)(eeaddress & 0xFF)); // LSB
Wire.write(rdata);
Wire.endTransmission();
}
// WARNING: address is a page address, 6-bit end will wrap around
// also, data can be maximum of about 30 bytes, because the Wire library has a buffer of 32 bytes
void i2c_eeprom_write_page( int deviceaddress, unsigned int eeaddresspage, byte* data, byte length ) {
Wire.beginTransmission(deviceaddress);
Wire.write((int)(eeaddresspage >> 8)); // MSB
Wire.write((int)(eeaddresspage & 0xFF)); // LSB
byte c;
for ( c = 0; c < length; c++)
Wire.write(data[c]);
Wire.endTransmission();
}
byte i2c_eeprom_read_byte( int deviceaddress, unsigned int eeaddress ) {
byte rdata = 0xFF;
Wire.beginTransmission(deviceaddress);
Wire.write((int)(eeaddress >> 8)); // MSB
Wire.write((int)(eeaddress & 0xFF)); // LSB
Wire.endTransmission();
Wire.requestFrom(deviceaddress,1);
if (Wire.available()) rdata = Wire.read();
return rdata;
}
// maybe let's not read more than 30 or 32 bytes at a time!
void i2c_eeprom_read_buffer( int deviceaddress, unsigned int eeaddress, byte *buffer, int length ) {
Wire.beginTransmission(deviceaddress);
Wire.write((int)(eeaddress >> 8)); // MSB
Wire.write((int)(eeaddress & 0xFF)); // LSB
Wire.endTransmission();
Wire.requestFrom(deviceaddress,length);
int c = 0;
for ( c = 0; c < length; c++ )
if (Wire.available()) buffer[c] = Wire.read();
}
다음은 직렬 모니터에서 출력이 어떻게 보이는지입니다.
lastminuteengineers.com/ds3231-rtc-arduino-tutorial/