Delay에 해당하는 글 1

[아두이노] delay()함수 안쓰고 delay 제어하기

IOT/아두이노|2019. 3. 16. 09:00

[아두이노] delay()함수 안쓰고 delay 제어하기



지금까지 delay()함수를 통해 시간 제어를 해왔습니다. delay()함수는 아두이노를 그 시간동안 암것도 하지 못하게 강제휴식 상태가 되도록 합니다. 지금까지는 대부분 하나의 부품을 제어 할때 delay()함수를 써왔고 별 문제가 발생하지 않았습니다. 하지만 부품 둘 이상 결합해서 표현 해보셨다면 정상적으로 동작하지 않는 현상이 생겼을거라 생각됩니다. 그 이유는 2개 부품이 있는데 한개의 부품의 delay() 시간을 줄때 다른 부품도 같이 대기 상태에 빠지게 됩니다. 가령 A라는 부품을 1초 대기할때 강제적으로 B도 같이 1초 대기하기 하게 됩니다. B 부품은 뭔가 작업을 해야하는데 A 부품의 delay()함수에 의해서 강제로 대기상태에 빠지게 됨으로 제대로 동작하지 않는 현상이 발생하게 됩니다. 이런 문제를 해결하기 위해서 delay() 함수를 쓰지 않고 delay 효과를 얻는 방법을 살펴보도록 하겠습니다.

오늘은 간단한 코딩을 배우는 시간입니다.

1. delay() 함수 없이 delay 시키는 코딩


  • 사용함수 : millis()(타이머 작동하면서 시간이 생성되며 그 시간값을 추출할때 사용)
  • 내용 : 현재시간값 - 이전시간 = delay 시간인지 조건문으로 비교해서 delay 시키자.

  • 현재시간값 : 현재 시간값을 저장할 변수로 unsigned long(부호가 없는 long형 정수) 자료형으로 currentMillis 변수를 선언했습니다. 시간값을 int형으로 하기 보다는 최대한 큰 변수로 선언하는게 좋습니다. 시간에는 음수가 없기 때문에 숫자를 양수만 표기하는게 좋겠죠. 그래서 기존 long형에서 표현하는 숫자의 두배정도 양수로 표기할 수 있는 부호가 없는 long형으로 변수를 선언했습니다.
    그냥 변수에 대해 이해가 안가시면 큰수를 저장할 수 있는 자료형이구나 정도로 이해하세요. 작은 수면은 int형 큰수면 long형 이정도만 우선 머리속에 담아두시면 됩니다.
unsigned long currentMillis = millis();

현재 시간값을 가져왔으면 다한 거나 마찬가지입니다. loop() 함수가 어떻게 동작한다고 했죠. 아두이노가 처음 전원이 공급되면 setup()함수가 처음 한번 수행한 뒤에서 loop()함수가 무한 반복 수행 한다고 했죠.

만약에 시간값 구하는 위 코드가 loop() 함수안에 있다면

void loop(){
    unsigned long currentMillis = millis();
}

loop()함수가 매 반복할 때마다 현재 시간을 추출하여 currentMillis 변수에 저장하게 됩니다. 처음 loop()함수가 호출이 되면 currenMillis 변수에 현재시간이 저장된 후에 두번째 loo()함수가 호출하게 되면 두번째 현재시간이 저장되겠죠. 그러면 첫번째 호출되어 저장된 현재시간은 이전시간이 되고 두번째 호출된 시간이 현재시간이 되겠죠. 여기서 currentMillis는 두번째 현재시간을 저장하기 전에 첫번째 저장된 값을 어딘가에 옮겨서 보관해야겠죠.

previousMillis = currentMillis;

간단하죠. 변수를 하나 더 만들어서 현재변수에 저장된 값을 이전변수에 저장하면 쉽게 해결됩니다.

딜레이 시간은 어떻게 구할까요.

대기시간 == 현재시간 - 이전시간

delayTime == currentMillis - previousMillis;

참 쉽죠. 현재시간에서 이전시간을 뺀 시간이 delayTime과 같아야 겠죠.

이제는 조건문으로 1초 단위로 딜레이 시켜볼까요.

unsigned long currentMillis = millis();

if(currentMillis - previousMillis >= delayTime){
  previousMillis = currentMillis;   
}

currenMillis의 시간값이 previousMillis값을 뺀 값이 delayTime시간보다 클경우 if문은 참이 됩니다. 왜 같다가 아니라 delayTime시간 이상일 경우에 참이라고 했을까요. 아두이노는 각 코딩라인을 수행할때마다 인간이 감지할 수 없지만 그래도 시간이 소요 됩니다. 결론은 현재시간을 측정할때 정확히 1초(1000)를 일치시키기 어렵습니다. 근사 초로 비교해야 합니다. 가령 1001이 현재시간이면 이것을 1초가 아닌게 아니잖아요. 1000,1001,1002 이렇게 측정될 경우 우리는 근사초로 1초로 보정해줘야 합니다. 그래서 delayTime 초보다 클경우를 참으로 잡는 이유가 되겠습니다.

delayTime(대기시간) 만큼이 만족하면 if문 안의 문장이 수행되고 이때 delay시간만큼 이동한 현재 시간이 previousMillis(이전시간) 변수에 currenMillis(현재시간)값을 저장해야겠죠. 왜냐면 다음 시간값과 비교해야하니깐요. 이렇게 표현함으로서 delayTime(대기시간)값에 만족하는 참이 될때마다 if문 안의 문장을 수행하는 로직이 됩니다.

void loop() {
  unsigned long currentMillis = millis();
  
  if(currentMillis - previousMillis >= delayTime){
    previousMillis = currentMillis;
    원하는 동작;
  }  
}

여기서 previousMillis, delayTime 변수는 외부로 빼줘야 합니다. 그 이유는 함수내에 선언된 변수는 함수가 끝나면 소멸합니다. 그래서 안에다 변수를 일반적으로 선언하면 이전 시간값은 사라지게 됩니다. 간단히 외부에 선언하시면 되겠습니다. 이전시간변수는 현재시간과 동일하니깐 부호가 없는 long형으로 해줘야하고 대기시간은 1(1000)초만 할꺼니깐 작은수이기 때문에 int형으로 선언하시면 되겠습니다.

unsigned long previousMillis = 0; 
const long interval = 1000;

2. 회로도 구성


  • 준비물 : Red LED 1개, 저항 220옴 1개, 아두이노우노
  • 내용 : Red LED 깜박이는 기본 에제 회로로


참 쉽죠. 회로도 별거 없지요. delay 원리를 쉽게 이해하기 위해 기본 LED 제어를 활용 했네요.

3. 코딩



LED 기본예제 소스(1초 단위로 LED 깜박이기)

void setup() {
    pinMode(13, OUTPUT);
}

void loop() {
    digitalWrite(13, HIGH);
    delay(1000);
    digitalWrite(13, LOW);
    delay(1000); 
}

딜레이효과 소스

unsigned long previousMillis = 0; 
const long interval = 1000;

void loop() {
  unsigned long currentMillis = millis();
  
  if(currentMillis - previousMillis >= delayTime){
    previousMillis = currentMillis;
    원하는 동작;
  }  
}

두 코딩을 합치면

int led = 13; //Red LED 핀번호
unsigned long previousMillis = 0; //이전시간
const long delayTime = 1000; //1초(1000) 대기시간
boolean ledState = false; //LED 현재상태

void setup() {
  pinMode(led, OUTPUT); //13번 핀을 출력모드로 사용.
}

void loop() {
  unsigned long currentMillis = millis(); //현재시간값 가져옴
  
  if(currentMillis - previousMillis >= delayTime){ //1000초 시간이 흘렀는지 체크
    previousMillis = currentMillis; //1초가 지나 참임으로 1초 지난 현재시간을 이전시간에 저장
    ledState=!ledState;   //1초 if문이 참이니깐 1초 단위로 ledState 값을 반전시키면 1초 단위로 참/거짓됨.
    digitalWrite(led, ledState);   //참(5V) or 거짓(0V) 
  }  
}

코드 주석을 달아 놓았으니깐 따로 설명은 필요 없겠죠.

두개 소스를 합쳐놓은 것에서

ledState=!ledState;   
digitalWrite(led, ledState);

이 표현만 추가된거죠. 인터럽트 예제에서 활용했던 방식인데 부울변수로 현재의 상태를 반전시킬때 많이 사용합니다. 디지털 출력은 0이 아닌 모든 값은 참이되고 0은 거짓이 됩니다.
그래서 부울변수로 True(1), False(0)의 값을 이용하여 1(5V), 0(0V)의 전류상태가 되는 원리를 이용한 것이죠. 반전원리를 사용할때 가장 많이 사용되는 기법입니다. 위 두 코딩을 합치는 부분에서 이 부분만 약간 추가되었을 뿐 나머진 그대로니깐 어렵지 않을거에요.

4. 결과


delay() 함수를 안써도 1초 단위로 그림에서 보는 것처럼 정상적으로 동작하네요.


마무리


delay() 함수를 안쓰게 되면 그 시간에 다른 작업을 수행 할 수 있게 됩니다. delay()함수를 써서 아두이노가 강제로 넌 쉬어! 올 스톱이 되는일은 이제 없게 된거죠. 그러면 2개이상의 부품에서 delay()함수를 사용할 경우 대기시간 문제에서 벗어났습니다.

코딩은 어렵지 않은데 정확히 어디에 써야할지 잘 연상이 안되실 수 있지만 우선 이런식으로 delay()함수 없이 1초 단위로 RED Led를 깜박이게 할 수 있구나 정도만 이해하시면 되겠습니다.

어제 포스팅한 실험을 그제 밤에 증흥적으로 3개의 주제를 합쳐서 만들어 실험 포스팅을 해서 사전 준비 없이 좀 시간을 잡아 먹었네요 그래오 오늘 포스팅은 쉬운 주제로 휴식하는 기분으로 간단한 주제로 내용을 담았네요.

댓글()