Когда 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) {
// готово
}Задание
СреднеНикто не может ждать 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() {
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Отсчёт времени без delay
1millis()
Часы на millis()Время в unsigned longМигание без задержкиДва светодиода, две скоростиМигание и переключение кнопкойСвет по таймеру4Точное измерение времени
Измерение с помощью micros()Когда millis() переполняетсяСекундомер с кнопкойПотренируйтесь самостоятельно: Песочница Arduino