Skip to content

Timer displayed in seconds with time remaining until 2030.

Notifications You must be signed in to change notification settings

Tetramad/2030Timer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

49 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Description

2년에 가까운 시간 동안 코딩하지 않아서 코딩하는 기억을 되살리기 위해 시작한 프로젝트이다. UN에서 낸 보고서 중 2030년대에 지구 평균 기온이 1.5도 이상으로 높아질 것이라는 예측에서 착안했다. 원본 보고서를 찾지 못 했지만 이 페이지에서도 언급한다. 2030년 1월 1일 00시 00분까지 남은 시간초를 표시해주는 시계로 AVR코어를 사용하는 ATmega계열 마이크로컨트롤러로 인터럽트, TWI(I2C)를 이용한 통신, 입출력 제어를 연습하는 것이 목표이다.

Block Diagram

Block Diagram

Perfboard

Perfboard

Implementation

  • Displaying seconds until 2030 to FND
  • Changing current time using buttons
  • Read and write current time using RTC

Recaps

버튼 디바운서 구현

초기 구상

소프트웨어로 구현하려면 마이크로컨트롤러의 타이머를 하나 사용하여 시간을 재야 한다. 버튼 입력만을 위해 타이머로 업타임을 계속 기록하는 것이 비효율적이라고 생각하여 하드웨어 측면에서 구현하려 했다.

조사

디바운서 회로를 보면 RC 저역 통과 필터와 슈미트 트리거로 구성하고 있다. 집에 슈미트 트리거가 없어서 그냥 RC 저역 통과 필터만 구성했고 그렇게 사용하는 사진도 많이 있어서 충분할 것이라 생각했다.

문제 발생 및 분석

회로 구성 후 여전히 바운싱 현상이 있어서 다시 생각해봤다. 입력 전압 근처에서 저주파 진동이 발생하는 경우가 충분히 있을 수 있고 이 경우 여전히 바운싱 현상이 일어난다. 이를 보완하기 위해 슈미트 트리거가 있었던 것이다.

해결 방안 구상

소프트웨어로 구현하는 것이 부품을 주문하여 받는 것보다 빠를 것이라고 판단하여 타이머를 이용하여 구현하기로 했다. 구현하는 중에 버튼 입력만 고려하면 굳이 업타임을 계속 기록하지 않아도 된다는 것을 깨닫고 입력 받고 일정 시간의 타이머가 끝나기 전까지 입력을 무시하는 방법으로 구현했다.

버튼 인터럽트

초기 구상

예전에 다이오드로 OR 게이트를 구성하여 여러 버튼을 하나의 인터럽트로 받는 방법을 사용한 기억이 있어서 그 방법 그대로 했다.

우연히 본 기능

마이크로컨트롤러 명세서를 볼 때 IO 부분을 보면서 Pin Change Interrupt 부분을 보긴 했지만 내용을 자세히 보진 않았었다. 자투리 시간에 어쩌다 그 부분을 읽게 됬는데 하드웨어로 구현했던 것이 이미 마이크로컨트롤러에서 지원하는 기능임을 알았다.

변경

다이오드로 연결된 버튼을 다 분리하고 PCINT 인터럽트를 사용하는 로직으로 변경했다.

breadboard를 perfboard로 이식

이유

버튼이나 크리스탈 등이 브래드보드에 잘 맞지 않고 흔들리기도 하고 브래드보드는 다른 프로젝트할 때 또 써야하기 때문에 회로를 옮기기로 했다. 이미 브래드보드에 회로를 전부 구현해두었기 때문에 그대로 perfboard로 옮기면 된다고 생각하여 PCB를 설계하고 주문하는 것보다 쉽고 빠를 것이라 예상했다.

문제

이 프로젝트는 9개의 7세그먼트를 사용한다. 7세그먼트들의 핀아웃만 81개에 달한다. 이를 다시 저항과 시프트 레지스터에 순차적으로 연결하려면 많은 선과 납땜이 필요하다. 3-4일이면 옮길 것이라는 예상과 다르게 배선용 선 부족까지 겹치면서 1주일 넘게 걸렸다. 이 시간이면 PCB를 설계해서 받을 수도 있었던 시간이라 생각한다.

TWI 구현

초기 구현

이전에 TWI를 사용해본 적이 있어서 그 때의 기억을 되살려 구현했다. TWINT 신호를 보내고 busy wait 방식으로 전송이 완료되는 것을 기다린 후 다음 동작을 하는 방식이다.

다음 구현

TWI 인터럽트가 존재하는 것을 알고 있기 때문에 이번에는 인터럽트를 이용하는 방법으로 바꾸기로 했다. TWI 인터럽트는 연쇄적인 인터럽트로 동작하기 때문에 처음 START 비트를 전송한 후 통신이 끝날 때까지 인터럽트 루틴을 여러번 호출하면서 동작하게 된다. 그래서 I2C 통신으로 데이터를 주고 받기 위해서는 별도의 정보를 저장할 필요가 있어서 TWI 상태와 별개로 I2C의 상태를 나타내는 변수와 TWI 인터럽트 벡터가 사용할 데이터들을 추가하여 구현하였다.

다다음 구현

TWI 인터럽트로 구현한 코드가 굉장히 더럽고 복잡하여 다시 읽어보니 switchcase, if가 연속적으로 나타나는 수상한 코드가 만들어져 있었다. 더 나은 코드를 작성하기 위해서는 다른 사람의 구현을 볼 필요가 있다고 생각하여 arduino의 'Wire.h' 헤더의 구현을 읽어보았고 TWI_STATE 정보만 가지고 분기하여도 충분히 구현할 수 있음을 확인하였다. 이후 TWI_STATE 정보와 I2C 상태 정보, 인터럽트 루틴에서 사용될 변수들로 구성하였다. 추가로 'single master' 구조만 지원하면서 더 간결한 코드를 만들 수 있었다.

time.h 헤더와 RTC의 BCD 데이터

RTC 추가 이전

RTC를 추가하기 전에는 time.htime_t 자료형, 타이머를 이용하여 시간 변화를 구현하였다. 그러다 보니 시간 변경 상황에서 세그먼트 출력할 때는 struct tm으로 변환하여 출력하였고, 1초 증가 시에도 struct tm으로 변환 후 1초 증가하고 다시 time_t로 변환했다. 간단히 말해서 struct tmtime_t 사이의 변환이 자주 일어났다. 동작에 문제가 생길 정도는 아니라서 일단 그대로 두었다.

RTC 추가 이후

RTC 추가된 후 많이 구현이 RTC 칩에서 담당하게 되었다. 그러나 RTC가 BCD 형식으로 데이터를 주기 때문에 기존 time.h의 기능을 사용할 수 없었다. 일단은 RTC의 BCD 데이터를 바이너리로 변경하여 struct tm을 구성한 다음 time.h의 기능을 사용하기로 했다. 시간 변경이 발생하였을 때는 struct tm을 BCD로 변경하여 RTC로 전송하였다.

BCD로 통일

BCD와 바이너리를 오가는 과정이 많은 연상량과 메모리를 차지하여서 RTC의 BCD 데이터에서 바로 time_t로 변경하는 함수를 구현해서 time.h를 완전히 제거하였다. 변경하는 코드 구현을 glibc의 구현을 참고하였는데 이 프로그램에서는 2000년부터 2030년까지만 표시하기 때문에 여러 부분에서 최적화할 수 있었다.

미구현된 아쉬운 점들

RTC 인터럽트

RTC에서 알람 인터럽트를 제공한다. 매초마다 인터럽트를 하는 방식과 정해진 시각에 인터러트를 하는 방식이 있다. 매초마다 인터럽트를 하는 방식을 이용하면 현재 구현의 _delay_ms 사용을 제거할 수 있었을 것 같다. RTC 알람 인터럽트는 고려는 해봤으나 고려하는 시점에서 이미 perfboard로 옮겨진 후이고 절전 부분을 구현할 계획이 없었기 때문에 RTC 알람 인터럽트도 같이 미구현으로 남겨 뒀다.

절전 기능과 슬립

메인 루틴의 루프에서 하는 일은 일정 시간마다 입력 인터럽트 발생을 확인하는 것과 세그먼트 디스플레이를 갱신하는 것이다. 세그먼트 디스플레이 갱신을 인터럽트에서 이벤트를 주는 방식으로 변경할 수 있다고 생각했다. RTC 알람 인터럽트와 기존 입력 인터럽트, 필요하다면 깜박이는 효과를 내기 위한 타이머 인터럽트까지 합치면 디스플레이가 갱신되어야 하는 상황을 모두 인터럽트로 받을 수 있게 된다. 슬립 상태에 들어가면 타이머 클럭이 멈추는 것 때문에 깜박이는 효과까지는 못하더라도 평상시에 _delay_ms 사용 없이 인터럽트 신호가 올 때까지 슬립 상태로 대기할 수 있었을 것 같다.

시간 변경 시 RTC 클럭 정지

시간 변경 중에 RTC 클럭을 정지 시켜서 조작 중에는 시간이 증가하지 않도록 했었다. 하지만 TWI 구현 미숙 문제인지 RTC 스펙을 잘 못 이해한 것인지 제대로 동작하지 않아서 해당 구현을 제거하고 이후 추가하지 않았다. 메모리 최적화 과정에서 동작 방식이 바뀌어서 다시 추가하기는 힘들 것 같다.

더 작은 마이크로컨트롤러

3kB에서 4kB를 오가고 크기 최적화로 빌드했을 때는 2kB대를 보여줬기 때문에 Arduino micro의 ATmega32u4 칩 대신 2kB 플레시 메모리를 장착한 더 작은 칩으로 교체하여 완성하고 싶었다. 메모리 최적화를 위해 많은 일을 했지만 2kB 밑으로 내려가지 못했다. 글을 쓰는 시점에서 크기 최적화 빌드의 바이너리 크기는 2200바이트이다. 4kB 메모리를 장착한 칩으로 옮기려 했지만 2kB, 4kB 구간의 attiny 계열의 칩에는 TWI가 없고 대신에 USI가 있다는 것을 알았다. TWI와 USI 모두 I2C 통신을 지원하지만 하드웨어적 구조나 인터럽트 방식 등 여러 부분에서 차이가 존재하여 일단 Arduino micro를 장착하여 완성하기로 했다.

About

Timer displayed in seconds with time remaining until 2030.

Resources

Stars

Watchers

Forks