Menu
CoddyTech

millis()가 오버플로될 때

Coddy Arduino 여정의 delay 없이 타이밍 제어하기 섹션에 포함된 레슨. 18개 중 17번째.

unsigned long은 4,294,967,295에서 멈춥니다. 약 49.7일 후면 millis()가 이 값에 도달하고 다시 0부터 시작합니다. 이를 롤오버라고 합니다.

더 작은 자료형에서도 같은 현상이 나타납니다. unsigned int는 65535에서 멈춥니다.

unsigned int a = 65530;
a = a + 10;   // 65540은 범위를 벗어납니다: a는 이제 4입니다

뺄셈도 같은 방식으로 순환하며, 이 덕분에 안전하게 사용할 수 있습니다. 65530에서 시작한 시계가 이제 4를 가리킨다면 10단계가 지난 것이고, unsigned int에 저장된 4 빼기 65530은 정확히 10입니다. 마찬가지로 millis() - start는 롤오버가 발생하더라도 항상 start 이후 경과한 시간입니다.

덧셈은 안전하지 않습니다. now >= start + interval에서는 합이 최댓값을 넘어 작은 수로 순환할 수 있으므로 타이머가 곧바로 완료된 것으로 처리됩니다. 반대로 now는 순환했지만 합은 아직 순환하지 않은 경우에는 완료되지 않습니다. 항상 뺄셈을 사용하세요.

if (now - start >= interval) {
  // 완료
}
challenge icon

챌린지

중급

테스트를 위해 49일을 기다릴 수 있는 사람은 없으므로, 이 스케치는 롤오버에 가까운 시간으로 isDone()을 호출하고 그 결과를 출력합니다. 너무 일찍 "done"이라고 말하고, 영원히 "waiting"이라고 말합니다. isDone()의 한 줄을 수정해 start 이후의 시간과 interval을 비교하세요.

Run을 누르고, 네 가지 결과를 읽은 다음, 해당 줄을 수정하고 Check를 누르세요.

직접 해보기

const unsigned long interval = 2000;

// start에서 시작한 타이머가 now 시점에 완료되었나요?
bool isDone(unsigned long start, unsigned long now) {
  return now >= start + interval;   // 이 줄을 수정하세요
}

// 경과 시간과 isDone()의 결과를 출력합니다.
void test(unsigned long start, unsigned long now) {
  Serial.print(now - start);
  Serial.print(" ms: ");
  if (isDone(start, now)) {
    Serial.println("done");
  } else {
    Serial.println("waiting");
  }
}

void setup() {
  Serial.begin(9600);
  // millis()는 4294967295에서 멈춘 뒤 0에서 다시 시작합니다
  test(4294966000, 4294966500);   // 값이 순환되기 직전
  test(4294966000, 4294967200);   // 아직 값이 순환되기 전
  test(4294966000, 900);          // 시계가 0을 지났습니다
  test(4294964000, 500);          // 더 일찍 시작한 타이머
}

void loop() {
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

delay 없이 타이밍 제어하기의 모든 레슨

직접 연습해 보세요: Arduino 플레이그라운드