반응형

예전에 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 데이터시트



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


위의 소스는 http://www.goodliffe.org.uk/arduino/rtc_ds3231.php 의 소스를 한국의 시간표시 방식에 맞도록 시간설정의 입력과 출력부분을 살짝 수정한 소스 입니다.


위의 소스를 실행한 결과 아래와 같이 1초 간격으로 년, 월, 일, 시, 분, 초, 요일 및 내장된 온도센서로 현재 온도까지 출력이 되었습니다. 설정한 시간은 모듈의 배터리에 의해서 백업되므로 외부전원을 완전히 끊었다가 연결해도 현재시간 데이터가 계속 살아 있습니다.





■ 추가내용 (2017.11.14) : 댓글에 SDA 와 SCL핀을 변경할 수 있느냐는 질문이 있어서 내용을 추가해 본다. 이 모듈은 I2C 방식으로 아두이노와 통신을 하는데 아두이노의 I2C 통신을 위한 핀은 정해져 있다. 아두이노 우노는 A4가 SDA, A5가 SCL 이다. 아두이노 메가는 보드에 쓰여 있다. 아래의 그림을 보면 이해가 편할 것이다.


[아두이노 우노의 구조]

반응형

+ Recent posts