Баг при замене if (); if() на if(); else;
- Войдите на сайт для отправки комментариев
Здравствуйте.
Разрабатываю систему электронной отметке для спортивного ориентирвоания с использованием Arduino (https://github.com/alexandervolikov/sportIDuino)
Наткнулся на непонятный мне баг.
Весь код прошивки можно посмотреть тут:
https://github.com/alexandervolikov/sportIDuino/blob/master/Stantion/Ard...
Здесь привожу кусок проблемного кода:
void loop () { // clear various "reset" flags MCUSR = 0; // allow changes, disable reset WDTCSR = bit (WDCE) | bit (WDE); // set reset mode and an interval // WDTCSR = bit (WDIE) | bit (WDP2) | bit (WDP1) ; WDTCSR = bit (WDE) | bit (WDP2) | bit (WDP1); // set WDE, and 1 second delay wdt_reset(); // pat the dog rfid(); //производит запись чипа и меняет work на true в случае записи if (work) sleep_light(); //уход в сон на 250 мс if (!work) sleep_deep(); //уход в сон на 1000мс в случае бездействия более 6 часов. } // end of loop void sleep_deep(){ for (byte i = 0; i <= A5; i++) { pinMode(i,OUTPUT); digitalWrite (i, LOW); // ditto } // disable ADC ADCSRA = 0; // clear various "reset" flags MCUSR = 0; delay(1); // allow changes, disable reset WDTCSR = bit (WDCE) | bit (WDE); // set interrupt mode and an interval WDTCSR = bit (WDIE) | bit (WDP2) | bit (WDP1) ; // set WDIE, and 1 second delay wdt_reset(); // pat the dog set_sleep_mode (SLEEP_MODE_PWR_DOWN); noInterrupts (); // timed sequence follows sleep_enable(); // turn off brown-out enable in software MCUCR = bit (BODS) | bit (BODSE); MCUCR = bit (BODS); interrupts (); // guarantees next instruction executed sleep_cpu (); // cancel sleep as a precaution sleep_disable(); }
конструкции if, if на if else :
if (work) sleep_light(); //уход в сон на 250 мс else sleep_deep(); //уход в сон на 1000мс в случае бездействия более 6 часов.
Происходит некорректная работа программы, изначально в программе work==false, и она должна уходить в сон на 1 секунду, но вместо этого она уходит в сон на 8 секунд (те же 8 секунд, которые устанавливаются в начале loop при запуске watchdog, если там поменять на 4 секунды, будет уходить на 4). По каким-то причинам процедура срабатывает неправильно и не записывает новые биты в регистр watchdog.
c if, if работает нормально, сразу же уходит в сон на 1 секунду.
Если убрать watchdog из начала или убрать процедуру rfid(), то баг исчезает. Специально устанавливал библеотеку с проверкой объёма ОЗУ в разных частях программы, потребление не превышает 500 байт, памяти достаточно. Также баг исчезает если убрать процедуру NoInterrupts() из sleep_deep().
В принципе, "работает - не трогай", но такое поведение крайне странное и, возможно, есть какие-то уязвимости, которые могут повлиять на надежность всей системы. Поэтому буду весьма признателен, если подскажете откуда растут корни у этой проблемы.
Знакомый программист посоветовал протестировать с #pragma GCC optimize ("-O0")
В итоге программа отказалась работать вообще, просто резитится в начале loop.
Его соображения на этот счёт:
"
мне кажется там есть какая-то тонкость в сторожевом таймере, 4тактном ожидании и оптимизациях компилятора.
По умолчанию компилятор имеет опцию -Os, означающую оптимизацию размера бинарника. В общем случае бинарный код каждой функции зависит от кода всех остальных и изменение в одной части программы может изменить другую, логически даже не связанную. В частности это может привести к тому, что между инструкциями установки регистра может быть как больше так и меньше 4 тактов.
Поскольку sleep_light и sleep_deep отличаются друг от друга единственным битом, задающим интервал, можно предположить, что при оптимизации одной функции повезло, другой нет.
http://www.avrfreaks.net/forum/watchdog-enable
Задание прагмы
#pragma GCC optimize ("-O0")
заставляет компилятор не оптимизировать код идущий ниже, оставлять как есть. Если логика кода была бы не затронута компилятором, то и работа программы не изменилась бы. Работа изменилась, значит компилятор при оптимизации делает там что-то незаконное. Выключение оптимизации, на первый взгляд окончательно доламывает программу, но на самом деле, это переход бага из скрытой плавающей формы(когда он может появляться в любой момент при любом изменении исходного кода) в явную постоянную.
То, что у тебя сломались обе функции говорит о том, что дело в исходном коде. Однако оптимизация тоже что-то ломает и так получилось, что баг оптимизации в сочетании с багом в коде при определенной записи друг друга уничтожили. Как костыль это можно было бы и оставить, если бы оптимизация всегда происходила одинаково.
"
Не пробовали написать "как положено"?
Попробовал и со скобками, всё также, с двумя if работает с else вылазит баг.
Пробовал также и на разном железе, проблема остается.
извини, хрустальный шар разбился и не хочет показывать объявление переменных.
Не пробовали написать "как положено"?
А где то прописано о необходимости заключать в скобки после IF или ELSE один оператор, вызов функции или процедуры ?
извини, хрустальный шар разбился и не хочет показывать объявление переменных.
Тогда надо сходить по ссылке, которую ТС дал в начале.
https://github.com/alexandervolikov/sportIDuino/blob/master/Stantion/Arduino_Scetches/Stantion/Stantion.ino
Вообще, интересный казус.
а если попробывать так
Я понимаю, что проблемма где то глубже, но выяснить "симптомы" уже пол дела.
Предложенный вариант заработал.
Спасибо)) Теперь бы ещё понять почему))
Предположение: функция sleep_ligрt() меняет переменную work.
Предположение: функция sleep_ligрt() меняет переменную work.
нет, work меняется только в фунции rfid().
Прошу прощения. Наверное, стоило привести весь код, пусть он и длинный. К тому же не гитхабе он изменится при следующем коммите, а тут пусть будет в проблемном виде:
Самые удивительные вещи начинают происходить когда заканчивается память.
Тогда и перестановка двух букв будет похожа на волшебство -)
Память проверил. Тыкал функцию freeRam во всевомзожные места программы, всегда стабильно показывает 1565 (на Atmega328).
При компиляции пишет, что свободно 1591 байт.
Пока тыкал, обнаружил любопытную особенность. Если поставить delay(1); в любое место процедуры deep_sleep() между установкой битов вотчдога и концом процедуры, то баг исчезает.