[아두이노] 아두이노등의 임베디드 시스템의 변수 값 오버플로우 문제

아두이노등의 임베디드 시스템의 변수 값 오버플로우 문제(Variable overflow problem in embedded systems)

아두이노에서 10ms 마다 스위치 상태를 검사하는 코드를 작성한다고 해 보자.

void loop() {
  static unsigned long last = 0;
  static boolean swPrev = HIGH;
  unsigned long now;

  now = millis();
  if ((now-last)>=10) {
    digitalWrite(13,digitalRead(10));
    last = millis();
  }
}

Untitled.jpg





위와 같은 코드가 될텐데 동작시켜 보면 생각한대로 아무 문제 없이 동작한다. 하지만 이 코드의 경우 치명적인 버그가 숨어있다. millis() 함수는 unsigned long 타입의 값을 리턴해 준다. unsigned long 타입의 경우 0 ~ 4,294,967,295 까지의 숫자가 들어갈 수 있다. 4,294,967,295 ms면 엄청나게 긴 시간(대략 50일)임에는 틀림없다. 하지만 임베디드 시스템의 경우 한번 켜 놓으면 전원을 끄지 않고 몇년씩 계속 동작시키는 경우도 흔하다. 위의 코드를 사용하면 처음부터 약 50일간은 문제없이 동작하지만 약 50일 정도가 지나 4,294,967,295 -> 0으로 변한 이후부터는 동작하지 않는 문제가 발생한다.

예를 들어 마지막으로 스위치를 감지했을 때 시간값이 last 변수에 들어가게 되는데 그 값이 4,294,967,290이었다고 해 보자. 그 때 부터 5 ms 후에는 4,294,967,295가 되고 다시 1ms가 더 지나면 4,294,967,256이 아니고 0이 되어 버린다. 즉 now-last 값이 6이 아니고 -4,294,967,290이 되어 버리는 것이다. 즉 그때 부터는 (now-last) > 10 이라는 조건이 만족될 수가 없게 되어 버리기 때문에 스위치가 눌렸는지 검사를 하지 않게 된다. 

Untitled%2B2.jpg

이런 타입의 에러가 임베디드 시스템에서 아주 악성인 이유는 프로그램을 만들어 테스트 해 보면 정상적으로 잘 동작한다는 것이다. 그래서 제품을 출시해도 아무 문제가 없는데 출시 시작한지 약 두달 정도가 지나면 문제가 발견되기 시작한다는 것이다. 그리고 디버깅을 위해 버그를 재현하기 위해 개발실에서 다시 테스트 해 보면 전원을 넣은지 50일이 지나지 않았기 때문에 버그를 재현할 수 없다. 개발자가 버그를 재현할 수 없으면 버그의 원인을 찾기 힘들기 때문에 디버깅이 매우 어렵다. 

처음부터 이런 부분을 생각해 변수 값이 overflow 되는 경우에도 동작할 수 있도록 프로그램을 작성하는 연습을 해야만 한다.

위의 코드라면 이런식으로 수정해 주면 된다.

#define MAX 0xFFFFFFFF

boolean diff(unsigned long now, unsigned long prev, unsigned long d)
{
  if (now>prev) return ((now-prev)>=d);
  else return (((MAX-prev)+now+1)>=d);
}

void loop() {
  static unsigned long last = 0;
  static boolean swPrev = HIGH;
  unsigned long now;

  now = millis();
  if (diff(now, last, 10)) {
    digitalWrite(13,digitalRead(10));
    swPrev = LOW;
    last = millis();
  }

}

Untitled%2B3.jpg

이제 now 변수의 값이 overflow 되어 0으로 돌아가도 정상적으로 비교되어 위에서 이야기 한 버그가 발생하지 않는걸 확인할 수 있다. 
0
0
이 글을 페이스북으로 퍼가기 이 글을 트위터로 퍼가기 이 글을 카카오스토리로 퍼가기 이 글을 밴드로 퍼가기

임베디드 보드

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