이 포스트는 DS3231 에 대한 이야기 이며, 후속편에 이어집니다.
* Hardware | RTC DS3231 부품 사용기 - 2
- http://chocoball.tistory.com/entry/Hardware-RTC-usning-DS3231-2
1. RTC
보통 internet 이 달린 기기라면 NTP Server 와 연동하여 시간을 맞추고,
특정 시간에 정확하게 일을 시킬 수가 있습니다.
Internet 에 연결되지 않은 기기의 경우는 다음과 같은 과정이 필요하겠죠.
A) 시간을 설정한다.
B) 시간을 기억한다.
C) 특정 시간에 일을 시킨다.
Internet 이 연결되지 않은 기기와 비교해 보면 B) 항목이 필요합니다.
이 "시간을 기억" 하고, 언제든지 현재 시간 정보를 가져올 수 있는 부품이 DS3231 입니다.
그래서 이번에는 DS3231 을 구입하고 사용해 보겠습니다.
자세한 내용은 아래 Arduino 사이트를 참조해 보세요.
* RTC Library
- https://www.arduino.cc/en/Reference/RTC
2. 구입
말할것도 없이 AliExpress 입니다.
배송까지 40일 걸렸습니다. 이정도 되면 배송에 대해서는 해탈해야 합니다.
* 1PCS DS3231 AT24C32 IIC Precision RTC Real Time Clock Memory Module For Arduino new original
건전지 미포함 1.06 USD 무료배송이면 고민거리는 아닙니다.
(배송기간 빼고)
인터넷을 뒤지니, 위의 부품으로 거의 통일되어 있는것 같았습니다.
3. 도착
요로코롬 도착했습니다.
부품 확대 사진입니다.
메인 chip 에 DS321 이라고 적혀 있네요.
발진기인 오실레이터도 보이고, 밑에는 AT24C32 EEPROM (32Kb) 도 있습니다.
참고로 DS3231 칩 안에는 추가로 온도센서도 존재합니다.
- Data sheet : DS3232.pdf
특이하게 "왜 온도센서?" 냐 하면,
전자 발진기 - 오실레이터 - 는 온도에 따라 그 값이 변합니다.
그래서 온도에 따른 변화를 보정하기 위해 온도센서가 자리잡고 있는 것이지요.
뒷면에는 CR2032 버튼 전지를 끼울 수 있는 플라스틱이 존재합니다.
이는 전원이 차단되더라도 "시간을 기억" 하기고 있기 위한 것이지요.
이 건전지 한개로 몇년은 쓴다고 하네요.
4. Layout
배선은 일반 IIC 배선과 동일합니다.
SLA 은 A4에, SCL은 A5 이죠.
DS3231 | Arduino Nano ------------------------- VCC | 3.3V GND | GND SDC | A5 SDA | A4 ------------------------- SSD1306 | Arduino Nano ------------------------- VCC | 3.3V GND | GND SDC | A5 SDA | A4 -------------------------
실재 배선 모양입니다.
I2C의 특성상, 다른 센서 / 부품들 중, I2C 방식이면 arduino 의 동일한 pin 에 꼽아도 따로 인식 됩니다.
이는 각 device 가 가지는 address 가 다르기 때문이지요.
이는 I2C Scanner 를 이용해서 살펴보면, 각각 따로 인식하는 것을 알 수 있습니다.
아래에서 0x3c 는 OLED 이고, 0x68 이 DS3231 입니다.
추가로 나오는 0x57 은 AT24C32 EEPROM 입니다.
여기서 이상한 점은 0x5f 라는 address 입니다.
무얼까... 답을 찾지는 못했지만, 찾는 와중에 한가지 새로운 사실을 알게 됩니다.
5. EEPROM
EEPROM 으로는 ATmega 사의 AT24C32 이 쓰입니다.
이것의 실제 chip 번호는 ATML332 라고 적혀 있습니다.
- https://www.kynix.com/Detail/447536/ATMLH745.html
그렇습니다. EEPROM 이 original 이 아니고 fake 제품인 것이죠.
그러나 제품 구동은 정상적으로 돌아갑니다.
100% original chip 과 동일하지 않기 때문에 보다 복잡한 작업을 시키면 정상적으로 동작하지 않을 지도 모릅니다.
여튼, 앞으로 싼 부품은 좀 걸러야 할지도 모르겠네요.
6. Library 등록
인터넷에 돌아다니는 source 를 등록해서 사용할 수 있지만,
IDE 에서 지원해주는 Library 등록 기능을 이용하여 Example source 를 등록해 봅니다.
우선 IDE 메뉴에서 "Sketch > Add File... > Manage Libraries..." 를 선택합니다.
이건 이제 매번 써먹는 방법이지요?
Libarry Manager 의 검색창에서 "ds3231" 을 쳐서 검색합니다.
그러면 여러가지 source 들이 나오는데, 왠만하면 제일 위에 나오는 것을 선택하면 됩니다.
아래 그림처럼 adafruit 에서 만든 library 이니, 쓸만 할껍니다.
이렇게 하면 IDE 메뉴의 "File > Examples > DS3231" 항목이 생기고 sample source 를 이용할 수 있습니다.
6. sketch - 시간 설정
인터넷에 돌아다니는 source 를 등록해서 사용할 수 있지만,
위에서처럼 Arduino IDE 의 Library Manager 를 통해서 얻은 소스를 활용해 봅니다.
일단 시간을 입력합니다.
소스에 보이듯이 Serial Monitor 에 "YYMMDDwHHMMSS" 를 넣고, 마지막에 "x" 를 붙이면 설정됩니다.
------------------------------------------------
YYMMDDwHHMMSS, with an 'x' at the end
------------------------------------------------
/* Sets the time and prints back time stamps for 5 seconds Based on DS3231_set.pde by Eric Ayars 4/11 Added printing back of time stamps and increased baud rate (to better synchronize computer and RTC) Andy Wickert 5/15/2011 */ #include "DS3231.h" #include "Wire.h" DS3231 Clock; byte Year; byte Month; byte Date; byte DoW; byte Hour; byte Minute; byte Second; bool Century=false; bool h12; bool PM; void GetDateStuff(byte& Year, byte& Month, byte& Day, byte& DoW, byte& Hour, byte& Minute, byte& Second) { // Call this if you notice something coming in on // the serial port. The stuff coming in should be in // the order YYMMDDwHHMMSS, with an 'x' at the end. boolean GotString = false; char InChar; byte Temp1, Temp2; char InString[20]; byte j=0; while (!GotString) { if (Serial.available()) { InChar = Serial.read(); InString[j] = InChar; j += 1; if (InChar == 'x') { GotString = true; } } } Serial.println(InString); // Read Year first Temp1 = (byte)InString[0] -48; Temp2 = (byte)InString[1] -48; Year = Temp1*10 + Temp2; // now month Temp1 = (byte)InString[2] -48; Temp2 = (byte)InString[3] -48; Month = Temp1*10 + Temp2; // now date Temp1 = (byte)InString[4] -48; Temp2 = (byte)InString[5] -48; Day = Temp1*10 + Temp2; // now Day of Week DoW = (byte)InString[6] - 48; // now Hour Temp1 = (byte)InString[7] -48; Temp2 = (byte)InString[8] -48; Hour = Temp1*10 + Temp2; // now Minute Temp1 = (byte)InString[9] -48; Temp2 = (byte)InString[10] -48; Minute = Temp1*10 + Temp2; // now Second Temp1 = (byte)InString[11] -48; Temp2 = (byte)InString[12] -48; Second = Temp1*10 + Temp2; } void setup() { // Start the serial port Serial.begin(57600); // Start the I2C interface Wire.begin(); } void loop() { // If something is coming in on the serial line, it's // a time correction so set the clock accordingly. if (Serial.available()) { GetDateStuff(Year, Month, Date, DoW, Hour, Minute, Second); Clock.setClockMode(false); // set to 24h //setClockMode(true); // set to 12h Clock.setYear(Year); Clock.setMonth(Month); Clock.setDate(Date); Clock.setDoW(DoW); Clock.setHour(Hour); Clock.setMinute(Minute); Clock.setSecond(Second); // Give time at next five seconds for (int i=0; i<5; i++){ delay(1000); Serial.print(Clock.getYear(), DEC); Serial.print("-"); Serial.print(Clock.getMonth(Century), DEC); Serial.print("-"); Serial.print(Clock.getDate(), DEC); Serial.print(" "); Serial.print(Clock.getHour(h12, PM), DEC); //24-hr Serial.print(":"); Serial.print(Clock.getMinute(), DEC); Serial.print(":"); Serial.println(Clock.getSecond(), DEC); } } delay(1000); }
시각을 입력하니 잘 등록되고 읽어집니다.
7. sketch - 시간 설정 + 시간 가져오기 + 온도
기본 sample 을 사용해도 되지만, 찾아다니면서 하나로 된 소스는 아래인것 같습니다.
Serial Monitor 의 입력창에 "T1124154091014" 등으로 입력하면 시각이 입력되면서,
그냥 와두면, "시간 + 온도" 를 표시해 준다.
* Tutorial – Using DS1307 and DS3231 Real-time Clock Modules with Arduino
#include "Wire.h" #define DS3231_I2C_ADDRESS 104 // SCL - pin A5 // SDA - pin A4 // To set the clock, run the sketch and use the serial monitor. // Enter T1124154091014; the code will read this and set the clock. See the code for full details. byte seconds, minutes, hours, day, date, month, year; char weekDay[4]; byte tMSB, tLSB; float temp3231; void setup() { Wire.begin(); Serial.begin(9600); } void loop() { watchConsole(); get3231Date(); Serial.print(weekDay); Serial.print(", "); Serial.print(date, DEC); Serial.print("/"); Serial.print(month, DEC); Serial.print("/"); Serial.print(year, DEC); Serial.print(" - "); Serial.print(hours, DEC); Serial.print(":"); Serial.print(minutes, DEC); Serial.print(":"); Serial.print(seconds, DEC); Serial.print(" - Temp: "); Serial.println(get3231Temp()); delay(1000); } // Convert normal decimal numbers to binary coded decimal byte decToBcd(byte val) { return ( (val/10*16) + (val%10) ); } void watchConsole() { if (Serial.available()) { // Look for char in serial queue and process if found if (Serial.read() == 84) { //If command = "T" Set Date set3231Date(); get3231Date(); Serial.println(" "); } } } void set3231Date() { //T(sec)(min)(hour)(dayOfWeek)(dayOfMonth)(month)(year) //T(00-59)(00-59)(00-23)(1-7)(01-31)(01-12)(00-99) //Example: 02-Feb-09 @ 19:57:11 for the 3rd day of the week -> T1157193020209 // T1124154091014 seconds = (byte) ((Serial.read() - 48) * 10 + (Serial.read() - 48)); // Use of (byte) type casting and ascii math to achieve result. minutes = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); hours = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); day = (byte) (Serial.read() - 48); date = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); month = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); year = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); Wire.beginTransmission(DS3231_I2C_ADDRESS); Wire.write(0x00); Wire.write(decToBcd(seconds)); Wire.write(decToBcd(minutes)); Wire.write(decToBcd(hours)); Wire.write(decToBcd(day)); Wire.write(decToBcd(date)); Wire.write(decToBcd(month)); Wire.write(decToBcd(year)); Wire.endTransmission(); } void get3231Date() { // send request to receive data starting at register 0 Wire.beginTransmission(DS3231_I2C_ADDRESS); // 104 is DS3231 device address Wire.write(0x00); // start at register 0 Wire.endTransmission(); Wire.requestFrom(DS3231_I2C_ADDRESS, 7); // request seven bytes if(Wire.available()) { seconds = Wire.read(); // get seconds minutes = Wire.read(); // get minutes hours = Wire.read(); // get hours day = Wire.read(); date = Wire.read(); month = Wire.read(); //temp month year = Wire.read(); seconds = (((seconds & B11110000)>>4)*10 + (seconds & B00001111)); // convert BCD to decimal minutes = (((minutes & B11110000)>>4)*10 + (minutes & B00001111)); // convert BCD to decimal hours = (((hours & B00110000)>>4)*10 + (hours & B00001111)); // convert BCD to decimal (assume 24 hour mode) day = (day & B00000111); // 1-7 date = (((date & B00110000)>>4)*10 + (date & B00001111)); // 1-31 month = (((month & B00010000)>>4)*10 + (month & B00001111)); //msb7 is century overflow year = (((year & B11110000)>>4)*10 + (year & B00001111)); } else { //oh noes, no data! } switch (day) { case 1: strcpy(weekDay, "Sun"); break; case 2: strcpy(weekDay, "Mon"); break; case 3: strcpy(weekDay, "Tue"); break; case 4: strcpy(weekDay, "Wed"); break; case 5: strcpy(weekDay, "Thu"); break; case 6: strcpy(weekDay, "Fri"); break; case 7: strcpy(weekDay, "Sat"); break; } } float get3231Temp() { //temp registers (11h-12h) get updated automatically every 64s Wire.beginTransmission(DS3231_I2C_ADDRESS); Wire.write(0x11); Wire.endTransmission(); Wire.requestFrom(DS3231_I2C_ADDRESS, 2); if(Wire.available()) { tMSB = Wire.read(); //2's complement int portion tLSB = Wire.read(); //fraction portion temp3231 = (tMSB & B01111111); //do 2's math on Tmsb temp3231 += ( (tLSB >> 6) * 0.25 ); //only care about bits 7 & 8 } else { //oh noes, no data! } return temp3231; }
Serial Monitor 의 결과 입니다.
8. sketch - 시간 설정 + 시간 가져오기 + 온도 + OLED
위의 소스를 조금 바꾸어 OLED 에 표시해주는 소스로 살짝 바꾸었습니다.
일주일 지난 뒤, 측정하니 여전히 잘 동작하고 있네요.
살짝 바꾼 소스 올려 봅니다.
#include "SPI.h" #include "Wire.h" #include "Adafruit_GFX.h" #include "Adafruit_SSD1306.h" Adafruit_SSD1306 display = Adafruit_SSD1306(); #define DS3231_I2C_ADDRESS 104 // SCL - pin A5 // SDA - pin A4 // To set the clock, run the sketch and use the serial monitor. // Enter T1124154091014; the code will read this and set the clock. See the code for full details. byte seconds, minutes, hours, day, date, month, year; char weekDay[4]; byte tMSB, tLSB; float temp3231; void setup() { Serial.begin(9600); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3C (for the 128x32) display.clearDisplay(); display.display(); delay(1000); Wire.begin(); } void loop() { watchConsole(); get3231Date(); display.clearDisplay(); display.setTextColor(WHITE); display.setCursor(0,0); display.setTextSize(1); display.print("DATE : "); display.print(weekDay); display.print(", "); display.print(date, DEC); display.print("/"); display.print(month, DEC); display.print("/"); display.println(year, DEC); display.print("TIME : "); display.print(hours, DEC); display.print(":"); display.print(minutes, DEC); display.print(":"); display.println(seconds, DEC); display.print("TEMP : "); display.println(get3231Temp()); display.display(); delay(1000); } // Convert normal decimal numbers to binary coded decimal byte decToBcd(byte val) { return ( (val/10*16) + (val%10) ); } void watchConsole() { if (Serial.available()) { // Look for char in serial queue and process if found if (Serial.read() == 84) { //If command = "T" Set Date set3231Date(); get3231Date(); Serial.println(" "); } } } void set3231Date() { //T(sec)(min)(hour)(dayOfWeek)(dayOfMonth)(month)(year) //T(00-59)(00-59)(00-23)(1-7)(01-31)(01-12)(00-99) //Example: 02-Feb-09 @ 19:57:11 for the 3rd day of the week -> T1157193020209 // T1124154091014 seconds = (byte) ((Serial.read() - 48) * 10 + (Serial.read() - 48)); // Use of (byte) type casting and ascii math to achieve result. minutes = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); hours = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); day = (byte) (Serial.read() - 48); date = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); month = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); year = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); Wire.beginTransmission(DS3231_I2C_ADDRESS); Wire.write(0x00); Wire.write(decToBcd(seconds)); Wire.write(decToBcd(minutes)); Wire.write(decToBcd(hours)); Wire.write(decToBcd(day)); Wire.write(decToBcd(date)); Wire.write(decToBcd(month)); Wire.write(decToBcd(year)); Wire.endTransmission(); } void get3231Date() { // send request to receive data starting at register 0 Wire.beginTransmission(DS3231_I2C_ADDRESS); // 104 is DS3231 device address Wire.write(0x00); // start at register 0 Wire.endTransmission(); Wire.requestFrom(DS3231_I2C_ADDRESS, 7); // request seven bytes if(Wire.available()) { seconds = Wire.read(); // get seconds minutes = Wire.read(); // get minutes hours = Wire.read(); // get hours day = Wire.read(); date = Wire.read(); month = Wire.read(); //temp month year = Wire.read(); seconds = (((seconds & B11110000)>>4)*10 + (seconds & B00001111)); // convert BCD to decimal minutes = (((minutes & B11110000)>>4)*10 + (minutes & B00001111)); // convert BCD to decimal hours = (((hours & B00110000)>>4)*10 + (hours & B00001111)); // convert BCD to decimal (assume 24 hour mode) day = (day & B00000111); // 1-7 date = (((date & B00110000)>>4)*10 + (date & B00001111)); // 1-31 month = (((month & B00010000)>>4)*10 + (month & B00001111)); //msb7 is century overflow year = (((year & B11110000)>>4)*10 + (year & B00001111)); } else { //oh noes, no data! } switch (day) { case 1: strcpy(weekDay, "Sun"); break; case 2: strcpy(weekDay, "Mon"); break; case 3: strcpy(weekDay, "Tue"); break; case 4: strcpy(weekDay, "Wed"); break; case 5: strcpy(weekDay, "Thu"); break; case 6: strcpy(weekDay, "Fri"); break; case 7: strcpy(weekDay, "Sat"); break; } } float get3231Temp() { //temp registers (11h-12h) get updated automatically every 64s Wire.beginTransmission(DS3231_I2C_ADDRESS); Wire.write(0x11); Wire.endTransmission(); Wire.requestFrom(DS3231_I2C_ADDRESS, 2); if(Wire.available()) { tMSB = Wire.read(); //2's complement int portion tLSB = Wire.read(); //fraction portion temp3231 = (tMSB & B01111111); //do 2's math on Tmsb temp3231 += ( (tLSB >> 6) * 0.25 ); //only care about bits 7 & 8 } else { //oh noes, no data! } return temp3231; }
동영상도 올려 봅니다.
FIN
DS3231 에 대해서는 이야기 할 내용이 더 있어서 2편에서 더 다루어 보겠습니다.
'Hardware' 카테고리의 다른 글
Hardware | MAX30105 파티클 센서 - 1 (0) | 2018.11.23 |
---|---|
Hardware | RTC DS3231 부품 사용기 - 2 (0) | 2018.11.20 |
Hardware | Transistor 구매 (0) | 2018.11.10 |
Hardware | 스위치 부품 구매하기 (0) | 2018.11.09 |
Hardware | ipTIME N704BCM 무선 공유기 (2) | 2018.11.06 |