Проверить прошел ли час от прошлого действия
- Войдите на сайт для отправки комментариев
Чт, 10/04/2014 - 19:29
Чевой-то я тупею по весне...
Надо ежечасно записывать некоторые данные в EEPROM. Корректен ли этот код, или есть более изящное решение?
loop() { // ... checkTime(); // ... } void checkTime() { static unsigned long prevMillisTime; if((millis() - prevMillisTime) > 60 * 60 * 1000) // если с последней записи прошло больше часа { EEPROM_Save_Data(); // записываем текущие показания в EEPROM prevMillisTime = millis(); } }
Трудно придратся. На первый взгляд "идеально". Но мы трудных задач не боимся ;)
1. Откройте виндовый калькулятор. Умножте 60*60*1000, отодвинте в угол экрана так что-бы видеть перед собой когда откроете ArduinoIDE
2. Загрузите этот скетч.
3. Откройте Serial монитор и посмотрите на цифру которую вам выведет Serial.println.
4. Перевидете взгляд на калькулятор, обратно на Serial. Протрите глаза. Воскликните "да как же так...!!!!"
5. Прекратите нервно смеятся
6. Идите читать Целочисленные константы | Аппаратная платформа Arduino
Если просветление не наступит в течении часа - обращайтесь :)
я ж говорю - тупеююю, даже причины такого понять не могу, вроде размерности хватает с запасом.
я ж говорю - тупеююю, даже причины такого понять не могу, вроде размерности хватает с запасом.
Я вам дал ссылку. Идите читать. Много раз. Час еще не прошел ;) ДАО должно прийти :)
P.S. Вы не тупеете - грабли действительно "подлые". Даже зная про них - легко наступить. Поэтому я хочу "что-бы запомнили" :) Ну и если "сами разгадаете" - вдвойне вам приятно будет.
15 минут мне хватило, чтобы понять, что это дает корректный ответ:
Но почему так по-разному считает компилятор, мне озарения не снизошло.
И ведь во многих скетчах (даже на этом форуме) такая запись, как в первом посте, прокатывала успешно, нареканий не было :)
Ну ok. Давайте по шагам повторим действие компилятора.
Вначале он смотрит на правую часть. Там у нас 60*60*1000
Вначале он умножит первые два числа.
60*60
Вопрос1: с точки зрения компилятора какой тип имеют аргументы операции умножения?
Вопрос2: какой тип будет у результат?
Вопрос3: какой будет результат? ;)
1. int
2. int
3. 3600
слева же явно указано, что результат должен быть беззнаковый long.
P.S. я понимаю, что вы будете ржать, но меня час назад реально забанили в Google. Всегда думалось, что это анекдот. А ведь всего лишь сделано несколько невинных запросов по C++, никакой Украины.
1. int
2. int
3. 3600
Едем дальше.
Умножаем 3600*1000
Три вопроса:
Вопрос1: с точки зрения компилятора какой тип имеют аргументы операции умножения?
Вопрос2: какой тип будет у результат?
Вопрос3: какой будет результат? ;)
слева же явно указано, что результат должен быть беззнаковый long.
А что нам "лево"? Мы туда еще не добрались. Мы про существование "лево" еще ничего не знаем. Может его и не будет вовсе (так тоже бывает). Динамической типизации у нас нет. Вывода типов - по большому счету тоже. Так что "по тупому, по шагам едем".
Какой тип у результата будет? Если перемножаем два int-та?
>я понимаю, что вы будете ржать,
Не буду. Может конечно "глюк". Может на одном из серверов SSL-сертификат обновить забыли. А может и "все по серьезному". Действительно какую-то заразу подцепили. Или ваш провайдер стал слишком любопытным и пытается снифать ВЕСЬ трафиик.
P.S. Если конечно вы сами, не занимаетесь скажем Web-разработкой и не забыли выключить что-то типа Fiddler2 после того как отлаживали. Но думаю, в этом случае вы бы уже сами понимали "что это может означать".
1. оба int
2. т.к. размерность результата превышает умолчания (int), то надо взять следующую размерность, т.е. long.
Или компилятор не может выйти за пределы умолчаний и опять исчисляет результат как int с переполнением? В этом косяк? Тогда это тупой компилятор, рулить космическими кораблями не дадим.
3. 3600000 по моей логике :)
>Или компилятор не может выйти за пределы умолчаний и опять исчисляет результат как int с переполнением?
Бинго.
> Тогда это тупой компилятор, рулить космическими кораблями не дадим
Наоборот. Все под вашим контролем (и за все отвечаете вы). К тому же - это частный случай. Это C++, куча способов "выстрелить себе в ногу". Обратная сторона - предсказуемость и эффективность.
В первых есть куча ситуаций когда "переполнение" используют сознательно. Где оно позволяет написать более изящный и эффективный код.
Во вторых, предположим что компилятор ведет себя так как вы хотели. Теоретически это возможно. Он видит все числа. Может перемножить их на этапе компиляции (во время выполнение программы - умножение каждый раз не делается, раз результат известен). Посмотреть на результат и выбрать более подходящий тип.
Но.... предположим вы решили заменить одну константу на переменную. И написали int sec=60;, а его потом застивили sec*60*1000
Все. Заранее высчитать он не может. Только в процессе выполнения. Значит есть два варианта:
1. Делать переполнение
2. ВСЕГДА все умножения, делать в виде LONG. А потом...
Первый вариант - фигня выходит. При использовании константы и переменной с тем же значением - разные результаты. Отлавливать такой баг - это будет "подлось в квадрате". В такой космический корабль - точно лучше не садится.
Второй вариант - тоже фигня. Это какой же сумашедший оверхед по производительности будет. Все числа умножать как long-ги (это если опустить проблемы что у нас еще бывают знаковы и беззнаковые). Да еще на 8-ми битных контроллерах. К тому же, а что потом делать когда мы доберемся "направо присваивать"? Откидывать лишнии биты? Или переполнение делать? Неоднозначно.
И если "покопатся". То можно будет найти с десяток неоднозначностей. И неочевидностей которые один програмер "интуитивно" поймет в одну сторону, а другой - в другую.
Кстати, если вы действительно "познали ДАО". То придумайте, как этот решение:
можно сократить на 4-ре буквы ;)
Стесняюсь спросить: а зачем такой хитровывернутый мазохизм вместо одноразового рассчета величины таймаута? Вселенную погреть захотелось?
Стесняюсь спросить: а зачем такой хитровывернутый мазохизм вместо одноразового рассчета величины таймаута? Вселенную погреть захотелось?
Полностью согласен с вами. Мозг - потребляет до 50% энергии. Греет вселенную. Может ошибатся. Может очепятатся. Потом опять будет греть вселенную, когда будет читать код. А не дай бог, нужно будет поменять с 60-ти минут на 43. Это же опять приближение тепловой смерти вселенной. Вспомнить "что на что мы там умножали", да потом еще умножить в уме 43*60
Поэтому, действительно, лучше расчитать это ОДИН раз. Только не мозгом, а компьютером. В момент компиляции.
Чисто из любопытсва. Cкопиляйте два варианта. Один "val=60UL*60UL*1000UL" и второй "val=36000UL" и сравните размеры получившихся бинарников.
P.S. Не, компиляторы все-таки не дураки пишут ;)
ладно, с циферками разобрались, теперь с буковками.
Тут никаких подвохов не будет? Если m < p (при переполнении), если m > p?
ладно, с циферками разобрались, теперь с буковками.
Тут никаких подвохов не будет? Если m < p (при переполнении), если m > p?
Потенциально - будет: если разность окажется равной X... Поэтому, обычно, рекомендут использовать выражения "меньше или равно" или "больше или равно"...
Ну и, по классике, пишется if( условие в скобках ) { действие по условию }
зы: Кстати, когда-то некто, похожий на leshak, грозился показать код для циклической записи в eeprom, чтобы увеличить время жизни одной ячейки памяти.
Тогда-же говорилось, что имеет смысл перед записью прочитать значение, и, если оно не изменилось, ничего не записывать...
>Кстати, когда-то некто, похожий на leshak, грозился показать код для циклической записи в eeprom
Где грозился, покажите, может и покажем.... Честно - не помню.
Хотя, в данном случае - тоже думал, но решил что не стоит заморачиватся и заострять на это. При записи раз в час... ну сами посчитайте сколько времени хватит ресурса.
>Тут никаких подвохов не будет?
Не, не будет. Ну разве что сработает на какою-то миллисекунду, другую позже чем вы расчитываете. Но "точно час" у вас все равно через такой механизм не получить. Вообщем тут скорее дело вкуса. Хоти-те, как подсказал Andrey_Y_Ostanovsky можете поставить >= (лично я именно так и предпочитаю), хотите - забейте. На глаз вы разницы увидеть все равно не сможете.
>Если m < p (при переполнении), если m > p?
У вас ардуина есть? Спросите ее. Привыкайте "быстро накидал тестовый скетчик". Пара переменных, пара принтов - и посмотрел что выходит.
Понятно, что "ждать сорок девять дней, что-бы увидеть переполнение" - не вариант. Ну так сделай "имитацию ситуации". Заведите пару переменных Unsigned long. Запихните в нужные вам значения. Вычтите одно из другого. Положите в третью. Выведите и посмотрите.
Или, если уж хочется "в режиме реального времени". Ну напишите свою millis() из будущего.
И пользуйтесь в коде ей. Если не вместо большого числа поставите 0- ничем от обычной миллис отличатся не будет. Чем больше число подставите - тем больше она "сдвинется в будущие".
Ну или по форуму... не раз этот вопрос обсуждался/объяснялся.
Кажись даже где-то в блоге меня с ним "догнали". Ага. вот http://alxarduino.blogspot.com/2013/09/ComfortablyBlinkWithoutDelay.html В конце там дописывал про это.
Не стоит заморачиваться, ибо там еще хитрее - запись раз в час, но в ячейку, соответствующую текущему часу. Т.е. по факту в одну и ту же ячейку повторная запись раз в сутки.
Да хоть с задержкой в пару минут, некритично для моих целей. Я оставлю ">" по двум причинам - глаз не спотыкается и обработка на несколько тактов короче, чем ">=" :)
Было такое, и, возможно, даже какие-то примеры приводились, но гугл меня игнорирует, а яша не слишком часто сюда заходит.