초급 예제

누구나 쉽게 따라해볼 수 있는 쉬운 예제들입니다. 가볍게 도전~!

BlinkWithoutDelay

2015-07-28 15:18:32

개요

 

코드를 사용하다 보면 코드는 작성한 순서대로 위에서 아래로 명령어를 진행하는 흐름이 있기 때문에 중간에 멈추게 하고 싶다면 별도의 명령어를 추가하여 그 흐름을 멈추어야 합니다. 

아두이노 코드에서 그 역할을 하는 명령어 함수가 바로 Delay()인데요. Delay()함수는 간단하게 안에 MilliSecond의 값만 넣어준다면 그 시간 만큼 아두이노의 loop()문이 작동하지 않게 할 수 있습니다.

 

근데 아두이노의 경우에는 싱글 쓰레드(Single Thread)이기 때문에 한번에 두 가지일을 하지 못하기 때문에 Delay()의 역할이 상당히 중요합니다.

 

하지만 역할에 비해 상당히 사용하는 방법이 쉽기 때문에 누구나 마구잡이로 Delay()함수를 사용하는 경우가 많습니다. 근데 이 Delay()함수는 코드의 흐름을 제어하는 함수이기 때문에 마구잡이로 사용 할 경우 코드의 흐름을 방해하거나 통신을 할 경우 동기화(Sync)에도 큰 영향을 끼치기 때문에 그 사용에 주의해야 합니다. 

 

이번 글에서는 Delay()함수 대신에 코드의 시간을 제어할 수 있는 방법을 알아보겠습니다.

 

 

 

Delay()함수를 사용 했을때의 문제점

 

Delay()함수를 사용하여 스위치를 눌렀을 때 LED가 5초간 켜졌다가 꺼지고 시리얼 모니터 창에 a라는 문자를 입력하면 'Hello'라는 문구를 출력하는 간단한 소스를 만들어 보겠습니다.

스위치와 LED를 준비하여 아두이노 회로를 아래와 같이 구성합니다.

 

 

 

 

 

이제 소스를 코딩해 봅시다.

 

int led = 3;
int sw = 5;

void setup() {
        pinMode(led, OUTPUT);
        pinMode(sw, INPUT_PULLUP);
        Serial.begin(9600);
}

void loop() {
        // 시리얼 입력값을 읽는다.
        char c = Serial.read();   
        
        // 스위치를 눌렀을 경우 LED가 Delay(5000)으로 5초간 켜졌다가 꺼진다.
        if (digitalRead(sw) == LOW) { 
                digitalWrite(led, HIGH);
                delay(5000);
                digitalWrite(led, LOW);
        }
        // 스위치가 눌리지 않았을 경우 'a'라는 글자를 입력하면 'Hello'라는 문구를 출력한다.
        else {
                if (c == 'a') {
                        Serial.println("Hello");
                }
        }
}

 

 

 

위의 코드는 스위치가 눌렸을 경우 LED는 5초간 켜졌다가 꺼지며 시리얼통신으로 'a'라는 값을 받았을 경우 'Hello'라는 문구를 출력하는 소스 입니다.

소스를 보게되면 별 문제도 없고 업로드 또한 정상적으로 이루어 집니다. 

하지만 막상 소스를 실행하다보면 문제점이 발견됩니다.

 

스위치를 눌러 LED를 켰을 때, 시리얼 모니터 창에 'a'라는 값을 입력할 경우 'Hello'라는 문구가 제대로 뜨지 않습니다. 

그러다가 LED가 꺼지고 나서야 'Hello'라는 문구가 시리얼 모니터 창에 뜨는것을 볼 수 있습니다.

만약에 'Hello'라는 글씨가 뜨지 않는다고 a를 계속적으로 입력 할 경우 LED가 꺼지고 나서야 a를 입력한 횟수만큼 'Hello'가 출력되는 것을 볼 수 있습니다.

 

모든 문제는 digitalWrite(led,HIGH)와 digitalWrite(led,LOW)사이에 있는 delay(5000)때문에 일어나는 문제인데요.

delay()함수는 실행될 경우 아두이노의 모든 코드 동작을 일시적으로 중단시켜 버립니다. 그렇기 때문에 delay()가 실행되고 있는 동안에는 시리얼 모니터에 아무리 'a'값을 입력해도 'Hello'라는 문구는 출력되지 않습니다.

 

이러한 문제가 바로 많은 분이 얘기하는 "통신을 하는데 센서값이 제대로 출력되지 않아요"라던가 "LED를 깜빡이게 하려고 하는데 값을 입력해도 LED가 지멋대로 깜빡깜빡 거려요"라는 문제를 발생시킵니다.

특히 통신에서 값을 받아서 특정한 출력을 하고 싶을 때 delay()가 존재할 경우 값을 보내도 출력이 지연되어 나온다던가 자기멋대로 출력되는 결과가 생길 수 있습니다.

(저 같은 경우에는 LED를 사용할 때 깜빡거릴때 아무생각없이 delay()함수를 추가하게 되는데 이때 LED여러개를 동시에 불규칙적으로 깜빡깜빡거려야 할때 문제가 발생합니다.)

 

이러한 문제를 해결하기 위해서는 근본적인 문제인 delay()의 사용을 없애야 겠죠.

밑에서는 delay()의 사용이 없이 LED를 깜빡여보겠습니다.

 

 

 

BlinkWithoutDelay사용해 보기

 

이번에는 똑같은 기능을 하는 소스를 delay()함수를 사용하지 않고 만들어 보겠습니다.

 

아두이노회로도는 위의 회로도와 동일하게 만들어 줍니다.

 

 

 

회로도를 똑같이 구성하였으면 이번에는 소스코드를 다르게 구성하여 만들어 봅시다.

 

long previousMillis = 0; 
// Interval을 5초로 지정
long interval = 5000; 

int led = 13;
int sw = 5;

void setup() {
        pinMode(led, OUTPUT);
        pinMode(sw, INPUT_PULLUP);
        Serial.begin(9600);
}

void loop() {
        // 현재 시간 측정
        unsigned long currentMillis = millis(); 
        // 시리얼 입력값을 읽는다.
        char c = Serial.read(); 
        
        if (digitalRead(sw) == LOW) {
                previousMillis = currentMillis;  
                digitalWrite(led, HIGH);
        }
        else {
                // a라는 글자를 입력한 경우 Hello문구 출력
                if (c == 'a') { 
                Serial.println("Hello");
                }
        }
        
        // LED가 켜진지 5초뒤에 LED를 끈다
        if (currentMillis - previousMillis > interval){ 
                digitalWrite(led, LOW);
        }
}

 

 

이번 코드는 위의 delay()를 사용한 코드와 기능은 같지만 구현 방식이 약간 다릅니다.

 

시간의 차이를 이용하여 delay()를 구현하는데 매 루프마다 Millis()를 사용하여 현재 시간을 측정합니다. 그 후에 특정 이벤트가 발생하면 그 이벤트의 발생시간을 측정하고 변수에 저장합니다.(previousMillis) 이벤트의 발생시간과 현재 시간을 비교하여 특정 시간 이상이 되면 수행해야할 명령어를 실행합니다.

 

위 코드를 예를 들면

1. 매 loop마다 현재 시간을 측정하여 currentMillis에 저장합니다.

2. 스위치를 누르면 previousMillis라는 변수에 스위치를 누른시간을 체크해서 저장합니다.

3. 매 loop마다 현재시간(currentMillis)을 체크해서 스위치를 누른시간(previousMillis)이 5000(5초)가 되면 LED를 끕니다.

4. delay()가 존재하지 않기 때문에 스위치를 눌러 LED가 켜지고 꺼지는 동안에도 시리얼모니터의 출력은 제대로 동작합니다.

 

 

어떻게 보면 일종의 편법으로 볼 수 있지만 Arduino IDE에서도 이 방법에 대해서는 예제로도 공개하고 있습니다.

굳이 delay()를 사용하지 않고도 시간을 계속적으로 체크하여 delay()의 효과를 유지하면서도 소스의 흐름이 끊기지 않게 만들 수 있습니다.

 

더 간단한 소스는 파일-예제-02.Digital-BlinkWithoutDelay로 볼 수 있습니다.

 

kocoafabeditor

항상 진취적이고, 새로운 것을 추구하는 코코아팹 에디터입니다!

BlinkWithoutDelay, 함수, 기타

원기옥 2015-07-29 15:32:20

thread 라이브러리를 사용해볼 수 도 있습니다. 일반적인 thread 방식은 아니지만요.