예전에 DS1302 라는 칩을 사용한 RTC (Real Time Clock) 모듈에 대해서 알아본적이 있습니다. 이번에는 DS1302 모듈보다 한층 업그레이드된 DS3231 이라는 모듈에 대해서 알아보았습니다. 아두이노에는 자체적인 RTC가 없으므로 Real Time 을 다루기 위해서는 이런 RTC 모듈을 붙여서 현재의 시간을 알아내야 합니다. DS3231은 다음과 같은 장점이 있습니다. 1년에 2분 이내의 오차, 5V/3.3V 겸용, 2개의 알람기능 제공, 주기적 인터럽트(Interrupt) 발생가능, 칩 내부에 발진기능을 가지고 있어 외부에 크리스탈 부품이 필요 없음, 자체에 온도센서를 가지고 있어서 온도측정도 가능... 이런 이유로 조금 더 정밀한 시간이 필요한 프로젝트에는 DS3231 모듈을 사용하는 것이 더 적합하다고 할 수 있습니다.
DS3231 모듈 스펙
- Size: 38mm (length) x 22mm (Width) x 14mm (height)
- Weight: 8g
- Operating voltage :3.3 - 5.5V
- Clock chip: high-precision clock chip DS3231
- Clock Accuracy :0-40 °C range, the accuracy 2ppm, the error was about 1 minute
- Calendar alarm clock with two Programmable square-wave output
- Real time clock generator seconds, minutes, hours, day, date, month and year timing and provide valid until the year 2100 leap year compensation
- Chip temperature sensor comes with an accuracy of ± 3 °C
- Memory chips: AT24C32 (storage capacity 32K)
- IIC bus interface, the maximum transmission speed of 400KHz (working voltage of 5V)
- Can be cascaded with other IIC device, 24C32 addresses can be shorted A0/A1/A2 modify default address is 0x57
- Rechargeable battery type: LIR2032
DS3231 모듈의 모습입니다. 전면에 현재의 시간을 유지할 수 있도록 LR-2032 배터리를 넣을 수 있습니다. 이걸 빼면 모든 시간설정값이 초기화 됩니다. 컴퓨터 메인보드에 끼워져 있는 전지와 같은 용도 입니다. 메인보드에 전지를 빼면 컴퓨터의 시간설정값도 초기화 되죠.
모듈의 구조는 위와 같습니다. 크리스털 등 모든 RTC 기능을 탑재한 DS3231 칩이 상부에 자리잡고 있으며 아랫쪽에 32KB 용량의 데이터를 저장할 수 있는 EEPROM 칩인 AT24C32N 이 달려 있습니다.
아두이노와의 연결
연결은 별다른 것 없이 전원과 SDA, SCL 단자만 연결해주면 됩니다.
아두이노 우노 |
DS3231 모듈 |
5V |
VCC |
GND |
GND |
A4 |
SDA |
A5 |
SCL |
소스
소스의 주석에도 써있지만 현재시간의 설정을 위하여 시간설정을 입력하는 방법은 다음과 같습니다.
▶ T(설정명령) + 년(00~99) + 월(01~12) + 일(01~31) + 시(00~23) + 분(00~59) + 초(00~59) + 요일(1~7, 일1 월2 화3 수4 목5 금6 토7)
▶ 예: T1605091300002 (2016년 5월 9일 13시 00분 00초 월요일)
위와 예와 같이 T 로 시작하는 시간 설정 문자열을 만들어서 시리얼모니터에 입력해서 아두이노로 전송하면 됩니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | #include <Wire.h> #define DS3231_I2C_ADDRESS 104 // 데이터핀 연결 // SCL - pin A5 // SDA - pin A4 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(", 20"); Serial.print(year, DEC); Serial.print("/"); Serial.print(month, DEC); Serial.print("/"); Serial.print(date, 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); } // 10진수를 2진화 10진수인 BCD 로 변환 (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(" "); } } } //시간설정 // T(설정명령) + 년(00~99) + 월(01~12) + 일(01~31) + 시(00~23) + 분(00~59) + 초(00~59) + 요일(1~7, 일1 월2 화3 수4 목5 금6 토7) // 예: T1605091300002 (2016년 5월 9일 13시 00분 00초 월요일) void set3231Date() { year = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); month = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); date = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); hours = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); minutes = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); seconds = (byte) ((Serial.read() - 48) * 10 + (Serial.read() - 48)); day = (byte) (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 { //error! no data! } return temp3231; } | cs |
위의 소스를 실행한 결과 아래와 같이 1초 간격으로 년, 월, 일, 시, 분, 초, 요일 및 내장된 온도센서로 현재 온도까지 출력이 되었습니다. 설정한 시간은 모듈의 배터리에 의해서 백업되므로 외부전원을 완전히 끊었다가 연결해도 현재시간 데이터가 계속 살아 있습니다.
■ 추가내용 (2017.11.14) : 댓글에 SDA 와 SCL핀을 변경할 수 있느냐는 질문이 있어서 내용을 추가해 본다. 이 모듈은 I2C 방식으로 아두이노와 통신을 하는데 아두이노의 I2C 통신을 위한 핀은 정해져 있다. 아두이노 우노는 A4가 SDA, A5가 SCL 이다. 아두이노 메가는 보드에 쓰여 있다. 아래의 그림을 보면 이해가 편할 것이다.
[아두이노 우노의 구조]
'아두이노' 카테고리의 다른 글
[아두이노] GPS 모듈의 사용 1편 - GPS 로그 찍어보기 (58) | 2016.05.12 |
---|---|
[아두이노] 0.96인치 OLED LCD 모듈의 사용 (Adafruit SSD1306 호환) (4) | 2016.05.09 |
[아두이노] 아두이노 프로 미니 보드와 USB TO TTL 모듈을 이용한 프로그램 업로드 (2) | 2016.05.02 |
[아두이노] 정밀한 온습도 센서 DHT22 (AM2302) 의 사용 (6) | 2016.05.02 |
[아두이노] 장애물 감지 자율주행 자동차 (43) | 2016.04.20 |