안녕하세요~!


이번시간에는 delay() 함수와 millis()의 차이에 대해 살펴보겠습니다.


일반적으로 LED를 깜빡이거나, 단순한 ON/OFF의 반복이 지속될때는 delay()함수를 많이 이용하죠?


아두이노 Blink 예제를 보셔도 delay()함수를 이용해 LED를 켰다 껐다 한답니다.


그럼 만약에 LED 두개를 엇갈려서 1초 간격으로 껐다켰다 하려면 어떻게 할까요?


아래 그래프처럼 보이게 만들어 봅시다.


자 그럼 LED1은 1초동안 깜빡이고, 그 중간 타이밍에 LED2를 깜빡이면 되니 직관적으로 코드를 짜보면



const uint8_t LED1 = 10;
const uint8_t LED2 = 11;

void setup() {
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
}

void loop() {
  //LED1 Blink
  digitalWrite(LED1,HIGH);
  delay(1000);
  digitalWrite(LED1,LOW);
  delay(1000);

  //LED2 Blink
  digitalWrite(LED1,HIGH);
  delay(1000);
  digitalWrite(LED1,HIGH);
  delay(1000);
  }


이런식이 될텐데, 실제로 이 코드를 돌려보면 원하는 형태로 움직이지 않는답니다.

아마 아래와 같은 식으로 켜졌다 꺼졌다 할겁니다.


이렇게 되는 이유는 바로 delay()함수때문인데요, delay()함수가 실행되면 

해당 시간(ms)동안은 아무런 동작도 하지않고 그냥 정지! 상태입니다.


즉, delay()함수뒤에 그 어떤 코드가 있더라도 실행이 되지않는답니다!



그럼 이를 해결할수 있는 방법은 두 가지가 있어요.


1) delay()함수를 쓰되, 최소 시간단위로 쪼개서 코딩하는 법


2) millis()를 써서 특정 시간이 되면 특정 기능을 실행


먼저 1번 방법부터 봅시다.


# 1 


최소 시간단위로 쪼갠다는게 무슨 의미냐 하면, 

상태가 변하는 순간부터 다음 상태가 변하는 순간까지의 시간을 단위시간이라 하고,

이를 기준으로 코딩한다는 겁니다.


맨 처음 그림에서 보면 상태는 500ms 마다 변하고있죠?

그럼 최소단위시간은 500ms가 된다는겁니다.


이에 맞게 코딩을 해보면,


const uint8_t LED1 = 10;
const uint8_t LED2 = 11;

void setup() {
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
}

void loop() {

  digitalWrite(LED1,LOW);
  digitalWrite(LED2,LOW);
  delay(500);

  digitalWrite(LED1,HIGH);
  digitalWrite(LED2,LOW);
  delay(500);

  digitalWrite(LED1,HIGH);
  digitalWrite(LED2,HIGH);
  delay(500);

  digitalWrite(LED1,LOW);
  digitalWrite(LED2,HIGH);
  delay(500);
}


이런 식으로 짜면 딱 맞게 동작하겠죠?


하지만, 여전히 delay()함수때문에 이 외의 코드를 추가한다거나,

analog 신호를 읽어올때는 제대로 실행이 되지 않는답니다.



# 2


그렇다면 delay함수때문에 뒤의 코드가 실행이 되지 않는다면 어떻게 하면 될까요?


우선 delay()함수를 좀 더 뜯어서 살펴보면,



int delay_time = 500;


while(delay_time < 500){

  //Nothing.. just wait

  delay_time++;

}



이런 식으로 빈 while루프가 특정 시간이 되기전까지 무한히 도는 형식의 코딩이 된답니다.


하지만 임의로 우리가 delay함수 내부의 while루프에 특정한 코딩을 할 수가 없기때문에, 

위 함수의 형식을 빌려와서 void loop() 메인 루프에 그대로 덧씌워 delay()함수를 모사한다면

조금 더 자유로움 delay()함수를 사용할 수 있게 되겠지요.



그럼 어떻게 할까요?


단순합니다. 매 루프마다 millis()라는 함수를 사용해서 현재 아두이노의 시간이 몇 ms인지 파악하고,

그 시간이 우리가 원하는 지정된 시간 이상이 되면 특정 함수를 실행하는 거죠.


단, millis()는 조금만 시간이 지나도 숫자가 매우 커지기때문에,

가장 큰 길이의 데이터 형태인 long을 사용하고,

음수의 시간데이터는 나오지 않기 때문에 unsigned를 붙혀 데이터의 가용범위를 늘립니다.


자, 그럼 LED 1개를 깜빡이는 코드를 millis()로 구현해보면,



const uint8_t LED1 = 10;


// Set timer

unsigned long pre_time = 0;

unsigned long cur_time = 0;


// Set LED status

boolean state_led1 = 0;


// Set duration

const int duration = 1000;


void setup() {

  pinMode(LED1, OUTPUT);


  // Initialize previous counter time

  pre_time = millis();

}


void loop() {


  // Update current time in every loop

  cur_time = millis();


  // If time gap between previous and current goes over the duration,

  // run digital write!

  if(cur_time - pre_time >= duration){


    // Change the boolean state.

    state_led1 = ~state_led1;


    digitalWrite(LED1,state_led1);


    // Update previous counter time.

    pre_time = cur_time;

  }


  else{

    // Do something...

  }

  

}



이런 코드가 나오게 됩니다. 

시간을 계속 업데이트하고,

-> 지정된 시간만큼의 차이가 나는지 확인

-> 만약 차이가 아직 안나면 else()구문으로 넘어가 평소 작업 수행

-> 막약 차이가 나면 if()구문 안의 작업을 수행

-> 작업 완료되면 pre_timer를 현재 값으로 업데이트 해주고 if()구문 끝


이 예제는 LED가 1개일때 사용하는 예제이구요, 

위에서 언급되었던 LED 2개를 번갈아가며 켜는 예제의 경우는

위의 코드를 보고 한번 짜보세요^^


하지만 이러한 millis()의 경우도, 완벽한 해결책이 되지 못할때가 많습니다.

예를 들어 else()구문의 코드가 굉장히 오래 걸려 100ms 가 걸렸다면, 

LED는 100ms이하의 속도로 깜빡거릴수가 없겠죠..?


이런걸 해결하는 방법이 바로 '타이머(Timer)' 랍니다.

타이머와 관련된 자료는 여기서 볼 수 있습니다^^


궁금하신 점이 있으시면 댓글 남겨주세요~!


그리고 도움이 되셨다면 공감 버튼 한번만 눌러주세요^^

+ Recent posts