156_RTC_DS1302_실시간시계 ⏰🔋

도로시와 지니가 함께하는 32.768kHz 수정 발진자 원리, CR2032 백업 배터리 및 I2C 1602 LCD 디지털 만년 달력 시계 제작
- 👧 도로시: “지니야! 우리가 아두이노에서
millis()함수로 시간을 재면, USB 전원을 뺐다가 다시 꽂았을 때 0초로 홀라당 리셋되어 버리잖아! 전원을 꺼도 스마트폰처럼 진짜 현재 시각(년, 월, 일, 시, 분, 초)을 계속 기억하게 할 수는 없을까?” - 🐱 지니: “그 고민을 한 방에 해결해 주는 구원투수가 바로 RTC(Real Time Clock - 실시간 시계) 모듈이란다! 동전 모양의 CR2032 백업 배터리가 붙어 있어서 전원이 꺼져도 몇 년 동안 오차 없이 째깍째깍 현재 시각을 지켜주거든!”
-
🐶 토토: “바우! 정해진 시간(예: 아침 7시)이 되면 자동으로 밥을 주는 스마트 자동 급식기를 만들자 멍!”
- 🖥️ 강의 슬라이드: slides.md
1. RTC(Real Time Clock)의 동작 원리
⏱️ 32.768kHz 쿼츠 수정 발진자와 $2^{15}$의 수학적 비밀
- 왜 하필 32,768Hz(32.768kHz)일까?:
- 시계 내부에는 초소형 쿼츠(수정) 소리굽쇠 진동자가 들어 있습니다.
- $32,768 = 2^{15}$ 입니다!
- 즉, 디지털 2진 플립플롭 카운터 회로를 15번 연속으로 거치면 32,768번의 진동이 정확히 1초(1Hz) 의 펄스로 변환됩니다.
- 백업 코인 배터리(CR2032)의 저전력 기적:
- 아두이노 본체 전원이 끊어지면 RTC 칩 내부 회로가 즉시 초저전력 모드(나노암페어 $nA$ 단위 소비)로 전환되어 배터리 전원으로만 시간을 카운트합니다.
- 이 덕분에 배터리 하나로 3년~5년 동안 멈추지 않고 동작합니다.
2. DS1302 핀 구조와 아두이노 배선표
| DS1302 핀 | 기능 설명 | 아두이노 우노(Uno) 연결 핀 |
|---|---|---|
| VCC | 주전원 공급선 (2.0V ~ 5.5V) | 5V |
| GND | 공통 접지선 | GND |
| CLK (SCLK) | 직렬 클록 동기화 신호선 | D6 |
| DAT (I/O) | 양방향 데이터 입출력선 | D7 |
| RST (CE) | 칩 활성화(Chip Enable) 제어선 | D8 |
- 실시간 시계 대시보드 디스플레이 (I2C 1602 LCD):
GND➔ GNDVCC➔ 5VSDA➔ A4 (I2C 데이터)SCL➔ A5 (I2C 클록)
3. 실습 예제: I2C LCD에 띄우는 실시간 디지털 만년 달력 시계
라이브러리 매니저에서 Rtc by Makuna 라이브러리와 LiquidCrystal_I2C를 설치한 뒤 아래 스케치를 업로드합니다.
#include <ThreeWire.h>
#include <RtcDS1302.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// DS1302 핀 매핑: DAT(7), CLK(6), RST(8)
ThreeWire myWire(7, 6, 8);
RtcDS1302<ThreeWire> Rtc(myWire);
// I2C 1602 LCD (주소: 0x27 또는 0x3F)
LiquidCrystal_I2C lcd(0x27, 16, 2);
void setup() {
Serial.begin(9600);
lcd.init();
lcd.backlight();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("RTC Clock Start!");
delay(1000);
Rtc.Begin();
// 컴파일 시점의 PC 컴퓨터 현재 시각으로 자동 1회 설정
RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
if (!Rtc.IsDateTimeValid()) {
Serial.println("⚠️ RTC 배터리가 방전되었거나 시간이 설정되지 않았습니다. 현재 시간으로 초기화합니다.");
Rtc.SetDateTime(compiled);
}
if (Rtc.GetIsWriteProtected()) {
Rtc.SetIsWriteProtected(false); // 쓰기 보호 해제
}
if (!Rtc.GetIsRunning()) {
Rtc.SetIsRunning(true); // 시계 카운터 가동
}
RtcDateTime now = Rtc.GetDateTime();
if (now < compiled) {
Serial.println("⏳ RTC 시간이 이전 시간이므로 컴파일 시간으로 업데이트합니다.");
Rtc.SetDateTime(compiled);
}
}
void loop() {
RtcDateTime now = Rtc.GetDateTime();
// 1행: 년-월-일 (YYYY-MM-DD)
char dateStr[17];
snprintf(dateStr, sizeof(dateStr), "%04d-%02d-%02d (%s)",
now.Year(), now.Month(), now.Day(),
getDayOfWeek(now.DayOfWeek()));
// 2행: 시:분:초 (HH:MM:SS)
char timeStr[17];
snprintf(timeStr, sizeof(timeStr), "TIME: %02d:%02d:%02d",
now.Hour(), now.Minute(), now.Second());
// LCD 화면 갱신
lcd.setCursor(0, 0);
lcd.print(dateStr);
lcd.setCursor(0, 1);
lcd.print(timeStr);
// 시리얼 모니터 실시간 출력
Serial.print(dateStr);
Serial.print(" ");
Serial.println(timeStr);
delay(1000); // 1초마다 갱신
}
const char* getDayOfWeek(uint8_t dayOfWeek) {
switch (dayOfWeek) {
case 0: return "SUN";
case 1: return "MON";
case 2: return "TUE";
case 3: return "WED";
case 4: return "THU";
case 5: return "FRI";
case 6: return "SAT";
default: return "???";
}
}
4. 실습 결과 및 시리얼 모니터 확인
2026-10-21 (WED) TIME: 14:35:28
2026-10-21 (WED) TIME: 14:35:29
2026-10-21 (WED) TIME: 14:35:30
⚡ 전원 차단 테스트:
USB 케이블을 컴퓨터에서 뽑고 1분이 지난 뒤 다시 꽂아보세요!
아두이노가 꺼져 있던 1분 동안에도 RTC가 시간을 정확히 세고 있어, 다시 연결하자마자 1분이 흐른 최신 시각이 즉시 표시됩니다!
5. 핵심 요약 정리 💡
millis()와의 차이: 아두이노 리셋 시 초기화되는 소프트웨어 타이머와 달리, RTC는 독립적인 하드웨어 카운터로 작동합니다.- $2^{15}$ 수정 분주기: 32.768kHz 쿼츠 진동자를 15단 플립플롭으로 나누어 완벽한 1초를 만들어냅니다.
- 만년 달력 내장: 윤년(Leap Year), 28/30/31일 등 각 월의 날짜와 요일까지 모듈 자체 캘린더 알고리즘으로 자동 계산됩니다.
- IoT 스케줄러 응용: 특정 시간대 조명 켜기, 화분 물주기, 스마트 알람시계 등 실시간 이벤트 제어의 핵심 부품입니다.
서브목차