산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Manufacturing Technology >> 제조공정

아두이노 나노 RTC 시계: DS1307로 정밀한 시간 유지하기

부품 및 소모품

아두이노 나노 R3 (Arduino Nano R3) 1개
실시간 시계 모듈 (RTC) - DS1307 기반 1개
4자리 7세그먼트 LED 디스플레이 1개
암 헤더 단일 열 3개
천공 기판 (퍼포레이티드 보드) 1개
점퍼 와이어 (범용) 25개
수 헤더 40핀 1열 (2.54mm) 1개

필요한 도구 및 장비

납땜 인두 (범용)
멀티미터

모든 프로젝트에는 멀티미터가 필요합니다. 제대로 된 제품을 구입하면 최소 4년, 수리 방법을 안다면 훨씬 더 오래 쓸 수 있습니다. 납땜 연결 상태 확인과 회로 전류 측정에 필요합니다.

앱 및 온라인 서비스

프로젝트 소개

RTC DS1307 IC를 사용해 만든 매우 간단하고 조립하기 쉬운 디지털 시계입니다. LCD 디스플레이 대신 4자리 7세그먼트 디스플레이에 시간을 표시합니다. 코드를 약간 수정하면 알람 기능 등 추가 기능을 쉽게 구현할 수 있습니다. 상상력과 독창성만 있으면 됩니다. 이 프로젝트는 더 복잡하고 나은 작품으로 나아가기 위한 디딤돌이자, 방에 전시할 멋진 시계를 만들고 싶어서 시작했습니다.

납땜 과정에서 겪은 문제들과 해결 방법까지 모든 세부 사항을 이 글에 담았습니다.

1단계: 부품 소개

RTC 모듈 (DS1307)

아두이노 나노 RTC 시계: DS1307로 정밀한 시간 유지하기

DS1307 칩은 전원이 꺼진 상태에서도 시간을 유지하는 탁월한 기능을 가집니다. 아두이노와 인터페이스가 쉽고, 이 모듈용 라이브러리도 많이 공개되어 있습니다. RTC는 I2C 프로토콜로 아두이노와 통신하며, 아두이노 나노의 A4(SDA)와 A5(SCL) 핀을 사용합니다. DS 핀은 이 프로젝트에서 사용하지 않습니다.

단점은 원하는 만큼 정확하지 않다는 점입니다. 온도에 따라 시간이 쉽게 드리프트(시간 오차 발생)됩니다.

주의: GND와 Vcc 핀을 올바르게 연결하세요. 모듈에서 Vcc 핀이 GND 핀 앞에 있습니다. 극성을 반대로 연결하면 순식간에 매우 뜨거워집니다. 극성 연결이 걱정된다면 전원 켤 때 코인셀 배터리를 만져보고 뜨거워지면 즉시 끄세요.

시프트 레지스터 (74HC595)

아두이노 나노 RTC 시계: DS1307로 정밀한 시간 유지하기

참고: 이 칩들은 꽤 신뢰성이 있지만, 몇 개는 불량이었습니다. 어떤 칩은 Q0, Q1이 작동 안 하고, 어떤 건 Q3가 내부적으로 접지되어 있었습니다 (제조 결함). 지금 쓰는 칩들도 완벽하진 않습니다. 하나는 Q7이 고장 나서 연결을 정확히 하고, 그래도 안 되면 멀티미터 연속성 테스트로 핀을 확인해야 했습니다. 그래도 불평 안 합니다. 프로젝트하며 작은 장애물을 극복하는 법을 배우는 과정이니까요.

4자리 7세그먼트 디스플레이

아두이노 나노 RTC 시계: DS1307로 정밀한 시간 유지하기
아두이노 나노 RTC 시계: DS1307로 정밀한 시간 유지하기

범용 공통 애노드(Common Anode) 타입 4자리 세그먼트를 사용했습니다. 12핀이며, 왼쪽 아래부터 번호가 시작해 왼쪽 위로 끝납니다. 각 세그먼트는 숫자와 소수점을 표시할 수 있습니다. 디지털 시계의 전형적인 콜론(:)이 없어서 두 번째 자릿수의 소수점으로 대체했습니다. 숫자 표시가 주 목적이라면 훌륭한 디스플레이입니다.

주의: 초보자에게는 좀 까다로울 수 있습니다. 세그먼트 a-g가 일렬로 정렬되어 있지 않으니까요. 전류 제한 저항 없이 5V에 직접 연결하지 마세요.

회로도를 포함시켰으니 참고하세요. 회로도의 디스플레이 타입과 제가 쓴 게 달라서, 시프트 레지스터에서 세그먼트로의 핀 연결을 표로 정리했습니다:

세그먼트 디스플레이 핀 번호 시프트 레지스터 핀
A1115 (첫 번째 595)
B71 (첫 번째 595)
C42 (첫 번째 595)
D23 (첫 번째 595)
E14 (첫 번째 595)
F105 (첫 번째 595)
G56 (첫 번째 595)
소수점37 (첫 번째 595)
D1 (첫째 자리)1215 (두 번째 595)
D2 (둘째 자리)91 (두 번째 595)
D3 (셋째 자리)82 (두 번째 595)
D4 (넷째 자리)63 (두 번째 595)

이 프로젝트는 저렴하고 만들기 쉽지만, PCB에 납땜해 제대로 만드려면 약간의 인내와 끈기가 필요합니다 (그저 재미로 브레드보드에 해보려면 2시간도 안 걸립니다).

피드백 주시면 개선에 참고하겠습니다. 빠트린 내용이 있다면 알려주세요.

코드

메인 시계 코드 (아두이노)

RTC 라이브러리와 I2C 라이브러리를 사용합니다. 프로그램 실행을 위해 이 라이브러리들이 필요합니다. 이 프로그램은 공통 애노드(Common Anode) 타입 디스플레이용입니다.

#include <Wire.h>
#include <EEPROM.h>
#include <RTClib.h>

RTC_DS1307 RTC;

int temp, inc, hours1, minut, add = 11;
int HOUR, MINUT, SECOND;
int latchPin = 3;  // 595의 12번 핀
int dataPin = 4;   // 595의 14번 핀
int clockPin = 2;  // 595의 11번 핀
int shift = 256;
int units, tens, hundreds, thousands;
int x, y;
const int alarmHour = 17;
const int alarmMinute = 26;

void setup() {
  Serial.begin(9600);
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(13, OUTPUT);
  Wire.begin();
  RTC.begin();
  if (!RTC.isrunning()) {
    RTC.adjust(DateTime(__DATE__, __TIME__));
  }
}

void loop() {
  int temp = 0, val = 1, temp4;
  DateTime now = RTC.now();
  HOUR = now.hour();
  MINUT = now.minute();

  // 시간 분리 (십의 자리, 일의 자리)
  if (HOUR < 10) {
    hundreds = HOUR;
    thousands = HOUR / 10;
  } else if (HOUR >= 10 && HOUR < 24) {
    hundreds = HOUR % 10;
    thousands = HOUR / 10;
  }
  if (MINUT <= 9) {
    units = MINUT;
    tens = MINUT / 10;
  } else if (MINUT > 9 && MINUT <= 60) {
    units = MINUT % 10;
    tens = MINUT / 10;
  }

  // 각 자릿수별 세그먼트 패턴 출력 (멀티플렉싱)
  // units (분의 1의 자리)
  switch (units) {
    case 0: writeDigit(shift * 8, 192); break;
    case 1: writeDigit(shift * 8, 249); break;
    case 2: writeDigit(shift * 8, 164); break;
    case 3: writeDigit(shift * 8, 176); break;
    case 4: writeDigit(shift * 8, 153); break;
    case 5: writeDigit(shift * 8, 146); break;
    case 6: writeDigit(shift * 8, 130); break;
    case 7: writeDigit(shift * 8, 248); break;
    case 8: writeDigit(shift * 8, 128); break;
    case 9: writeDigit(shift * 8, 144); break;
  }
  delay(1);

  // tens (분의 10의 자리)
  switch (tens) {
    case 0: writeDigit(shift * 4, 192); break;
    case 1: writeDigit(shift * 4, 249); break;
    case 2: writeDigit(shift * 4, 164); break;
    case 3: writeDigit(shift * 4, 176); break;
    case 4: writeDigit(shift * 4, 153); break;
    case 5: writeDigit(shift * 4, 146); break;
  }
  delay(1);

  // hundreds (시의 1의 자리)
  switch (hundreds) {
    case 0: writeDigit(shift * 2, 64); break;
    case 1: writeDigit(shift * 2, 121); break;
    case 2: writeDigit(shift * 2, 36); break;
    case 3: writeDigit(shift * 2, 48); break;
    case 4: writeDigit(shift * 2, 25); break;
    case 5: writeDigit(shift * 2, 18); break;
    case 6: writeDigit(shift * 2, 2); break;
    case 7: writeDigit(shift * 2, 120); break;
    case 8: writeDigit(shift * 2, 0); break;
    case 9: writeDigit(shift * 2, 16); break;
  }
  delay(1);

  // thousands (시의 10의 자리)
  switch (thousands) {
    case 0: writeDigit(shift, 192); break;
    case 1: writeDigit(shift, 249); break;
    case 2: writeDigit(shift, 164); break;
    case 3: writeDigit(shift, 176); break;
    case 4: writeDigit(shift, 153); break;
    case 5: writeDigit(shift, 146); break;
    case 6: writeDigit(shift, 130); break;
    case 7: writeDigit(shift, 248); break;
    case 8: writeDigit(shift, 128); break;
    case 9: writeDigit(shift, 152); break;
  }
  delay(1);

  // 알람 섹션
  if (HOUR == alarmHour && MINUT == alarmMinute) {
    digitalWrite(13, HIGH);
  } else {
    digitalWrite(13, LOW);
  }
}

// 세그먼트 출력을 위한 헬퍼 함수 (코드 중복 제거)
void writeDigit(int shiftVal, int pattern) {
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, shiftVal >> 8);
  shiftOut(dataPin, clockPin, MSBFIRST, shiftVal + pattern);
  digitalWrite(latchPin, HIGH);
}

시간 설정 코드 (아두이노)

DS1307은 시간 드리프트가 발생하기 쉽습니다. 이 프로그램으로 시리얼 모니터를 통해 정확한 시간/날짜를 설정할 수 있습니다. 시간이 맞지 않을 때 RTC 모듈을 아두이노에 연결하고 이 코드를 업로드한 뒤, 시리얼 모니터에서 날짜, 월, 년, 시간을 입력하세요. 그런 다음 메인 시계 코드를 업로드하면 정확한 시간이 7세그먼트 디스플레이에 표시됩니다.

/* RealTimeClockDS1307 라이브러리 - DS1307 RTC 모듈 제어
   Copyright (c) 2011 David H. Brown. All rights reserved.
   ... (라이선스 생략) ... */

#include <Wire.h>
#include <RealTimeClockDS1307.h>

#define Display_Clock_Every_N_Seconds 1
#define Display_ShortHelp_Every_N_Seconds 25

int count = 0;
char formatted[] = "00-00-00 00:00:00x";

void setup() {
  Serial.begin(9600);
}

void loop() {
  if (Serial.available()) processCommand();
  delay(1000);
  RTC.readClock();
  count++;
  if (count % Display_Clock_Every_N_Seconds == 0) {
    Serial.print(count);
    Serial.print(": ");
    RTC.getFormatted(formatted);
    Serial.print(formatted);
    Serial.println();
  }
  if (count % Display_ShortHelp_Every_N_Seconds == 0) {
    Serial.println("Send ? for a list of commands.");
  }
  // ... (테스트용 코드들 생략) ...
}

void processCommand() {
  if (!Serial.available()) return;
  char command = Serial.read();
  int in, in2;
  switch (command) {
    case 'H': case 'h': in = SerialReadPosInt(); RTC.setHours(in); RTC.setClock(); break;
    case 'I': case 'i': in = SerialReadPosInt(); RTC.setMinutes(in); RTC.setClock(); break;
    case 'S': case 's': in = SerialReadPosInt(); RTC.setSeconds(in); RTC.setClock(); break;
    case 'Y': case 'y': in = SerialReadPosInt(); RTC.setYear(in); RTC.setClock(); break;
    case 'M': case 'm': in = SerialReadPosInt(); RTC.setMonth(in); RTC.setClock(); break;
    case 'D': case 'd': in = SerialReadPosInt(); RTC.setDate(in); RTC.setClock(); break;
    case 'W': Serial.print("Day of week is "); Serial.println((int)RTC.getDayOfWeek()); break;
    case 'w': in = SerialReadPosInt(); RTC.setDayOfWeek(in); RTC.setClock(); break;
    case 't': case 'T': /* 12/24시간 전환 */ break;
    case 'A': case 'a': /* AM 설정 */ break;
    case 'P': case 'p': /* PM 설정 */ break;
    case 'q': RTC.sqwEnable(RTC.SQW_1Hz); break;
    case 'Q': RTC.sqwDisable(0); break;
    case 'z': RTC.start(); break;
    case 'Z': RTC.stop(); break;
    case '>': /* 레지스터 쓰기 */ break;
    case '<': /* 레지스터 읽기 */ break;
    default: /* 도움말 출력 */ break;
  }
}

int SerialReadPosInt() {
  int i = 0; boolean done = false;
  while (Serial.available() && !done) {
    char c = Serial.read();
    if (c >= '0' && c <= '9') i = i * 10 + (c - '0');
    else done = true;
  }
  return i;
}

라이브러리 파일들

다음 라이브러리 파일들을 아두이노 IDE에 설치해야 합니다:

상세한 라이브러리 코드는 원문 참고 바랍니다. (파일 크기 관계로 핵심만 요약)

회로도

프로젝트 회로도입니다.

7segmentClock.fzz (프리칭 파일)

제조공정

  1. 다리미판 제조: 설계, 재료, 생산 공정
  2. 쿠쿠 시계: 역사, 장인 정신, 현대적 진화
  3. 인쇄회로기판(PCB): 설계, 제조, 미래 트렌드
  4. 서핑보드 제작과 디자인: 폴리네시아 기원에서 현대 혁신까지
  5. 라즈베리 파이와 아두이노로 로봇 만들기: 단계별 통합 가이드
  6. RasPiRobot Board V2: 라즈베리 파이를 강력한 로봇 컨트롤러로 확장하기
  7. 아두이노 POV 클락: 퍼시스턴스 오브 비전 LED 디스플레이 제작
  8. 인피니티 미러 시계: 끝없는 우아함과 정확한 시간의 만남
  9. 결함 없는 인쇄 회로 기판(PCB)의 중요성
  10. 엔진 구성 분류 총정리: 행정, 설계, 점화, 실린더 수, 배치, 연료별 특징