[아두이노] 4-Digit 7-Segment Display 시계 만들기

IOT/아두이노|2019. 6. 15. 09:00

[아두이노] 4-Digit 7-Segment Display 시계 만들기



지난 시간에 4-Digit 7-Segment Display을 사용하는 방법을 실험 하였습니다. 오늘은 지난시간에 만든 회로도와 기본소스를 기반으로 아두이노 시계를 실제 만들어 보겠습니다.


1. 4-Digit 7-Segment Display 회로도


  • 준비물 : 4-Digit 7-Segment Display 1개, 스위치버턴 1개, 아두이노우노
  • 내용 : 4-Digit 7-Segment Display 부품의 핀 번호 순서에 맞게 아두이노우노에 연결하고, 스위치 버턴은 2번핀에 연결하시오.
  • 참조 : [아두이노] 4-Digit 7-Segment Display 제어

지난 시간의 회로도와 동일합니다.


2. 시계 코딩



시간을 참조 Post에 가셔서 사전 학습 해주세요.

1) 시간 구하기


시간은 시리얼통신으로 입력을 받을 경우를 가정해서 시간을 구해 볼까요.

if(Serial.available()){
  String inString = Serial.readStringUntil('\n');    
}

inString에 시간 문자열을 읽습니다.

int index1 = inString.indexOf(':'); 
int index2 = inString.indexOf(':',index1+1);   
int index3 = inString.length();
    
hour = inString.substring(0, index1).toInt();
min = inString.substring(index1+1,index2).toInt();
sec = inString.substring(index2+1,index3).toInt(); 

위와 같이 코딩해서 hour, min, sec 구하게 됩니다. 지난시간에 설명을 다했기 때문에 간단히 넘어 갑니다.

timer0_millis = ((long)hour*3600+min*60+sec)*1000;   

hour, min, sec 값을 초로 변환하여 timer0_millis 변수에 저장하면 타이머는 입력된 시간을 기준으로 돌아가게 됩니다. 즉, 입력된 시간에서 타이머 시간이 흐른다고 보시면 됩니다.

readTime = millis()/1000;

sec = readTime%60;
min = (readTime/60)%60;
hour = (readTime/(60*60))%24; 

readTime 변수에는 millis()함수에서 읽은 타이머 시간값을 1000으로 나눈 몫 값을 저장하기 때문에 입력된 시간을 기준으로 1초 단위로 값이 변화가 일어 나겠죠. 입력시간을 기준으로 흐르는 시간값을 hour, min, sec값으로 다시 구하면 현재 시간을 구할 수 있게 됩니다.

이 값을 4-Digit 7-Segment Display로 출력하면 실제 아두이노 시계가 완성 됩니다.

2) 시간 타이머리셋 지정


24시간을 기준으로 시간을 리셋 시킬 예정입니다. 그러면 어떻게 코딩해야 할까요.

if(millis()>=86400000){ //타이머 리셋
     timer0_millis=0;
}

if문으로 1day는 24시간이고 이 시간은 "60x60x24" 가 됩니다. 여기에 1초(1000)을 곱해주면 86400000의 타이머 시간값이 만들어 집니다. 이 시간이 되면 timer0_millis=0으로 리셋 시키면 24시간 단위로 타이머는 처음부터 다시 돌게 됩니다.

3) 인터럽트 스위치버턴 활용


지난시간에 인터럽트 스위치버턴을 추가했는데 그러면 이 스위치버턴을 그냥 두기가 아쉬워서 어떤 기능을 넣을까 고민하다가 시간 표시를 제어하는 스위치버턴으로 변경해 보았습니다.

void switchFn(){
  state=!state;
}

스위치 버턴이 눌러지면 state 변수가 반전이 일어나게 했습니다.

if(state==true){ //12시 or 24시 출력모드
  hour = hour%12;
}   

이렇게 수정했습니다. 현재 시간 hour를 12시 기준으로 출력할 것인지 24시 기준으로 출력할 건지를 스위치버턴으로 제어하는 기능을 추가 했네요.

4) 종합소스


이제 지난시간의 소스에다가 위 설계 코딩을 삽입하면 아래와 같이 완성 됩니다.

//a,b,c,d,e,f,g 상태값
const byte segValue[10][7] = {
   {1,1,1,1,1,1,0}, //0
   {0,1,1,0,0,0,0}, //1
   {1,1,0,1,1,0,1}, //2
   {1,1,1,1,0,0,1}, //3
   {0,1,1,0,0,1,1}, //4
   {1,0,1,1,0,1,1}, //5
   {1,0,1,1,1,1,1}, //6
   {1,1,1,0,0,0,0}, //7
   {1,1,1,1,1,1,1}, //8
   {1,1,1,1,0,1,1}  //9  
};

const byte segPin[8]={7,3,A3,A1,A0,6,A4,A2}; //사용핀{a,b,c,d,e,f,g,dp} 순서대로임
const byte digitPin[4] = {8,5,4,A5}; //segment 위치 핀

const byte interruptPin = 2;//인터럽트핀

boolean state = false;//시간 출력형식 지정
extern volatile unsigned long timer0_millis; //타이머변수
unsigned long readTime; //현재타이머시간
int hour, min, sec;

void setup() {
  Serial.begin(9600);  
  
  pinMode(interruptPin, INPUT_PULLUP); 
  attachInterrupt(digitalPinToInterrupt(interruptPin), switchFn, FALLING);
  
  for(int i=0;i<10;i++){
    pinMode(segPin[i], OUTPUT);
  }
  for(int j=0;j<4;j++){
    pinMode(digitPin[j], OUTPUT);    
    digitalWrite(digitPin[j], HIGH); 
  }  
}

void loop() {   
  if(Serial.available()){ //입력시간 읽기
    String inString = Serial.readStringUntil('\n');    
    int index1 = inString.indexOf(':'); 
    int index2 = inString.indexOf(':',index1+1);   
    int index3 = inString.length();
    
    hour = inString.substring(0, index1).toInt();
    min = inString.substring(index1+1,index2).toInt();
    sec = inString.substring(index2+1,index3).toInt(); 
    
    timer0_millis = ((long)hour*3600+min*60+sec)*1000;   //입력시간 초변환
  } 
    
  if(millis()>=86400000){ //타이머 리셋
     timer0_millis=0;
  }
  readTime = millis()/1000; //현재시간 읽기
  sec = readTime%60;
  min = (readTime/60)%60;
  hour = (readTime/(60*60))%24; 

  if(state==true){ //12시 or 24시 출력모드
    hour = hour%12;
  } 
  segOutput(3,min%10,0); //min 1의 자리
  segOutput(2,min/10,0); //min 10의 자리
  segOutput(1,hour%10,1); //hour 1의 자리
  segOutput(0,hour/10,0); //hour 10의 자리    
}

//12시 or 24시 출력 변경
void switchFn(){
  state=!state;
}

//LED 초기화
void segClear(){ 
  for(int i=0;i<8;i++){
    digitalWrite(segPin[i], LOW);        
  }
}
//LED 출력
void segOutput(int d, int Number, int dp){ 
  segClear();
  digitalWrite(digitPin[d], LOW); 
  for(int i=0;i<7;i++){
     digitalWrite(segPin[i], segValue[Number][i]);        
  }
  digitalWrite(segPin[7], dp);
  delayMicroseconds(1000);
  digitalWrite(digitPin[d], HIGH); 
}

3. 결과


1) 분/초 출력


숫자를 4개 뿐이 출력을 못하기 때문에 간단히 시간이 흐르는 것을 확인 할 수 있게 출력 숫자는 분/초로 세팅했습니다.

  segOutput(3,sec%10,0); //min 1의 자리
  segOutput(2,sec/10,0); //min 10의 자리
  segOutput(1,min%10,1); //min 1의 자리
  segOutput(0,min/10,0); //min 10의 자리    

입력은 정상적으로 시간 "23:59:50"을 입력할 때는 내부적으로 시간은 정확하게 흘러갑니다. 여기서, 출력은 분/초만 표시될 뿐이죠


분/초만 아래와 같이 출력되어 정상적으로 시간이 흘러가네요.


2) 시/분 출력


종합 소스에 시/분 코딩을 그대로 실험한 결과입니다. 출력은 시/분만 출력됩니다.

  segOutput(3,min%10,0); //min 1의 자리
  segOutput(2,min/10,0); //min 10의 자리
  segOutput(1,hour%10,1); //hour 1의 자리
  segOutput(0,hour/10,0); //hour 10의 자리    

입력은 시간 "23:59:10"을 입력했습니다.

결과는 아래의 동영상에 보시면 "23:59"만 출력됩니다. 내부적으로 초는 계속 흘러가고 있습니다. 인터럽트 스위치버턴을 누르면 12시 기준으로 시간을 표시되고 다시 누르면 23시 기준으로 시간이 표시 되는 실험 결과를 보실 수 있을 꺼에요. 그리고 24시가 되면은 "00:00"으로 리셋되는 것 까지 보실 수 있을 꺼에요.

마무리


4-Digit 7-Segment Display 부품 밖에 없어서 안타깝게 6자리 시간 표시를 못했네요. 그러다보니 분/초 아니면 시/분으로 표시할 수 밖에 없었고 결과 동영상으로는 표현의 한계가 있었네요.

개별 7-Segment가 2개 있으면 추가로 연결해서 표시 할 수 있었겠지만 한개뿐이라 연결을 포기 했습니다. 아무튼 이런식으로 아두이노 시계를 만들 수 있다는 것을 보여드리는 걸로 정리를 할까 합니다. Stepper Motor로 시계를 표시하고 싶은데 4-Digit 7-Segment Display가 post 주제에서 나중에 응용편으로 기회가 된다면 그때 보여드릴게요. 주제에 맞게 post를 하기 위해서 그 실험은 생략하도록 하겠습니다.

가상시뮬레이터에서 6개 7-Segment를 이용해서 확실히 시계를 만들어서 보여드릴까 고민 좀 해보겠습니다. 여러분들은 실제 제작을 안하더라고 가상시뮬레이터에서 동일한 실험이 체험할 수 있게 귀찮은 회로선 연결을 해야하지만 한번 생각해보고 다음 post으로 올릴지 결정할께요. 될 수 있으면 가상시뮬레이터로 한번 제작해서 보여드리는 방향으로 할께요.


댓글()