пауза без delay
- Войдите на сайт для отправки комментариев
Доброго времени всем!
У меня постала задача поставить на электрокотел контроллер для управления нагревом. В котле присуцтвует 9 тен, соответственно 9 реле которые включают тенны.
Управляются реле через здвиговые регистыры.
Вроди как все получилось, но не могу справится с одной задачей:
нужно чтобы реле последовательно включались с интервало, допустим 5 секунд.
delay не подходит, так как зависает считывание показаний с датчиков и управление кнопками.
Millis тоже не подходит (ну возможно подходит, только я не понимаю) потому, что в процесе работы программы, изменяя какие то параметры (например включение\выключение, или выбор режима "ЕКО") реле включаются хаотично, не учитывая заданых интервалов в Millis.
Подскажите кто может, пожалуйста!! Как решить такову пробллему?
Зарание спасибо огромное!!!
Millis тоже не подходит (ну возможно подходит, только я не понимаю) потому, что в процесе работы программы, изменяя какие то параметры (например включение\выключение, или выбор режима "ЕКО") реле включаются хаотично, не учитывая заданых интервалов в Millis.
Реле не могут включаться хаотично (если у Вас по схеме помехи не гуляют как по бульвару). Они включаются точно тогда, когда их включает Ваша программа.
Давайте код и схему, посмотрим что там вызывает хаос.
Здравствуйте ЕвгенийП!
Спасибо большое что откликнулись! Схемы как таковой нет, потому, что потключаюсь Ардуиной к старой плате управления, на которой вылетел контроллер. Принцип очень простой - на здвиговые регистры отправляются 2 байта информации. Схемы не рисовал.)
Не судите очень громко)) так как только учусь и всего 2 недели как впервые попробовал програмировать!Чувствую что в коде много безсмысленных строк... Но пока смог только так!
вот это полотно! :-) sergey555, однотипные (идентичные) переменные, к которым применяются одни и те же операции, лучше паковать в массивы, а обработку их проводить циклично, функциями - так и код в разы сократиться (будет и места меньше в памяти занимать и читабельнее станет), и проект в целом легко масштабировать можно будет.
Понятно, Сергей.
Поправьте меня, если я где-то навру:
1. Вы хотите иметь интервал между включениями / выключениями, чтобы они не включались все сразу и не давали резкую нагрузку. Сейчас у Вас установлен интервал в три секунды.
2. Включение и выключение котлов производится с блоках (номера строк): 352-395, 397-440, 442-484, ... 1309-1351.
3. Все эти блоки одинаковые, за исключением строки для вывода на экран (типа "lcd.print(" P=0 ");") и параметров HIGH/LOW в вызовах функции Shifter.setRegisterPin. Кстати, и чего бы их не оформить функцией с двумя параметрами? Программа бы в разы сократилось, ну, да ладно.
4. Хаотичность выражается в том, что пока там они включаются, может поступить новая команда - включаться по-другому. При этом все переменные, хранящие текущие millis уже имеют значения, в общем голову сломаешь, пока их починишь и всё настроишь.
Пожалуйста, поправьте, если что не так.
Теперь вопрос к Вам. Устроит ли Вас чтобы каждый тэн имел своё фиксированное время включения/выключения? Например, на нулевой секунде может вкл/выкл только первый (другие - никогда), на третьей - воторой, на шестой - третий, ... на 24-ой - девятый, и снова - на 27-ой - первый, на 30-ой - второй и т.д.
Т.е. допустим первый двигатель может вкл/выкл только на 0-ой, 27-ой, 54-ой и т.д. секундах. В промежутках он всегда остаётся в том состоянии (включён или выключен), в которое перешёл в последний раз.
Если так устроит, то программу можно поменять очень просто - малой кровью. Расписывать сейчас не буду, т.к. если Вас это не устроит, то чего пиисать? Кстати, если не устроит, то напишите как устроит.
Возможно, я смогу Вам ответить только поздно вечером. Сейчас я в дороге и батарея садится :( Если не успеем пока сядет, то тогда вечером. Пока ответьте мне как сможете.
Евгений, Вы все правильно разобрали мою проблему! Спасибо!
Наверно Вы на 100% правы по поводу функций, только я пока не знаю как это делается:)
Я думаю что меня устроит такой вариант, что Вы предлагаете, главное, чтобы при переключении какого то режима работы отщет начинался заново (также как работает delay, только без заморозки выполняемой программы). Конечно, если у меня с Вашей помощью или как там получится, все начнет работать коректно, то я займусь "порядком" в самом коде, попробую собрать в функции или в масивы:)!
Dimanoss спасибо большое за совет!
Буду стараться в конечном итоге весь код собрать грамотно!
Просто "я еще не волшебник, я только учусь":)
Пишите подсказки, делитесь знаниями! Не только я а и многие другие будут весьма благодарны за Вышу информацию!!
Ну, если правильно понял, то давайте попробуем.
Для начала, полный список того, что я поменял в программе:
Больше я не менял ничего.
Прочитайте мой комментарий в строке 381 и поступите также во всех остальных местах, т.е. вместо Ваших длинных кусков, вставьте мой кусок с вызовом функции setupHeaters. Таких длинных кусков у Вас в программе аж 22 (если я не ошибся. Я поменял только один. Поменяйте остальные по образу и подобию (см. комментарии в строках 383-385).
У Вас 9 тэнов. Я их нумерую с 0-го по 8-ой. Номера пинов, соответствующие тэнам определены в строке 306. Т.е. 0-ой тэн у нас на пине 5, 1-ый – на пине 2, второй – на пине 9 и т.д.
Теперь давайте о логике работы.
Самое важное здесь это переменная allTenStates (строка 302). Она всегда в соответствующих битах содержит желаемое состояние пинов соответствующих тэнов (именно желаемое, а не реальное – это важно!). Т.е. если в нулевом бите 1, в первом 0, а во втором снова 1, это значит, что логика Вашей программы хочет чтобы пины тэнов 0 и 2 (пины 5 и 9) были в HIGH, а пин тэна 1 (пин 2) – в LOW. Повторяю, в этой переменной содержатся ЖЕЛАЕМЫЕ состояния пинов. Они могут совпадать с реальными, а могут и не совпадать. В этом вся фишка.
Функция setupHeaters (строки 382-386) как раз устанавливает эти желаемые состояния. Она вызывается из Вашей логики (пример вызова в строках 382-386, остальные вызовы допишете сами). Она выводит нужную строку на экран и устанавливает желаемые состояния. Её можно вызывать сколь угодно часто. Она никак не завязана на время, т.к. она ничего не делает с реальными пинами. Она лишь устанавливает их желаемое состояние.
С этой функцией всё понятно? Её цель – обеспечить, чтобы переменная allTenStates всегда содержала правильное, самое последнее – актуальное желаемое состояние пинов.
Теперь переходим к функции realSetupHeaters (строки 330-331). Она должна обязательно вызываться при каждом проходе loop (я делаю это в строке 331) и ей передаётся параметр – текущее значение millis (чтобы ей лишний раз не запрашивать).
Она чётко делится на три части.
Первая часть (строки 316 – 318) – отвечает за то, что бы остальные части работали строго раз в 3 секунды (на самом деле временной интервал задан в константе TEN_OPERATION_INTERVAL – строка 304). Если интервал ещё не истёк, она возвращает управление в loop (строка 318).
Вторая часть (строки 319-320) считает номер тэна который в этот раз будем включать/выключать и помещает его в переменную. currentTen. Этот номер равен остатку от деления «количества пошедших с начала работы программы трехсекундных интервалов» на количество тэнов. Т.е. в самом начале мы обслуживаем тэн 0, через 3 секунды – тэн 1, через три секунды – тэн 2 и т.д.. После тэна 8, через 3 секунды мы снова обслужим тэн 0.
Наконец, третья часть (строки 321-322) приводит состояние пина текущего тэна в соответствие с «желаемым», которое берёт из переменной allTenStates. Если он и так уже был в нужном состоянии – ничего страшного. Ну был он HIGH, а мы его ещё раз в HIGH перевели – хуже не будет – ещё HIGH’ее он не станет.
Вот и вся логика функции realSetupHeaters
Теперь Вы понимаете общий замысел. Мы отделили логику принятия решений от их (решений) исполнения. Решения принимает Ваш длинны if-else-if-… моя функция setupHeaters их (решения) просто записывает для хранения, а функция realSetupHeaters независимо от того когда решения приняты, просто исполняет их строго по своему расписанию.
Такой приём – «отделение логики принятия решений» от «исполнения решений» - очень частая вещь в архитектуре программ. Это позволяет чётко резать программы на независящие друг от друга куски. Например, один кусок ставит запросы в очередь на обработку, а обработчик - обрабатывает. Первый кусок вообще не знает что там делается с запросами - он их только в очередь ставит, а второй понятия не имеет откуда они в очереди берутся. Зато менеджер процессов может наблюдать за очередью и если она растёт - запустить дополнительный обработчик. а если иссякла - загнуть один их обработчиков - нефиг ресурсы жрать. Это вообще нормальная практика.
Разумеется при такой логике работы, если мы включили (уже реально включили) какой-то тэн, а логический блок буквально через миллискунду передумал и приказал его выключить, реально он выключится только тогда, когда до него дойдёт очередь, т.к. через 27 секунд. Но я заранее спросил Вас, устраивает ли Вас такой подход, так что «кушайте теперь» :))))
Ну, ладно, если чего неясно – спрашивайте.
P.S. У меня нет Вашей схемы, и проверить я не могу. Вроде всё нормально, но если где лопухнулся-таки, Вы опишите повнятнее затык. Подумаем, может попрошу Вас куда-нибудь отладочную печать поставить – разберёмся.
Крутняк!!:)
Спасибо Евгений!!
Разложили как на пальцах! Практически даже все понял, несмотря на мой мизерный опыт!
Переписал код аналогично Вашему примеру, и вот что получилось:
компилятор выдал ошибку "'EEPROM' was not declared in this scope" и указал на строку 297 (также на все подобные строки)
Вы их тоже изменяли, да?
С чем это может быть связано?
Разобрался, у меня была подключена не та библиотека. Поменял но теперь выскочила следующая ошибка "expression cannot be used as a function" на строке 303.
Это уже не могу разобрать(((
компилятор выдал ошибку "'EEPROM' was not declared in this scope" и указал на строку 297 (также на все подобные строки)
Вы их тоже изменяли, да?
С чем это может быть связано?
Простите, забыл сказать. У меня другая библиотека епрома и чтобы скомпилировать код я поменял все вызовы.
1. Верните свою библиотеку, что была и
2. сделайте замену типа "Заменить все" вот таким образом
2.1 EEPROM.write везде замените на EEPROM_write_byte
Вот что получилось
Сейчас ошибка в 303й строке, но я там переставил единицу и больше той ошибки не выдает компилятор! Проверьте пожалуйста, правильно ли я сделал.
Но еще вылезла ошибка в строке 563 "expected primary-expression before ')' token"
Строка 563 - Вы удалили третий параметр вызова функции. Если не нужен - поставьте 0, но удалять нельзя. Тоже проо строку 411 и про 570. Может ещё где, смотрите (если что сообщение выдаст).
В 303 строке 1 убирайте, там всё правильно, ошибка наведённая. Должно быть
#define TEN(x) ((uint16_t)1 << (x))
Где-то Вы использовали TEN без скобок или с пустыми скобками. Найти пока не могу, нашёл только в строке 546 у Вас TEN1 вместо TEN1 - но это должна быть самостоятельная ошибка.
Кстати, про наведённые ошибки, если бы Вы скопипастили сообщение полностью, было бы видно откуда она вылзла. Впредь копипастите пожалуйста блок сообщений целиком (выделите и Ctrl+C скажите).
В общем исправляйте всё, что я написал. Если будут ошибки, снова выкладывайте и код и ошибки, но посмотрю я их только завтра утром.
sergey555, вот теперь лучше. не зря Вы копипастили.
Вот смотрите, что написано в сообщении
Строки 1 и 3 - это про одно. Дел в том, что была ошибка в строке 411 и из-за неё не смог развернуться макрос в строке 303. Исправьте в 411 и про 303 само уйдёт (наведённая ошибка).
А в 411 у Вас просто прпущена | между TEN(5) и TEN(6).
Со строкй 546 поняно, скобки пропустили.
В строке 567, видимо, 0 забыли вставить и там параметр пропущен.
Евгений, все перепроверил и исправил на сколько понимаю!
Но все равно ошибка... не понятная для меня
Сергей, учитесь читать такие сообщения. Вот, смотрите, сообщение в строке 29 (в портянке сообщений) явно наведённое, надо искать рядом другое с упоминанием макроса TEN. Оно как раз в строке 31. Там говорится, что с макросом TEN проблемы в строке 393. Отлично, смотрим в строку 393 программы и видим, что между TEN(5) и TEN(6) проущена вертикальная черта. Видите? Исправляйте.
Евгений учусь и очень стараюсь!! Даже кое что и получается!
Спасибо за Вашу помощь!!!
Все поисправлял и теперь скетч компилируется и загружается на плату! Визуально (на дисплее) все корректно работает, но вот ТЕНы не включаются. Как бы не посылается сигнал команды... все время выключены, при любых условиях.
есче запутался с кнопкой включения\выключения (строки 254...281). Мне удалось организовать с помощю АЖ трех кнопок, чтобы всего лиш елементарно включить \выключить котел!!((((
Может тоже подскажете ...:) пожалуйста!!
Визуально (на дисплее) все корректно работает, но вот ТЕНы не включаются. Как бы не посылается сигнал команды... все время выключены, при любых условиях.
Давайте разбираться. Для начала нужно понять, кто виноват. Программа или схема. Как Вы понимаете, программа ничего не включает. Её дело лишь подать HIGH или LOW на нужный пин. Если она подёт правильно, а тэн не включается - это уже вопрос к схеме. Поэтому, давайте выясним правильно ли программа включает пины. Для этого
1) в setup добавьте Serial.begin(...
2) строку 304 закомментируйте (выбрасывать не надо, пригодится ещё)
3) сразу после закомментированной строки 304 вставьте код:
(у меня нет под рукой компилятора, так что если какая опечатка, уж поправьте сами).
Теперь, каждый три секунды в Serial будет уходить строка. содержащая номер пина и значение в которое он устанавливается. Проанализируйте, всё ли правильно.
Если всё правильно, то смотрите схему "почему на пин подаём правильно, а тен не работает". Если же в этом месте пины устанавливаются неправильно, дайте мне копию того, что выводится в сериал монитор (только не картинку, а скопипастите текст) я проверю по битам и поговрим дальше.
есче запутался с кнопкой включения\выключения (строки 254...281). Мне удалось организовать с помощю АЖ трех кнопок, чтобы всего лиш елементарно включить \выключить котел!!((((
Может тоже подскажете ...:) пожалуйста!!
Давайте дожмём одно, а потом переключимся.
Вы имеете ввиду пмн на моих здвиговых регистрах?
Просто напомню у меня тены подключены через здивиговые (2) регистры.
С ардуины подаются толь 16 бит
Вот что в Serial
И.Т.Д
Блиин!
allTenStates - всегда 0!
Посмотрите на строки 293-294. Ну, понятно же, что чёртова опечатка!
Разумеется в нижней должно быть toLow, а не toHigh.
Правьте :)
Вы имеете ввиду пмн на моих здвиговых регистрах?
Просто напомню у меня тены подключены через здивиговые (2) регистры.
С ардуины подаются толь 16 бит
Это мне без разницы. Постарайтесь перенять эту привычку - всегда четко разделяйте сущности. Без этого системному инженеру не жить.
Вот смотрите, в данный момент мы отлаживаем программу. Программа выставляет пины (чьи они - сейчас нам всё равно). Нас ПОКА вообще не волнует, что просиходит вокруг, до тех порЮ пока мы не будем уверены, что пины выставляются правильно. Тогда, если система не заработает, будем смотреть дальше, но мы уже будем уверены, что пины выставляются хорошо - это будет наш тыл, точка от которой мы безопасно отталкиваемся. Понимаете, большую систему охватить одним взлядом невозможно. Её надо делить на куски и работать с кусками. А глобальным взлядом охватывать уже крупные куски, но для этого нужно быть в этих кусках стопудово увереным. Вот сейчас мы дожимаем выставление пинов. Печать показала, там есть проблема. Исправим. Если печать покажет, что пины выставляются хорошо, а работать таки не будет, будем смотреть схему и т.п. пока не заработает.
Евгений все ЗАРАБОТАЛО!!!
Так как вы и описывали!)))
вообще работает так как мне и нужно!!!!
Сасибо огромное чисто человеческое!!!
честно говоря я только 2 недели как вообще познакомился с програмированием и ардуиной!! Так что простите, если выгляжу как первокласник! Стараюсь учится и понимать! Но когда Вы мне присылаете куски кодов, я понимаю что вообще ничего не понимаю!!
Да, незачто, обращайтесь, если что.
Та вот по поводу кнопок... Просил есче:)
если можте конечно!
есче запутался с кнопкой включения\выключения (строки 254...281). Мне удалось организовать с помощю АЖ трех кнопок, чтобы всего лиш елементарно включить \выключить котел!!((((
Может тоже подскажете ...:) пожалуйста!!
Могу. только объясните подробно и толково что нужно, и что есть сейчас.
Отвечать я смогу сегодня только от случая к случаю (типа раз в час), а завтра нормально можно будет поработать.
Отлично! Так и наберусь пратики хоть чуть!
Вот кусок который управляет вкл\выкл котла
Но тут как видите мне нужно сначала нажать кнопку "4" потом кнопку "3" чтобы выключить (логический 0), или снова "4" чтобы включить. После чего кнопкой "5" сохраняю выбраное состояние! Бред конечно!! но так у меня получилось и все(((
Хотелось бы чтобы нажал кнопку "4" - включился котел, нажал есче раз - выключился.
Хотелось бы чтобы нажал кнопку "4" - включился котел, нажал есче раз - выключился.
У вас очень много пробелов в знании. Так что с разбегу и нахрапом не получится.
При загрузке в скетча . EEPROM обнуляется принудительно. Так что нереально одним скечем загузить ЕЕPROM, а потом закоментить запись и работать дальше. Для тестировки и добавление в программу скину вам код.
Хотелось бы чтобы нажал кнопку "4" - включился котел, нажал есче раз - выключился.
Ну, так и делайте, кто Вам не даёт-то?
Здарвствуте Евгений!
Хочу высказать Вам большую благодарность за помощь в реализации моего проекта! Долго не писал о результатах, так как хотел испытать на практике (зима пришла)! Уже болие месяца проект работает отлично!!
За исключением одного момента, который, как оказалось немножко не коректно работает. Его я не упоминал в данной теме, потому что не считал столь важным. Но в итоге получилось так, что на практике етот момент оказался не мение важным.
И так:
Помимо тенов контроллер включает тем же битовым сигналом вводной пускатель и циркуляционный насос.
Получается если ети два "контакта" добавить в виде дополнительных тенов, то они включаются по своей очереди.
А нужно чтобы включение выглядило так : включаем коьел - включается вводной пускатель - через 5 секунд циркуляционный насос - через 5 секунд все тены, согласно настроек.
А когда отключаем котел - выключаются тены, согласно настроек - через 30 секунд выключается циркуляционный насос - через 20 секунд выключается вводной пускатель.
Но есть возможность, перепаяв некоторые ножки на плате котла, устроить управление включением\выключением определенного пина. Но все же важен тот момент, что ети два пина (пускатель и насос) должны включатся \выключатся именно так как я описал выше.
Спасибо большое!!!