라즈베리파이, 아두이노

[간단한 아두이노 코딩] 20. 7세그먼트 (5461AH)로 디지털 시계 만들기 (8편 번외)

포도알77 2020. 11. 29. 22:57

8-번외 / 7세그먼트 (5461AH)로 디지털 시계 만들기

 

1. 4자리 7 세그먼트 디스플레이 (5461AH)

 5461AH는 7 세그먼트 디스플레이 4개가 하나로 구성된 장치이다. 일반 7세그먼트와는 달리 a,b,c,d,e,f,g그리고 dp까지 8개의 핀과 각 세그먼트별 CC 라인 4개가 포함되어 있다.

 

 

 

 

 즉, A~DP 순서대로 11, 7, 4, 2, 1, 10, 5, 3이고, 첫번째 세그먼트부터 12, 9, 8, 6가 해당 세그먼트의 CC가 된다.

그렇다면 회로는 어떻게 짜야할까? 11, 7, 4, 2, 1, 10, 5, 3 핀은 CC타입이므로 디지털 핀에 할당하면 된다. 그리고 12, 9, 8, 64개의 세그먼트가 동일한 A~DP 입력 선을 가지므로 한번에 1개의 세그먼트의 불만 켤 수 있다. 따라서 우리는 매우 짧은 시간 동안 각 세그먼트를 순차적으로 켜고 꺼야만 한다.

 

 

2. 잔상 효과

 우리가 보는 영상은 일반적으로 초당 30~60장의 사진이 빠르게 바뀌는 것이다. (이를 frame per second, fps라고 부르고 물론 사람은 더 높은 fps의 영상을 비교했을 때 인지할 수 있다.)  1초 (1000ms) 동안 30 장의 사진이 변경되는 것은 사진 한장당 약 33ms 정도 노출되는 것이고 컴퓨터의 관점에서 33ms는 매우 긴 시간이기 때문에 우리는 7 세그먼트를 33ms마다 한번씩 갱신하게 된다면 우리의 눈은 LED가 항상 켜져 있는 것으로 인식할 수 있다. (물론, LED가 전원이 끊어졌을 때 한 순간에 바로 꺼지는 것이 아니므로 이러한 지연시간까지 고려한다면 33ms 보다 더 길게 하더라도 사람은 인지할 수 없다)  하지만 loop 함수는 리턴되고 나서 10ms후에 호출되기 때문에 명령어 실행 속도등을 고려했을 때 그냥 delay를 주지 않거나 매우 적게 주는 것이 좋다.

 

 아무튼 우리는 4자리의 숫자를 잔상 효과를 통해 제어하기 위해서는 순간마다 4개의 세그먼트 중 하나씩 8개의 a~dp핀을 껐다 켜주면 된다. 그리고 4개의 세그먼트를 선택하기 위해서는 위의 5461AH는 CC타입이므로 선택된 세그먼트는 GND 그리고 나머지 세그먼트의 CC핀은 5V를 인가하면 된다.

 

 

3. 세그먼트 동작하기도 벅찬데 시간 데이터는?

 아두이노는 버튼 사용하기에 언급했던 인터럽트를 이용할 수 있다. 우리가 사용할 수 있는 인터럽트 말고도 다양한 인터럽트가 존재하는데, 그 중 timer라는 인터럽트를 이용한 라이브러리를 이용하게 된다면 우리가 원하는 간격마다 함수를 실행할 수 있게 된다.

 

 대표적인 타이머 인터럽트 라이브러리는 MsTimer2로 라이브러리 관리자를 통해 쉽게 설치할 수 있다.

 

 

 이제 이 타이머를 통하여 매 1000ms마다 초 단위 정보를 갱신할 것이다.

 

 

4. 회로 구성

 12개의 핀 모두 Digital 핀에 할당하면 된다. (여기서 dp는 사용하지 않을 것이므로 나는 11개의 핀만 할당할 것이다.)

11, 7, 4, 2, 1, 10, 5를 각각 D2, D3, D4, D5, D6, D7, D8에 할당하고, 12, 9, 8, 6D9, D10, D11, D12에 할당하자.

 

 

 그림에 g(5번)이 누락되었는데 아두이노 D8에 연결하면 된다.

 

 

5. 소스코드

 우리는 2가지를 생각해야 한다.

 (1) 타이머를 이용한 시간 계산

 (2) 세그먼트를 적절하게 갱신

 

 (1)을 위해서 MsTimer2를 이용하여 시간을 출력하는 프로그램을 간단하게 작성해보자. 

  타이머는 1초마다 갱신되고, 전역 변수를 이용하여 현재의 시간을 저장한다. 현재의 시간은 세그먼트가 출력하기 쉽도록 시간(10단위), 시간(1단위), 분(10단위), 분(1단위)로 표현한다. (분, 초 단위로 해보아도 무방하다.)

 

 결과는 시리얼을 통해서 출력한다.

 

(1) 타이머를 이용한 시간 계산 소스코드

// MsTimer 헤더 추가
#include <MsTimer2.h>

// 초단위 저장 변수
long secTime = 0;

// 시간, 분, 초의 각각 10단위, 1단위 변수
// 배열을 이용하면 더 간결하게 짤 수 있다.
int hour_10 = 0;
int hour_1 = 0;
int min_10 = 0;
int min_1 = 0;
int sec_10 = 0;
int sec_1 = 0;

// 초단위 값을 이용하여 시,분,초로 변환 후 변수에 저장
void timeCalculate(unsigned long secTime){
  int hours = secTime/3600; secTime%=3600;
  int mins = secTime/60; secTime%=60;
  int secs = secTime;

  hour_10 = hours/10; 
  hour_1 = hours%10;
  min_10 = mins/10; 
  min_1 = mins%10;
  sec_10 = secs/10; 
  sec_1 = secs%10;
}
// MsTimer2의 인터럽트 루틴 함수
// 1초에 한번씩 호출되고 24시간*60분*60초마다 0으로 초기화된다
// % 연산자는 나머지를 구하는 연산자 1일=86400초이므로 secTime은 0~86399의 값만 가진다.
void tick()
{
  secTime+=1;
  secTime%=(24L*60*60);
  // L은 long 상수임을 뜻하는 키워드로,
  // 24*60*60은 86400으로 아두이노의 int 타입 변수 2byte = 2^16 = 65536(signed의 경우 절반)
  // 저장 범위를 넘어서므로 long 4byte 변수로 사용됨을 뜻하는 것이다.
  // 이전의 온도센서에서 상수를 5.0으로 표현한 것과 같은 이치이다.
  
  timeCalculate(secTime);
  // 갱신된 secTime으로 시간 변수 저장
  printTime();
  // 결과 출력
}

// 시간 변수를 출력하는 함수
// 시리얼 모니터 ctrl+shift+M에서 확인할 수 있다.
void printTime(){
  Serial.print(hour_10);
  Serial.print(hour_1);
  Serial.print(':');
  Serial.print(min_10);
  Serial.print(min_1);
  Serial.print(':');
  Serial.print(sec_10);
  Serial.println(sec_1);
}

void setup()
{
  Serial.begin(9600);
  // MsTimer2에 1000ms(1초)마다 tick 호출하도록 등록  
  MsTimer2::set(1000, tick);
  MsTimer2::start();
}

void loop()
{
}

 

 

위의 소스코드는 아두이노 수준에 맞춘 것으로, 실제로는 각 시각, 분, 초 값은 1byte 값에 담을 수 있고 배열을 이용하면 훨씬 더 간결하게 계산할 수 있다. 또한 컴퓨터 환경에서는 시간을 초단위가 아닌 ms 단위로 저장한다.

 

(2) 세그먼트 출력하기

 세그먼트에 값을 출력하는 소스코드를 작성해보자. 소스코드의 로직은 예전의 7 세그먼트 출력하기와 유사하지만, 이번에는 핀이 12개인 5461AH이므로 아래와 같이 핀 세팅을 한다.

11, 7, 4, 2, 1, 10, 5는 각각 D2, D3, D4, D5, D6, D7, D8

12, 9, 8, 6는 D9, D10, D11, D12

 

// a~h 핀
int seg_pin[7] = {2, 3, 4, 5, 6, 7, 8};
// 세그먼트의 cc 핀
int seg_cc[4] = {9, 10, 11, 12};

// 세그먼트 숫자 값 0~9
int num[10][7] = {
  {1,1,1,1,1,1,0},
  {0,1,1,0,0,0,0},
  {1,1,0,1,1,0,1},
  {1,1,1,1,0,0,1},
  {0,1,1,0,0,1,1},
  {1,0,1,1,0,1,1},
  {1,0,1,1,1,1,1},
  {1,1,1,0,0,0,0},
  {1,1,1,1,1,1,1},
  {1,1,1,0,0,1,1}
};
// 세그먼트 출력 함수
// a~h핀에 val에 해당하는 값을 출력
void printSeg(int val){
    for(int j=0; j<7; j++){
      digitalWrite(seg_pin[j], num[val][j]);
    }
}

void setup() {
  // 핀 설정
  for(int i=0; i<7; i++)
    pinMode(seg_pin[i], OUTPUT);
  for(int i=0; i<4; i++)
    pinMode(seg_cc[i], OUTPUT);
}
void loop() {
  // 4개의 세그먼트를 돌면서
  for(int i=0; i<4; i++){
    // i : 출력할 세그먼트
    for(int j=0; j<4; j++){
      // i==j이면 0(GND)
      // i!=j이면 1(5V)이므로 현재 선택된 세그먼트에만 GND를 인가
      digitalWrite(seg_cc[j], i!=j);
    }
    // 숫자 6출력
    printSeg(6);
  }
}

 

 

 (3) 타이머와 세그먼트 소스코드를 합치기

 이제 시간 계산 부분과 세그먼트 출력 부분이 만들어 졌으므로 이를 합치는 작업을 수행한다.

#include <MsTimer2.h>

long secTime = 0;

int hour_10 = 0;
int hour_1 = 0;
int min_10 = 0;
int min_1 = 0;
int sec_10 = 0;
int sec_1 = 0;

void timeCalculate(unsigned long secTime){
  int hours = secTime/3600; secTime%=3600;
  int mins = secTime/60; secTime%=60;
  int secs = secTime;

  hour_10 = hours/10; 
  hour_1 = hours%10;
  min_10 = mins/10; 
  min_1 = mins%10;
  sec_10 = secs/10; 
  sec_1 = secs%10;
}

void tick()
{
  secTime+=1;
  secTime%=(24L*60*60);
  timeCalculate(secTime);
}

// 세그먼트 코드
int seg_pin[7] = {2, 3, 4, 5, 6, 7, 8};
int seg_cc[4] = {9, 10, 11, 12};

int num[10][7] = {
  {1,1,1,1,1,1,0},
  {0,1,1,0,0,0,0},
  {1,1,0,1,1,0,1},
  {1,1,1,1,0,0,1},
  {0,1,1,0,0,1,1},
  {1,0,1,1,0,1,1},
  {1,0,1,1,1,1,1},
  {1,1,1,0,0,0,0},
  {1,1,1,1,1,1,1},
  {1,1,1,0,0,1,1}
};

void printSeg(int val){
    for(int j=0; j<7; j++){
      digitalWrite(seg_pin[j], num[val][j]);
    }
}

void setup()
{
  for(int i=0; i<7; i++)
    pinMode(seg_pin[i], OUTPUT);
  for(int i=0; i<4; i++)
    pinMode(seg_cc[i], OUTPUT);
    
  MsTimer2::set(1000, tick);
  MsTimer2::start();
}

void loop()
{
  for(int i=0; i<4; i++){
    for(int j=0; j<4; j++){
      digitalWrite(seg_cc[j], i!=j);
    }
    if(i==0) printSeg(min_10);
    else if(i==1) printSeg(min_1);
    else if(i==2) printSeg(sec_10);
    else printSeg(sec_1);
    delay(5);
  }
}

 

 마지막 loop에서 hour_10, hour_1, min_10, min_1을 출력하도록 하면, 시각과 분이 출력된다.

 

 

페이스북으로 공유카카오톡으로 공유카카오스토리로 공유트위터로 공유URL 복사