[아두이노] 아두이노에서의 delay() 함수

아두이노에서의 delay() 함수

아두이노를 처음 시작하면 누구나 한번은 보게 되는 가장 쉬운 프로그램(아두이노의 ‘hello world’)는 아마도 아래 코드일 것이다. 

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

void loop() {
  digitalWrite(13, HIGH);   // LED ON
  delay(1000);              // 1초 대기
  digitalWrite(13, LOW);    // LED OFF
  delay(1000);              // 1초 대기
}

예제 -> 01. Basics -> Blink 코드로 아두이노 보드에 붙어있는 LED를 깜빡이게 하는 코드이다.

48912-arduinouno_r3_front%2B2.jpg


위의 사진에 ‘L’자 옆의 LED가 digital I/O 13번 핀에 연결되어 있는 것으로 2초 간격으로 깜빡이게 된다.
코드는 매우 직관적이어서 곧바로 이해할 수 있고 아두이노에서 프로그램을 만들면 delay() 함수를 많이 사용하게 된다.
하지만 위와 같이 단순하게 한가지 작업만 하는 경우는 상관없지만 일반적인 프로그램의 경우 동시에 여러가지 작업을 수행해야만 한다. 그 경우 delay() 함수가 사용되면 delay()에 지정된 시간만큼 대기하는 동안 프로그램 실행이 중단되어 다른 작업을 할 수가 없게 된다. 

그러므로 처음 코드를 이해할 때는 delay()함수가 편해도 실제 프로그램을 만들 때는 delay() 함수 사용을 최소화 해야만 한다. 예를 들어 2개의 스위치를 연결해 LED가 깜빡이는 속도를 조절하는 프로그램을 만든다고 생각해 보자.

단순하게 생각한다면 다음과 같은 식으로 코드를 만들 수 있을 것이다.

int gDelay = 1000;

void setup() {
  pinMode(13, OUTPUT);
  pinMode(7, INPUT_PULLUP);
  pinMode(8, INPUT_PULLUP);
}

void loop() {
  if (LOW == digitalRead(7)) {
    gDelay += 100;
  }
  if (LOW == digitalRead(8)) {
    gDelay -= 100;
  }
  gDelay = constrain(gDelay, 100, 2000);
    
  digitalWrite(13, HIGH);   // LED ON
  delay(gDelay);              
  digitalWrite(13, LOW);    // LED OFF
  delay(gDelay);              
}

위의 경우 스위치를 검사해 스위치가 눌리면 각각 delay 시간을 늘리거나 줄여주고, LED를 깜빡이는 코드가 다 포함되어 있다. 하지만 실행시켜 보면 스위치를 눌러도 깜빡이는 속도가 원하는대로 바뀌지 않을 것이다. 

Untitled.jpg


위의 그림에서 볼 수 있는 것 처럼 (1)과 (2)에서 스위치가 놀렸는지 검사하는데 만일 gDelay 변수 값이 1000이었다고 하면 2초에 한번씩만 스위치가 눌렸는지 검사하게 된다. 스위치를 검사하고 다음번에 다시 검사하는 2초 사이에 스위치를 눌러다 떼면 프로그램은 스위치가 눌린걸 알수가 없게 된다. 즉 delay()로 프로그램 실행이 중단되어 있는 동안에는 다른 작업을 하지 못하게 된다.

Untitled.jpg


이 문제에 대한 해결책으로 millis() 함수를 사용하면 된다. 아두이노에는 전원이 들어와 스케치가 시작되면 0에서 시작되어 1/1000초 단위로 1씩 증가하는 카운터가 있다. millis() 함수를 호출하면 호출된 시점의 카운트 값(unsigned long 타입)을 알려준다.

Untitled%2B2.jpg


위의 그림에서 b에서 a 값을 빼면 그 값이 a에서 b까지의 시간(ms 단위)이 된다. 

그러므로 위의 코드를 delay()를 사용하지 않도록 다음과 같이 바꿀 수 있다.

int gDelay = 1000;

void setup() {
  pinMode(13, OUTPUT);
  pinMode(7, INPUT_PULLUP);
  pinMode(8, INPUT_PULLUP);
}

void loop() {
  static unsigned long last = 0;
  static boolean ledStat = LOW;

  if (LOW == digitalRead(7)) {
    gDelay += 100;
  }
  if (LOW == digitalRead(8)) {
    gDelay -= 100;
  }
  gDelay = constrain(gDelay, 100, 2000);

  if ((millis()-last) >= gDelay) {
    ledStat = !ledStat;
    digitalWrite(13, ledStat);
    last = millis();
  } 
}

delay() 함수를 사용한 스케치에서는 loop() 함수가 한번 실행되는데 2초(gDelay가 1000인 경우)가 걸리지만, delay()함수를 사용하지 않은 스케치는 loop() 함수가 한번 실행되는데는 매우 짧은 시간이 걸려 2초동안 loop()함수가 수만~수십만번 실행되므로 스위치 상태도 그 횟수만큼 검사가 되기 때문에 사람이 아무리 빠르게 스위치를 눌렀다 떼더라도 감지하지 못하는 경우는 없다. 하지만 LED의 상태를 토글하는 코드(위에서 빨간색 부분)는 그 중 두번만 실행되게 된다.

Untitled%2B3.jpg

 
(물론 위의 코드는 스위치의 debouncing 처리가 안되어 있고 한번 눌린 동안 너무 여러번 스위치 상태를 감지하게 되기 때문에 스위치를 한번 눌렀을 때 gDelay 값이 크게 변하게 된다. 이런 문제들은 이 글의 주제와 다른 부분이라 다른 포스트에서 설명할 것이다.)

참고로 이런 문제들을 모두 해결한 코드는 아래와 같다.

#define SW1  8
#define SW2  9
#define LED  13

boolean gLedStat = HIGH;
int gDelay = 500;

void setup() {
  pinMode(SW1, INPUT_PULLUP);
  pinMode(SW2, INPUT_PULLUP);
  pinMode(LED, OUTPUT);
  Serial.begin(115200);
}

void loop() {
  static unsigned long last = 0;
  static unsigned long lastSw = 0;
  unsigned long now = millis();
  static boolean prev1 = HIGH;
  static boolean prev2 = HIGH;
  boolean cur;

  if ((now - lastSw) >= 10) {   // Check switch for every 10ms
    cur = digitalRead(SW1);
    if ((HIGH == prev1) && (LOW == cur)) {
      gDelay -= 100;
      gDelay = constrain(gDelay, 100, 2000);
      Serial.print("Delay up to ");
      Serial.println(gDelay);
      prev1 = LOW;
    } else if ((LOW == prev1) && (HIGH == cur)) {
      prev1 = HIGH;
    }
    cur = digitalRead(SW2);
    if ((HIGH == prev2) && (LOW == cur)) {
      gDelay += 100;
      gDelay = constrain(gDelay, 100, 2000);
      Serial.print("Delay down to ");
      Serial.println(gDelay);
      prev2 = LOW;
    } else if ((LOW == prev2) && (HIGH == cur)) {
      prev2 = HIGH;
    }
    lastSw = now;
  }
  if ((now - last) >= gDelay) {
    gLedStat = !gLedStat;
    digitalWrite(LED, gLedStat);
    last = now;
  }

}
0
0
이 글을 페이스북으로 퍼가기 이 글을 트위터로 퍼가기 이 글을 카카오스토리로 퍼가기 이 글을 밴드로 퍼가기

임베디드 보드

번호 제목 글쓴이 날짜 조회수
72 아두이노 ESP32 Analog Inputs (ADC) +4 icon 양재동메이커 02-12 12,153
71 아두이노 TIP : Serial의 Port가 Open 시점 확인 icon 양재동메이커 01-21 9,335
70 아두이노 ESP32 Boot Mode icon 양재동메이커 12-28 8,853
69 아두이노 아두이노 에러 리스트(Arduino Error list) icon 양재동메이커 11-24 13,505
68 아두이노 ESP32 main.cpp +1 icon 양재동메이커 11-19 9,452
67 아두이노 ESP32 EEPROM 와 IR Remote icon 양재동메이커 08-06 9,519
66 아두이노 Learn ESP32 icon 양재동메이커 06-25 9,000
65 아두이노 C 언어 비교문에서 == 사용 방법 icon 양재동메이커 04-12 9,584
64 아두이노 [아두이노 실습] 푸쉬버튼 long press, short press 판단하기 icon 양재동메이커 03-27 11,299
63 아두이노 [아두이노 실습] Push button 스위치로 FND 카운트 증가/감소 icon 양재동메이커 03-27 14,230
62 아두이노 Blynk를 사용해 아두이노에서 IoT 맛보기 icon 양재동메이커 03-27 11,824
61 아두이노 아두이노에서 u8glib로 0.96" OLED 사용하기 icon 양재동메이커 03-27 11,826
60 아두이노 아두이노에서 여러개의 스위치를 1개의 analog input핀으로 검사하기 icon 양재동메이커 03-27 10,910
59 아두이노 아두이노에서 RTOS 사용하기 (FreeRTOS in Arduino) icon 양재동메이커 03-27 13,769
58 아두이노 아두이노에서의 delay() 함수 icon 양재동메이커 03-27 10,074
57 아두이노 아두이노의 pinMode()에서 INPUT과 INPUT_PULLUP의 차이 icon 양재동메이커 03-27 12,195
56 아두이노 아두이노등의 임베디드 시스템의 변수 값 오버플로우 문제 icon 양재동메이커 03-27 8,818
55 아두이노 아두이노에서 외부 라이브러리 설치하기 (Install library in arduino) icon 양재동메이커 03-27 10,499
54 아두이노 WS2812 color LED 사용하기 icon 양재동메이커 03-27 9,656
53 아두이노 WS2812와 APA102의 차이 비교 (Comparison between WS2812 and APA102) icon 양재동메이커 03-27 11,262