Menu
CoddyTech

Gdy licznik millis() się przepełnia

Część sekcji Odmierzanie czasu bez delay() ścieżki Arduino w Coddy. Lekcja 17 z 18.

Typ unsigned long zatrzymuje się na wartości 4,294,967,295. Po około 49.7 dniach millis() osiąga tę wartość i zaczyna od 0. Nazywa się to przepełnieniem.

Mniejszy typ pokazuje to samo. Typ unsigned int zatrzymuje się na wartości 65535:

unsigned int a = 65530;
a = a + 10;   // 65540 się nie mieści: a wynosi teraz 4

Odejmowanie zawija się w ten sam sposób, co zapewnia bezpieczeństwo: zegar, który wskazywał 65530, a teraz wskazuje 4, odmierzył 10 kroków, a 4 minus 65530, zapisane w typie unsigned int, daje dokładnie 10. Podobnie millis() - start zawsze oznacza czas, który upłynął od start, nawet po przepełnieniu.

Dodawanie nie jest bezpieczne. W wyrażeniu now >= start + interval suma może przekroczyć górną granicę i zawinąć się do małej liczby, przez co timer natychmiast uzna, że czas minął. A gdy now zawinie się, ale suma jeszcze nie, czas nigdy nie minie. Zawsze odejmuj:

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

Wyzwanie

Średni

Nikt nie może czekać 49 dni na test, dlatego ten szkic przekazuje do isDone() czasy bliskie przepełnieniu i wyświetla odpowiedzi. Zbyt wcześnie zwraca "done", a potem bez końca zwraca "waiting". Popraw ten jeden wiersz w isDone(), aby porównywał czas, który upłynął od start, z interval.

Naciśnij Run, przeczytaj cztery odpowiedzi, popraw wiersz, a następnie naciśnij Check.

Spróbuj swoich sił

const unsigned long interval = 2000;

// Czy licznik czasu uruchomiony w chwili start zakończył odliczanie w chwili now?
bool isDone(unsigned long start, unsigned long now) {
  return now >= start + interval;   // popraw tę linię
}

// Wypisuje, ile czasu upłynęło i co zwraca 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() zatrzymuje się na 4294967295, a potem zaczyna ponownie od 0
  test(4294966000, 4294966500);   // tuż przed przepełnieniem licznika
  test(4294966000, 4294967200);   // nadal przed przepełnieniem
  test(4294966000, 900);          // zegar przekroczył 0
  test(4294964000, 500);          // licznik czasu uruchomiony wcześniej
}

void loop() {
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Odmierzanie czasu bez delay()

Poćwicz samodzielnie: Edytor online Arduino