Menu
CoddyTech

Когда millis() переполняется

Часть раздела Отсчёт времени без delay путешествия по Arduino на Coddy. Урок 17 из 18.

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 шагов, а 4 минус 65530, сохранённые в unsigned int, дают ровно 10. Точно так же millis() - start всегда показывает время, прошедшее с момента start, даже при переполнении.

Сложение небезопасно. В выражении now >= start + interval сумма может превысить максимальное значение и переполниться до малого числа, поэтому таймер сразу сработает. А если now уже переполнилось, а сумма ещё нет, таймер никогда не сработает. Всегда вычитайте:

if (now - start >= interval) {
  // готово
}
challenge icon

Задание

Средне

Никто не может ждать 49 дней ради теста, поэтому этот скетч передаёт функции isDone() значения времени, близкие к переполнению, и выводит её ответы. Она слишком рано сообщает "done" и бесконечно сообщает "waiting". Исправь одну строку в isDone(), чтобы она сравнивала время, прошедшее с start, с interval.

Нажми Запустить, прочитай четыре ответа, исправь строку, затем нажми Проверить.

Попробуйте сами

const unsigned long interval = 2000;

// Завершился ли к моменту now отсчёт таймера, запущенного в момент start?
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