Великое переполнение millis()
- Войдите на сайт для отправки комментариев
Данный пост имеет своей целью положить конец бессмертной как Вечный Жид теме «переполнения millis()», которая только и норовит переполниться настолько, что того и гляди лопнет и из неё во все стороны полезут непомещающиеся биты. Не знаю, как millis(), но моё терпение точно переполнилось и я твёрдо решил таки прихлопнуть её.
Предлагаю, данный пост в закладки и немедленно отсылать к нему всех новообретённых страстотерпцев и свидетелей Святого Переполнения. Тех, же кому отсылки к этому посту окажется недостаточно, посылать дальше.
Часть первая. Для тех, кто хочет знать как
Если Вам надо пользоваться millis() и при этом не думать о переполнении, пользуйтесь и не думайте. Главное, всегда вычитайте из текущего значения millis() стартовое значение, и результат сравнивайте с заданным интервалом. И никогда не делайте наоборот – никогда не складывайте стартовое значение с интервалом, чтобы потом сравнить с текущим значением.
Для демонстрации правильной и неправильной работы с millis() я сделал два скетча. В них используется тот факт, что внутренний счётчик миллисекунд хранится во вполне себе доступной нам переменной timer0_millis, которая при запуске Ардуино устанавливается в 0. Я подкрутил её, чтобы переполнение наступало не через два месяца, а через пять секунд после старта и благодаря этому появилась возможность просто проверить как поведёт себя программа при переполнении.
Итак, правильный скетч
////////////////////////////////////////////////////////////////////// // // ПРАВИЛЬНОЕ использование millis() - плевать на переполнение // // Сделано так, чтобы millis() переполнился через 5 секунд после старта // программа же отсчитывает двухсекундные интревалы (в функции make2SecondsInterval) // Так что переполнение millis придётся на середину интервала // Запускаем и смотрим на происходящую "катастрофу" // template <typename T> inline Print & operator << (Print &s, T n) { s.print(n); return s; } extern volatile unsigned long timer0_millis; static void make2SecondsInterval(void) { uint32_t startTime = millis(), currentMillis; while ((currentMillis = millis()) - startTime < 2000ul); // ТАК НАДО !!! Serial << " start: " << startTime << "\nmillis: " << currentMillis << "\ndiffer: " << (currentMillis - startTime) << "\n---\n"; } void setup (void) { Serial.begin(115200); // // Устанавливаем счётчик millis так, чтобы он переполнился через 5 секунд timer0_millis = UINT32_MAX - 5000ul; // // Отсчитываем 5 двухсекундных интервалов for (int i=0; i < 5; i++) make2SecondsInterval(); } void loop(void) {} // // РЕЗУЛЬТАТ - всё отлично работает, несмотря на переполнение // // start: 4294962295 // millis: 4294964295 // differ: 2000 // --- // start: 4294964296 // millis: 4294966296 // differ: 2000 // --- // start: 4294966297 // millis: 1001 // differ: 2000 // --- // start: 1002 // millis: 3002 // differ: 2000 // --- // start: 3002 // millis: 5002 // differ: 2000 // ---
как видите, переполнение происходит, но интервалы прекрасно отрабатываются
Неправильный скетч
////////////////////////////////////////////////////////////////////// // // НЕПРАВИЛЬНОЕ использование millis() - переполнение создаёт проблемы // // Сделано так, чтобы millis() переполнился через 5 секунд после старта // программа же отсчитывает двухсекундные интревалы (в функции make2SecondsInterval) // Так что переполнение millis придётся на середину интервала // Запускаем и смотрим на происходящую катастрофу // template <typename T> inline Print & operator << (Print &s, T n) { s.print(n); return s; } extern volatile unsigned long timer0_millis; static void make2SecondsInterval(void) { uint32_t startTime = millis(), currentMillis; while (startTime + 2000ul > (currentMillis = millis())); // ТАК НЕЛЬЗЯ !!! Serial << " start: " << startTime << "\nmillis: " << currentMillis << "\ndiffer: " << (currentMillis - startTime) << "\n---\n"; } void setup (void) { Serial.begin(115200); // // Устанавливаем счётчик millis так, чтобы он переполнился через 5 секунд timer0_millis = UINT32_MAX - 5000ul; // // Отсчитываем 5 двухсекундных интервалов for (int i=0; i < 5; i++) make2SecondsInterval(); } void loop(void) {} // // РЕЗУЛЬТАТ - всё сломалось на третьем проходе // // start: 4294962295 // millis: 4294964295 // differ: 2000 // --- // start: 4294964296 // millis: 4294966296 // differ: 2000 // --- // start: 4294966297 // millis: 4294966297 // differ: 0 // --- // start: 4294966299 // millis: 4294966299 // differ: 0 // --- // start: 4294966303 // millis: 4294966303 // differ: 0 // ---
а здесь всё ломается
Разница между правильным и неправильным скетчами ТОЛЬКО в строке 15. И состоит она именно в том, о чём я говорил чуть выше – используйте вычитание и не используйте сложение.
Делайте всегда, как написано в первом скетче и забудьте о перхоти переполнении millis().
Часть вторая. Для тех, кто хочет понимать почему
Если Вы хотите понимать как это происходит, Вам необходимо:
№1 Внимательно изучить арифметику в дополнительном коде
№2 Для проверки усвоения материала попытаться устно решить две задачи, приведённые ниже. Если задачи устно решить не удалось, обязательно вернуться к п.1 и так, пока задачи не покажутся очевидными, и не решатся устно.
Часть третья. Задачи для проверки понимания работы арифметики
В обеих задачах требуется в обозначенное в коде место подставить число такое, чтобы получался приведённый результат. Обе задачи решаются устно теми, кто действительно понимает работу арифметики в дополнительном коде.
Задача №1
Найти такое целое, отличное от нуля число, которое равно самому себе с обратным знаком.
void setup (void) { Serial.begin(115200); int a = <вставьте сюда число>; if (a != 0 && a == -a) Serial.println("What the fuck?!?"); Serial.println("That's all! No more fun!"); } void loop(void) {} // // РЕЗУЛЬТАТ // // What the fuck?!? // That's all! No more fun!
Задача №2.
Найти такое целое беззнаковое, отличное от нуля число, чтобы оно же умноженное на 2 равнялось нулю.
void setup (void) { Serial.begin(115200); unsigned a = <вставьте сюда число>; if ((a != 0) && (a * 2 == 0)) Serial.println("What the fuck?!?"); Serial.println("That's all! No more fun!"); } void loop(void) {} // // РЕЗУЛЬТАТ // // What the fuck?!? // That's all! No more fun!
Надеюсь, у тех, кто исполнил алгоритм из части второй до конца, больше никаких вопросов о переполнении millis() не возникает. Если же таки остались, то, даже не знаю, попробуйте написать в «Спортлото».
Часть последняя. Для тех, кто «как математик не может согласиться»
Ребята, задолбали! Если бы вы действительно были математиками, то легко заметили бы, что множество целых чисел в математике и множество всех возможных значений целочисленного типа в нашей системе – суть принципиально разные вещи. Первое – абелева группа, а второе – не является группой вообще. Так какого хрена Вы пытаетесь распространять свойство одного на другое?
Глас вопиющего в пустыне. И снова придет неофит, и снова у него все переполнится, и все случится сначала. Ибо для неофита: во многая знания, многая печали, преумножающий знания, преумножает печали. Аминь.))))
Это я типа на тему подписался.))))
И снова придет неофит
Я предлагаю использовать метод Эдуарда Овечкина - ничего не объяснять и не ввязываться в холивары (по Овечкину "в моральные противостояния"). Сам по себе метод просто замечательный. Он настолько же прост и эффективен, насколько краток и понятен. Полное изложение метода состоит из одной единственной фразы из рассказа "Мичман Тоня". Начинается она со слов: "Я вообще человек неконфликтный", легко найдёте поиском :)))
Фигня ваши примеры. Запустил на компиляцию - выдало ошибку в строке после знака равно:
unsigned a = <<strong>вставьте сюда число</strong>>
Что я сделал не так? Может версия ИДЕ не та? :-)
Arhat109-2, решили поиграть в тупого нуба? В другой раз, ладно, у меня сегодня нет настроения, извините.
Просто показал Вам какого рода вопросы будут в этой теме, если её прикрепить. Придут новички, и это будет первое что они сделают. Далее будут попытки вставить число в тег strong "как есть" (новички с какого-нить ПХП) и т.д. Если человек не разбирается с дополнительным кодом, то в "сях" ему делать в общем-то нечего.
Кстати, задал сыну ваш второй вопрос в таком виде: имеем байтовую целочисленную беззнаковую переменную. Какое положить в неё чиселко, чтобы при умножении на 2 получился ноль? Не с первого раза, но сообразил таки. :)
Мне кажется что такая запись примера ближе к "сионистам":
unsigned a = "замените этот комментарий на число";
Ну, strong-то случайно попал, хотел жирным выделить, а редактировать низзя.
кстати, что об этом всём думал не математик, не учёный и даже не программист, а обычный гуманитарий и философ:
Значит, чтобы запрограммировать машину, следовало повторить если не весь Космос с самого начала, то по крайней мере солидную его часть.
«Путешествие первое А, или Электрибальд Трурля» Станислав Лем 1964 год
ЕвгенийП в правильном и неправильном примерах применил сложные конструкции, которые усложняют понимание новичками данных примеров, поэтому им остаётся верить в переполнение миллис.
Що знову?
Що знову?
Евгений, вы же можете доступно объяснить людям понятие шаблона и как этими шаблонами пользоваться и при таких раскладах отсылать их туда, к объяснению, с примерами и разъяснениями. Думаю, многим бы пригодилось.
Без обид новых ОК? Пока Евгений занят.
...Смотри вот шаблон
Это обозначает,что на этапе компиляции, для каждого использованияконструкции вида:
будет порождено описание, в котором T будет заменено на то, что есть:
для Serial << 5;
будет порождена строка:
Условно можешь считать, что все этистроки будут помещены ВМЕСТО строки с шаблоном.
-------------------------------------
Еще остались вопросы?
То есть шаблон равен описанию функции (в данном случае оператора) для РАЗНЫХ типов операндов. Написание такогошаблона, как у Евгения, все равно, что написать пять - семь строк для все типов. ОК? И для компилятора тоже все равно.
Без обид новых ОК? Пока Евгений занят.
...Смотри вот шаблон
Это обозначает,что на этапе компиляции, для каждого использованияконструкции вида:
будет порождено описание, в котором T будет заменено на то, что есть:
для Serial << 5;
будет порождена строка:
Условно можешь считать, что все этистроки будут помещены ВМЕСТО строки с шаблоном.
-------------------------------------
Еще остались вопросы?
То есть шаблон равен описанию функции (в данном случае оператора) для РАЗНЫХ типов операндов. Написание такогошаблона, как у Евгения, все равно, что написать пять - семь строк для все типов. ОК? И для компилятора тоже все равно.
Блин, я это знаю :) Молодеж не знает. Им нужно объяснить. Ты дал объяснения для данного конкретного случая, а примененеий дохрена. Нужно раскрыть вопрос. Я вот не смогу. Всегда в таких случаях вспоминаю как я пытался младшему сыну про двоичное и шестнадцетиричное счисление рассказать :) Ну учитель был хуже меня, весь класс не знал, а я за ДВА (!!) дня все же вдолбил.
Тут , в примере, нужно еще как то объяснить, что эта конструкция бесконечна, потому что каждое ее звено возвращает указатель на Serial для следующего звена.
Для новичков, которые то как раз и не понимают, что есть подход к миллису, которому наплевать на переполнение, этот шаблон ваще мозг выносит, и они вместо того, что бы врубиться в проблеммы миллиса, входят в ступор видя тааакое....
Однако есть нюанс, что то мне говорит, что при такой конструкции компилятор построит не оптимальный код...
ЕвгенийП в правильном и неправильном примерах применил сложные конструкции, которые усложняют понимание новичками данных примеров, поэтому им остаётся верить в переполнение миллис.
а, я шо говорил - без разговоров сбрасывать в пропасть.
ЕвгенийП в правильном и неправильном примерах применил сложные конструкции, которые усложняют понимание новичками данных примеров, поэтому им остаётся верить в переполнение миллис.
а, я шо говорил - без разговоров сбрасывать в пропасть.
Так он и сбросил, попросив модераторов удалить не нравящиеся ему высказывания: http://arduino.ru/forum/obshchii/chistim-forum-ot-spama-vmeste?page=4#co...
Прошу, по возможности, убрать политоту и срач из темы про переполнение. Начиная с поста #9, и до конца темы там можно всё спокойно выбросить.
Если это про меня, то я не девочка, что бы кому то нравится.
И так, на всякий случай, по хорошему, претендовать на роль учителя, при этом еще более запутывая потенциальных "учеников", по крайней мере глупо. Да еще потом упираться рогом, мня себя мессией. Сами разводите тут цирк с конями, а потом подтираетесь.
Попробуйте объяснить , в чем я не прав ?
Если это про меня, то я не дквочка, что бы кому то нравится.
Я в курсе, что Вы не дквочка, но высеров про начало войны, политоту и оскорблений от Вас я не видел. Прошу прощения, если пропустил, но это было не про Вас.
претендовать на роль учителя
Так не претендуйте! Зачем Вы постоянно пытаетесь учить меня что мне писать, а что - нет? Пишите сами, что считаете нужным, а я буду писать то, что я считаю нужным.
И да, если мои опусы не нужны этому форуму, я могу их не писать, в том, что я пишу новой для меня информации нет.
еще более запутывая потенциальных "учеников"
Я уже говорил Вам, что про потоковый вывод у меня есть отдельный пост, кому надо прочитает, кому не надо - тому не надо.
по крайней мере глупо
Ну, вот такой я человек, каким Бог создал.
Да еще потом упираться рогом
Вот тут не понял. Во что я упёрся рогом? Отказываюь написать то, чего хотите Вы? Так ... я не отказываюсь, готов обсудить стоимость этой этой работы. А бесплатно я пишу то, что считаю нужным сам и не пишу то, чего не считаю нужным. Если же Вы хотите, чтобы я писал что-то по Вашему заказу, так заказывайте! Я ведь тоже, как Вы изволили выразиться не дквочка, чтобы всем нравится, а Вы не мой работодатель, чтобы требовать от меня написать что-то, что сам я писать не планировал.
мня себя мессией
Не знаю, кем Вы себя мните - это не моё дело.
а потом подтираетесь.
Что-то я не припомню каких-то срачей между нами. Давайте избегать такого рода лексики, ладно? Мне это неприятно и не думаю, что это приятно Вам.
Попробуйте объяснить , в чем я не прав ?
Вы во всём правы.
Просто я тут писал, что это война между тупоконечниками и остроконечниками , пытаясь показать ее безсмысленность ( https://dic.academic.ru/dic.nsf/ruwiki/374980 ), потому и принял на свой счет.
Евгений, стеб ни к чему. У меня и в мыслях не было гнать вас с форума. Да и ваши статьи тут очень даже радуют. Да , видимо я погорячился, приняв все на свой счет. Оскорбить не хотел, просто видимо эхо "прошедшей войны", был тут инцидент :( Правда , не обижайтесь, нервы...
Вижу наезды, не понимаю почему, пытаюсь вмешаться, ибо мешают спокойствию. Поэтому и пытаюсь сделать нечто, что вы принимаете за мои поучения.
Ну, разобрались, и слава Богу.
Иначе говоря правильно будет так
так будет НЕ правильно
а такой метод имеет право на существование?
а такой метод имеет право на существование?
Право имеют все.
А будет ли он корректно работать - возьмите проверочный скетч из первого поста, подставьте туда свой метод, запустите, посмотрите и не забудьте рассказать нам о результатах.
Кусочек из библиотеки tinyFAT
строки 3 и 39 :)
А что за проблема? Я не понял, мне кажется, что нормально всё. Что-то не так?
просто улыбнуло, ищу проблему зависания без sd карты по таймауту, но хороший вариант, ждать всего 65 секунд до великого переполнения
Так там поди SD_INIT_TIMEOUT меньше. Если так, то всё должно быть нормально.
да там по умолчанию 2000 стоит, но не отрабатывает
тоисть как ускорить "конец света" ))
тоисть как ускорить "конец света" ))
ничего я на заметку не брал, я знаю как работает millis(), и что переполнения не будет если правильно его использовать
[/quote]
и что переполнения не будет если правильно его использовать
[/quote]
а что будет если не правильно??
и что переполнения не будет если правильно его использовать
а что будет если не правильно??
Ну, понятное дело - переполнение :)
и что переполнения не будет если правильно его использовать
а что будет если не правильно??
Ну, понятное дело - переполнение :)
Петрович, эта тема вечная).
Ещё бывают "великие переполнения" суток в году, часов в сутках и т. д.
Все решают по разному.
Я применяю такой подход.
Ещё бывают "великие переполнения" суток в году, часов в сутках и т. д.
Все решают по разному.
вспомните "проблему 2000" :)
вспомните "проблему 2000" :)
Уже не так далеко и переполнение UNIX-time
Евгений, вы можете самый первый Правильный пример расписать на уровне
все у вас превосходно конечно описано но уж больно заумно для дятлов вроде меня))) пожалуйста, по-народному, к примеру мигалки можете расписать?
все у вас превосходно конечно описано но уж больно заумно для дятлов вроде меня))) пожалуйста, по-народному, к примеру мигалки можете расписать?
Положь колдобину со стороны загогулины и два раза дергани за пимпочки. Опосля чего долбани плюхалкой по кувыкалке и, кады чвокнет, – отскочь дальшее, прикинься ветошью и не отсвечивай. Потому как она в энто время шмяк тудыть, сюдыть, ёксель-моксель, ёрш твою медь... Пш-ш-ш! – И ждешь пока остынет. Остыло – подымаесся, вздыхаешь. Осторожненько вздыхаешь, про себя, шобы эта быдла не рванула! И бегишь за угол за пол-литрой. Потому как пронесло!
(С) Задорнов
все у вас превосходно конечно описано но уж больно заумно для дятлов вроде меня))) пожалуйста, по-народному, к примеру мигалки можете расписать?
Каждому дятлу по новой обьяснять - жизни не хватит. Не Евгений должен опускаться до дятлов - а вы пытаться расти вслед ему. Так что обучайтесь. Все данные в посте Евгения есть. Как разберетесь в беззнаковой арифметике - так и все остальное в этой теме перестанет быть "заумным".
Вас смутила запись Serial << ... ?
На самом деле, это очень удобно и компактно. И работает не только для сериала, а много для чего. На эту тему был отдельный пост, освойте. И здесь понятнее станет и впредь сможете таким выводом пользоваться.
Все что угодно но только не по-делу. Да что я один такой?! Это один раз написать всего надо. Как раз таки для развития. Ну не в силах допустим новичку сразу разобраться. Я допустим полностью код понимаю, очем речь, но куча примеров с этими миллисами и все написаны в простой форме. А здесь написанно еще и правильно но в сложной форме для первого понимания. Зачем?! Зачем сразу на вилы сажать ? Ну черкони ты раз для тебя это уж совсем просто. Не, давайте демагогию разводить...
За Задорнова спасибо! -приятный момент. Даже если в свой адрес
Евгений, спасибо! Это не в ваш адрес
Плюс
Все что угодно но только не по-делу. Да что я один такой?! Это один раз написать всего надо.
в том-то и дело, что не один. Вас тыщи. И каждому надо по-своему обьяснить. Поэтому "один раз написать" - не получится.
Собственно, это в инете уже обьяснено десятки раз. Да и на этом форуме. тема-то и создана. чтобы раз и навсегда пресечь дурацие вопросы. Не помогает.
Зачем?!
что бы ты оценил уровень своего интеллектального развития и свалил бухать вотку.
Понеслось...
Ладно, обозвите меня как хотите(в л.с), только перестаньте срать в тему.
Пожалуйста. Я сюда прихожу за знаниями
Судя по ответам Ув. Гуру форума, сюда за знаниями приходить н-н-не стоит.
Страуструп более лоялен, пожалуй.
Ну и Ув. Гугл.
Так что...
Привыкайте.
Вас смутила запись Serial << ... ?
Фокусировка вопроса именно на
uint32_t startTime = millis(), currentMillis;
15
while ((currentMillis = millis()) - startTime < 2000ul); // ТАК НАДО !!!
start time равен миллисам, дальше почему current millis через запятую? Ему тоже присваивается значение millis? Дальше цикл пошел причем while
Для вас тут естественно все чисто и просто. Ок. Но что тяжелого составило бы написать в самой простой форме.? Для нуб использования. Вот вам глобальная переменная, вот сетап вот в лупе арифметика и место где что-то исполняется, если вставить. И новичек тогда пойдет дальше, ведь все примеры начинаются именно так. Никтож блинк не написал сразу отдельной функцией. Все то хорошо конечно что вы делаете ребята ибо вы это делаете просто так в первую очередь. Возможно и есть такие кто в виду своей жизненной немощи пытается выглядеть хоть где-то богом, но я думаю не здесь уж точно. Это ближе к мастерам стиралок относится.... Тама клан просто и непосвященные уже есть говно ибо предстали перед ними. Здесь нормальная атмосфера и я лично очень много вынес конкретно для себя, для своей головы, не для цель продаж там и. Т. Д.
Спасибо всем за терпение. Жаль только что сам форум построен на дерьмовом движке без нормальных привычных опциях(удаление, шапки и. Т. Д) все свалкой. По этому прошу извинения что мой категорически -поперечный вопрос, прозьба стала причиной ненужных пол-десятка сообщений. Ссори с телефона коряво пишется)))
Для нуб использования ..... И новичек тогда пойдет дальше, ведь все примеры начинаются именно так.
Прикол в том, что ардуино - это, цитата: "забавная игрушка ДЛЯ ПРОГРАММИСТОВ" (-wdrakula). Поэтому синтаксис и т.п. тут не разжевывают.(На собственном опыте выявленно)
start time равен миллисам, дальше почему current millis через запятую? Ему тоже присваивается значение millis?
...поэтому об этом Вам не расскажут тута. Вы, мол, дебил, гуманитарий, зачем ардуино купили, в окно его или в печку, и тд и тп.
Все то хорошо конечно что вы делаете ребята ибо вы это делаете просто так в первую очередь.
+
Иван_123, ваня иди погуляй. Миру не нужен такой программист.Здесь не ясельки и не садик, куда мамочки приводят своих деточек, что бы они голову по свой дурости не разбили. Да и не гуманитарий Вы, а банальный пиз**ол.
Для вас тут естественно все чисто и просто. Ок. Но что тяжелого составило бы написать в самой простой форме.? Для нуб использования.
А для нуб-использования нужно начинать с вот этой небольшой книжки - Брайан Керниган, Денис М. Ритчи "Язык программирования Си"
По ссылке выше скачивайте и читайте. Эта книга - классика, основы языка C, от которого производным будет и C++.
И всё станет надолго и гораздо понятнее.
Иван_123,
Иван, я понимаю, что учиться Вы не хотите принципиально (или беспринципно - мне пофиг), но вот хотя бы вот такую малюсенькую книжку для совсем чайников изучите - ну себя же больше уважать будете.