[아두이노] timer0_millis 리셋 시키기

IOT/아두이노|2019. 5. 14. 09:00

[아두이노] timer0_millis 리셋 시키기



오늘은 아두이노 내부 시간을 출력하는 timer를 리셋시키는 실험을 하겠습니다. 아두이노에 전원이 공급되는 그 순간부터 0부터 timer가 돌기 시작합니다. 그리고 millis()함수을 통해서 현재 시간값을 얻게 됩니다. 시간값 1000은 1초를 나타내고 전원이 끊어질 때까지 계속 돌게 됩니다. 여기서, 아두이노가 무한으로 계속 숫자가 증가할까요. 그렇지 않습니다. 시간값은 unsigned long 자료형으로 그 값에는 한계가 있고 한계값을 넘게 되면은 리셋이 됩니다. 아두이노를 실험하면서 몇날 몇일을 계속 켜놓을 일은 거의 없겠죠. 그래서 대수롭지 않게 시간을 사용하는데 장기간 사용할 시에 시간변수 값을 제어하면 좀 더 효율적으로 시간을 관리할 수 있기 때문에 한번 이 변수에 대해서 이야기하면 좋을 것 같아서 오늘의 주제로 결정했습니다.

이제 간단히 이 변수에 대해서 실험을 해보도록 하겠습니다.

1. timer0_millis 변수


아두이노 어느 위치의 라이브러리에에 이 변수가 선언되어 있습니다. 이 값은 아두이노가 전원이 공급되면 0부터 1씩 증가합니다. 그리고 1000이 되면 1초의 시간이 흘러갑니다.

변수는 다음과 같습니다.

unsigned long timer0_millis;

long형의 자료형인데 unsinged(부호가없는) long형이니깐 음수의 숫자만큼 양수로 표현될 수 있기 때문에 양수로 표현되는 숫자는 그냥 long형으로 표현한 숫자보다 2배의 숫자로 시간을 표현할 수 있습니다.

unsigned long => 4,294,967,295 (2 ^ 32 - 1)

대충 이정도의 양의 숫자를 표현할 수 있는데 timer0_millis으로 날짜로 계산해보면은 다음과 같습니다.

1초 => 1000
1분 => 60초
1시간 => 60분
1일 => 24시간

=> 1일 = 1000*60*60*24 = 86,400,000

4,294,967,295/86,400,000 = 49.71026961805556일

이렇게, 계산이 되어 나옵니다. 한달 이상을 타이머가 돌다가 리셋이 되겠죠. 아두이노를 실험을 할 때 이정도로 오랫동안 켜놓고 하는 실험은 드물겠지요.

뭔가 직접 시간에 대한 제어가 필요할 것 같지 않나요. timer0_millis의 변수를 접근할 수 있다면 원하는 시간 만큼 타이머가 돌다가 강제적으로 리셋 시킴으로써 어떤 시간단위로 동작 패턴을 만들어 낼 수 있게 됩니다.

타이머를 건들 수 있다는 것은 시간을 제어할 수 있다는 의미랑 같습니다. 지금은 딱히 활용할 곳이 생각이 안나시더라도 시간을 건들 수 있다는 것을 알고 있으면 나중에 시간에 관계된 제어를 할 때 유용하게 사용할 수 있을 꺼에요.

2. millis() 함수



millis()함수로 현재 timer값을 가져올 수 있습니다. millis()함수가 호출하면 반환되어 나온 값을 통해서 시간을 제어할 수 있습니다. 우리고 delay(1000)이라는 1초 단위로 부품을 제어했는데 delay(1000)은 강제적으로 아두이노를 쉬게 했지만 millis()함수를 이용하여 강제적인 delay 없이 millis()값을 통해서 원하는 시간 단위로 부품을 제어 할 수 있게 됩니다. 즉, millis()함수는 딜레이 없이 시간을 제어하고 싶을 때 자주 사용되는 함수입니다.

복습차원으로 링크 걸어놓은 delay()함수 없이 delay 제어를 살펴봅시다.

 timeVal=millis();
 if(timeVal-previousVal>=1000){   
    state=!state;
    digitalWrite(redPin,state);
    previousVal=timeVal;
  }

timeVal 값은 millis()함수의 현재 timer0_millis의 값을 가져오게 됩니다.

timeVal-previousVal>=1000
현재시간값과 이전 시간값의 차이가 1000 이상이면은 1초이상이면 참이되는 IF문입니다. 즉, 시간을 1초 단위로 딜레이 없이 제어할 수 있다는 의미이고요. 1초 단위로 "state=!state"로 상태값이 반전으로 redPin에 true or false 상태가 1초단위로 왔다 갔다 하게 됩니다. 그리고 1초 단위가 만족할 때 "previousVal=timeVal"이전시간값에 저장합니다. 다음 1초를 준비하게 됩니다.

timer0_millis 변수값이 무척 중요하지요. 이 변수값을 건들 수 있게 되면 마음데로 시간을 제어 할 수 있게 됩니다. 지금까지는 규칙적인 시간을 흘러갔지만 timer0_millis변수 값을 건들 수 잇게 되면 불규칙적인 시간으로 움직이게 할 수 있고 일정 패턴 시간 단위로 시간을 리셋 시킬 수 있습니다. timer0_millis변수를 건들 수 있다는 것은 시간을 마음대로 제어 할 수 있기 때문에 많은 것들을 할 수 있게 됩니다.

그럼 간단히 실험을 통해서 제어를 해보겠습니다.

3. 회로도 구성


  • 준비물 : 아두이노우노
  • 내용 : 코딩으로 실험 하기 때문데 따로 준비사항은 없습니다.


4. 코딩


extern volatile unsigned long timer0_millis;

이 한줄이 timer0_millis 변수를 건들 수 있게 됩니다. 프로그램 전체에 영향을 주는 extern(외부전역변수) 인데 이상태로만 표현해도 접근 할 수 있지만 volatile 추가로 더 붙입니다. 그 이유는 volatile가 없이 선언하면 컴파일시 컴파일 최적화로 만들어지지만 volatile을 붙이면 컴파일 최적화가 아닌 사용자 의도에 따라 변경이 가능한 컴파일이 됩니다.

아무튼 이렇게 timer0_millis변수를 선언하면 해당 시간 관련 라이브러리에 선언된 timer0_millis변수의 값을 이 변수에 의해서 값이 변경되게 됩니다. 아두이노 로직을 짠 코딩안에서 timer0_millis변수 값을 바꾸면 millis()함수로 반환 되어 나오는 시간값은 바꾼 값으로 나오게 됩니다.

간단히 코딩으로 변경이 되는지 살펴 볼까요.

extern volatile unsigned long timer0_millis; //타이머변수

unsigned long timeVal=0;  //현재시간값 저장변수
unsigned long previousVal=0; //이전시간값 저장변수

const int redPin = 13; //RED LED PIN
boolean state = false; //LED 상태값

void setup()
{
  Serial.begin(9600); 
  pinMode(redPin,OUTPUT);
}

void loop()
{
  timeVal=millis();
  //1초 단위로 RED LED 깜박임
  if(timeVal-previousVal>=1000){ //1초단위
    state=!state;
    Serial.println(timeVal);
    digitalWrite(redPin,state);
    previousVal=timeVal; 
  }  
  //6초후 timer 리셋
  if(timeVal>=6000){
     timer0_millis=0;  //6초 후 시간값 리셋
     previousVal=0;
     Serial.println("reset");    
  }
}

두개의 IF문이 있습니다.

  if(timeVal-previousVal>=1000){ //1초단위
     명령문
     previousVal=timeVal; 
  }

위 IF문은 "현재시간-이전시간>=1000" 만족하면인데 이것은 현재시간이 이전시간과의 차이가 1000이상되면 1초이상이 되기 때문에 1초단위로 특정 명령을 수행할 수 있게 됩니다. 그리고, 명령을 수행한 뒤에 이전시간변수에 현재시간을 저장해야지 다음 1초를 준비할 수 있게 됩니다.

if(timeVal>=6000){
 timer0_millis=0;  //6초 후 시간값 리셋
}

위 IF문은 "현재시간>=6000"이 될때 참인데 이것은 현재시간이 6초이상이 되면 참으로 timer0_millis변수 값을 0으로 초기화 함으로서 타이머시간을 리셋을 시키게 됩니다. 즉, 강제적으로 다시 처음부터 millis()함수의 반환되어 나오는 값이 0부터 시작하게 됩니다.

5. 실험 결과



6. 타이머 시간 변경을 통한 접근 제어


위 실험에서는 간단히 6초 단위로 타이머 시간을 리셋 시켰습니다. 우리가 어떤 부품을 시간 단위로 제어를 한다고 가정했을 때 상황을 설정할 경우는 각 부품의 접근에 대해 제어할 수 있게 됩니다.

가령, 1초때 A부품, 5초때 B부품, 10초때 C부품이 있다고 생각해봅시다.

if(timeVal-timeA>=1000){ //1초단위

     timeA=timeVal; 
}
if(timeVal-timeB>=5000){ //5초단위

     timeB=timeVal; 
}
if(timeVal-timeC>=10000){ //10초단위

     timeC=timeVal; 
}

이럴 때, 상황 조건에 따라서 특정 측정값이나 또는 계산 처리식에서 특정 부품을 건너 뛰기가 가능하게 됩니다. 즉, A->B->C 동작이 순차적으로 원래 진행되는데 특정 상황이 발생하면 A->B에서 동작을 마무리 할 수 있습니다.

if(특정상황 조건식){
  timer0_millis=0;
  timeA=0;
  timeB=0;
  timeC=0;
}

시간을 제어한다는 것은 자신이 아두이노의 기본 순차적인 동작을 자신이 의도하는 방향으로 불규칙적인 시간을 통해 원하는 방향으로 동작을 제어도 가능하게 됩니다. 참 재밌는 접근 방식이지요.

마무리


복습차원으로 두개의 IF문으로 첫번째는 delay()함수 없이 시간을 제어한 문장인데 아두이노에서 가장 많이 사용하는 표현입니다. 두번째는 "timer0_millis=0"으로 타이어를 리셋시키는 문장인데 이 부분은 아직 초보분들은 쓸만한 곳은 없습니다. 하지만 이 표현을 기억해 뒀다가 나중에 코딩 로직에 따라서 이 표현을 쓸 수 있으니깐 꼭 기억해 두셨으면 합니다.

가령, 시간단위로 어떤 부품들을 제어하는데 그 시간단위를 어떤 조건에 따라서 시간을 건너 뛸수도 있고 아니면 처음부터 다시 어떤 부품의 동작을 시작하도록 할 때 사용할 수 있습니다. 아직은 와 닿지 않을 꺼에요. 그래도 외부변수 선언하는 저 한줄만 기억하면 timer0_millis 변수를 마음대로 제어 할 수 있으니깐 알아만 두세요.

오랫만에 가상시뮬레이터로 실험할 수 있는 post였네요

댓글()