[아두이노] 아두이노에서의 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
이 글을 페이스북으로 퍼가기 이 글을 트위터로 퍼가기 이 글을 카카오스토리로 퍼가기 이 글을 밴드로 퍼가기

임베디드 보드

번호 제목 글쓴이 날짜 조회수
118 아두이노 ESP32 Analog Inputs (ADC) +4 icon 양재동메이커 02-12 15,883
117 아두이노 TIP : Serial의 Port가 Open 시점 확인 icon 양재동메이커 01-21 12,794
116 아두이노 ESP32 Boot Mode icon 양재동메이커 12-28 12,586
115 아두이노 아두이노 에러 리스트(Arduino Error list) icon 양재동메이커 11-24 17,515
114 아두이노 ESP32 main.cpp +1 icon 양재동메이커 11-19 12,969
113 아두이노 ESP32 EEPROM 와 IR Remote icon 양재동메이커 08-06 12,878
112 아두이노 Learn ESP32 icon 양재동메이커 06-25 12,446
111 라즈베리 파이 라즈베리 파이 (Raspberry Pi) 기초 icon 양재동메이커 06-19 13,867
110 라즈베리 파이 (동영상 강의) 라즈베리 파이 강좌 Link icon 양재동메이커 06-17 13,038
109 STM32 / MBED [MED] Switch debouncing icon HellMaker 12-30 14,975
108 기타 [타이젠] 아두이노의 16x2 LCD Display라이브러리 LiquidCrystal_I2C의 타이젠 포팅 icon 양재동메이커 09-15 14,593
107 기타 [타이젠] GPIO의 디지탈 출력과 입력 인터럽트의 C++ Class 제작 icon 양재동메이커 09-12 14,064
106 마이크로비트 서보 모터 icon HellMaker 09-03 13,872
105 마이크로비트 아날로그 온도센서 (마이크로 비트 센서 활용) icon HellMaker 09-01 14,413
104 마이크로비트 터치센서 (마이크로 비트 센서 활용) icon HellMaker 09-01 13,489
103 마이크로비트 디지털 홀 센서 (마이크로 비트 센서 활용) icon HellMaker 08-29 12,653
102 마이크로비트 리니어 홀 센서 (마이크로 비트 센서 활용) icon HellMaker 08-29 12,198
101 마이크로비트 불꽃 감지 센서 (마이크로 비트 센서 활용) icon HellMaker 08-26 12,562
100 마이크로비트 로터리 엔코더 (마이크로 비트 센서 활용) icon HellMaker 08-25 12,682
99 마이크로비트 2컬러 LED(3mm) (센서 활용) icon HellMaker 08-22 12,652