개발자/Arduino

아두아노 millis()함수 시간 다루기

지구빵집 2022. 4. 4. 10:29
반응형

 

오늘은 아두이노 프로그래밍에서 자주 사용하는 millis( ) 함수에 대해 알아보겠습니다. millis 함수 레퍼런스를 참조는 이곳을 참고하시면 되고 여기서는 millis 함수를 사용해 읽은 값을 다루는 방법을 설명합니다.

 

아두이노의 전원이 공급되면 타이머가 돌기 시작합니다. 타이머가 도는 시간을 가져오기 위해서 millis( ) 함수를 사용합니다. 현재 타이머의 밀리세컨드 시간 값을 읽어오게 됩니다. 1초당 1000의 값을 갖게 되는데 이 시간 값을 통해 우리는 시간을 만들어 낼 수 있습니다. 

 

* 주의사항: millis( )의 반환 값은 unsigned long 이므로 프로그래머가 int 와 같은 작은 자료형으로 산술을 수행하려고 하면 논리 오류가 발생할 수 있다. signed long 의 최대값의 경우도 unsigned long의 최대값의 절반이기 때문에 오류가 발생할 수 있다. 

 

여기서 millis( ) 함수에서 읽어온 데이터의 최대 크기는 얼마인지 확인하기 위해 아두이노 데이터 타입을 살펴보면 4,294,967,295 (unsigned long maximum value)가 된다는 것을 알 수 있다. 초로 환산하면 4,294,967초, 71,582분, 1,193시간, 약 49일이 된다. 이 숫자를 넘어가면 오버플로우가 발생한다는 것을 예상할 수 있습니다. 이 오버플로우 문제에 대해서는 차후 포스팅을 하겠습니다.

 

 

아두이노 데이터 타입

 

우선 시간 값을 최대 사이즈로 담을 수 있는 변수를 선언합니다.

 

unsigned long millisTime = millis();

 

아두이노에 전원이 공급되면 그때부터 타이머가 동작하고 타이머 변수에 숫자가 증가하는데 그 값을 millis( ) 함수가 읽어서 millisTime 변수에 저장하게 됩니다. 

 

1초 = 1000 millisecond = 1000 msec로 표시합니다. 

 

void setup()
{
  Serial.begin(9600);  
}
void loop()
{
  unsigned long millisTime = millis();
  Serial.println(millisTime);  
}

 

millis( ) 함수를 통해 읽은 시간 값을 시리얼 모니터로 출력하는 것을 볼 수 있습니다.  1초가 1000입니다. millis( )함수로 읽은 시간 값을 나누어 볼까요? 

 

  unsigned long millisTime = millis();
    
  int v1 = millisTime%10; //일의 자리
  int v2 = (millisTime/10)%10; //10의 자리
  int v3 = (millisTime/100)%10; //100의 자리
  int v4 = (millisTime/1000)%10; //1000의 자리

 

C언어에서 몫과 나머지를 구하는 코드는 아래와 같습니다. 

몫 : 123/10 => 12

나머지 : 123%10 =>3

 

그러면, millisTime에 저장한 시간 값의 일의 자리를 구하려면 millisTime값을 10으로 나눈 나머지 값이 1의 자리가 됩니다. 이 원리를 이용해서 다음 10의 자리를 구하려면 어떻게 할까요. 일의 자리가 필요 없기 때문에 1의 자리를 버리려면 millisTime값을 10으로 나눈 몫의 값을 구하면 됩니다. 그러면, 10 이하의 1의 자리가 버려집니다. 그 몫에다 다시 10을 나눈 나머지 값을 구하면 10의 자리 숫자를 구할 수 있게 됩니다.

쉽게 수식으로 살펴보면,

 

123%10 -> 3 // 10으로 나눈 나머지를 구한다. 일의 자리 3이 구해진다.

(123/10)%10 => (12)%10 => 2 //일의 자리를 버리기 위해 10으로 나눈 몫을 가지고 10으로 나눈 나머지를 구한다.

(123/100)%10 => (1)%10 => 1 // 마찬가지

 

이렇게 자릿수 값들을 구하게 됩니다. 아래는 전체 코드입니다. 각 자릿수를 구하고 시리얼 모니터로 인쇄합니다.

 

void setup()
{
  Serial.begin(9600);
}
void loop()
{
  unsigned long millisTime = millis();
  int v1 = millisTime%10;
  int v2 = (millisTime/10)%10;
  int v3 = (millisTime/100)%10;
  int v4 = (millisTime/1000)%10;

  Serial.print(v4);
  Serial.print(" : ");
  Serial.print(v3);
  Serial.print(" : ");
  Serial.print(v2);
  Serial.print(" : ");
  Serial.println(v1);
}

 

초단위로 일의 자리를 출력되게 하려면 다음과 같이 해줘야 합니다.

 

millisTime = millis()/1000;

 

처음 millisTime에 millis() 값을 1000으로 나눈 몫으로 하면 millisTime변수에 1이 들어 있으면 그 숫자는 1초가 됩니다. 일의 자리가 1초이므로 시간을 초로 계산하여 나타내는 코드입니다. 

 

  unsigned long millisTime = millis()/1000;
  int v1 = millisTime%10;
  int v2 = (millisTime/10)%10;
  int v3 = (millisTime/100)%10;
  int v4 = (millisTime/1000)%10;

 

위 코딩에서 millis()/1000으로 세팅하면 1초가 일의 자리로 해서 출발하게 됩니다. 이 방법과 유사하게 아래와 같은 방식으로도 표현이 가능합니다. 

 

unsigned long timeVal=0; //이전 시간

void loop(){
  if(millis()-timeVal>=1000){
   d1++; 
   timeVal=millis();
  }
  if(d1==10){
    d1=0;
    d2++; 
  }
  if(d2==10){
    d2=0;
    d3++;    
  }
  if(d3==10){
    d3=0;
    d4++;    
  }
  if(d4==10){
    d4=0;    
  }
}

 

if문으로 현재시간(millis()) - 이전 시간(timeVal)의 차이가 1000보다 크거나 같다면 1초를 의미하게 됩니다. 그때 d1을 1씩 증가시키게 됩니다 이 말은 1초씩 증가한다는 의미랑 같습니다. 그리고, 1초일 때마다 timeVal은 millis( ) 함수의 값을 저장함으로써 다음 1초를 비교하기 위한 이전 시간 값이 저장됩니다. 

 

시간을 쪼개는 이유 

 

7-segment display 와 같은 부품에 값을 출력할 때는 각 자리 숫자를 분해가 필요합니다. 타이머 카운터 같은 곳에서 응용으로 사용하기 딱 좋겠죠. 그리고, 이번에 post에서 다룬 이유는 다음에 4-digit 7-segment display를 사용한 실험을 하기 위해서입니다. 1초 단위로 숫자를 4-digit 7-segment display에 출력을 한다면 millis( ) 함수에서 읽은 시간 값을 분해를 할 수 있는 능력이 필요합니다.  아두이노 millis( )함수를 이용해 시간을 읽어와 분리하는 방법을 알았다면 7-segment 에 시간을 표시하는 방법을 실습하셔도 됩니다. 즐 개발하세요~^^

 

 

참고

시간 millis()함수로 읽은 숫자 쪼개기 (아두이노) 

 

 

 

 

 

반응형