Приветствую всех. Помогите разобраться. Есть один скетч вывода на TFT различных показаний, в нем использовал отсчет через millis. Обновление данных каждую секунду: Код (C++): в Setup: currentTime = millis(); // считываем время, прошедшее с момента запуска программы MainTime = currentTime; в Loop: currentTime = millis(); if (currentTime >= (MainTime + 1000) or Redraw) { MainTime = currentTime; ...Код... Все прекрасно отрабатывается ровно через секунду. Потом написал другой скетч. В Setup все тоже самое, в Loop: Код (C++): currentTime = millis(); if (currentTime >= (RegTime + 10000) and Done) { RegTime = currentTime; Randomizer(Min, Max); } Срабатывает раньше чем через 10 секунд. Так же пробовал и с таким вариантом: Код (C++): if (Done) { digitalWrite(13, HIGH); if (currentTime >= (RegTime + 10000)) { RegTime = currentTime; digitalWrite(13, LOW); Randomizer(Min, Max); } } Судя по светодиоду, пролетает то за 5 секунд, то за 3. Вобщем мне нужно чтобы таймер запускался только после того как будет выполнено условие Done. PS В каком месте правильнее ставить Time = currentTime, сразу после if, или в конце на выходе из if? PPS Можно ли использовать в if - and, or, или только &&, ||? Компилятор не ругается.
Можно, это стандартные альясы операторов && и || в С++. По поводу работы с интервалами: надеюсь, currentTime и MainTime у вас имеют тип unsigned long, это, что называется, раз. Два - так работать с беззнаковыми числами, как работаете вы, замеряя интервал - неправильно. Вкратце, не вдаваясь в особенности беззнаковой арифметики, правильный вариант замера интервала между двумя беззнаковыми числами таков: Код (C++): unsigned long lastTime = millis(); ...... unsigned long curTime = millis(); if(curTime - lastTime >= interval) { lastTime = curTime; .... }
У меня они объявлены как uint32_t. Правильно ли я понял из вашего ответа, что назначение lastTime нужно делать сразу после входа в if? lastTime в Setup объявляется, или в начале Loop? curTime назначать один раз в начале Loop, или перед каждым if где проверяется таймер?
Я предпочитаю так: Код (C++): unsigned long curTime = millis() + interval; if(curTime < millis()) { ... curTime = millis() + interval;; } Мелочи, но на один оператор в сравнении меньше, стало быть теоретически быстрее и короче.
Неправильно предпочитаете Смотрите: millis отдала значение, близкое к максимальному для unsigned long (скажем, на 1 меньше). Вы прибавили interval, и в curTime у вас стало, из-за переполнения, минимальное значение. Поэтому ваш код отработает правильно в этом случае, но - неправильно в других, когда millis() + interval ещё не достигли максимального значения для unsigned long. Я настаиваю, что единственно расово верный вариант проверки интервала при работе с беззнаковой арифметикой - следующий: Код (C++): if(millis() - lastTime > interval) { lastTime = millis(); .... } Какие операторы сравнения юзать - неважно, можно "больше", можно "больше либо равно" - на быстродействие это не повлияет никак. А вот интервал всегда будет подсчитан верно, вне зависимости от значения millis(), т.е. даже в том случае, когда millis() между вызовами loop начнёт маслать с самого начала
Мои счетчики не переполняются, устройства которые делал работают без выключения в пределах недели, а чаще всего нескольких часов. Для таких условий счетчик работает правильно. Да и если переполнится, сбойнет разок, ничего страшного, да и предусмотреть можно этот момент.
uint32_t - это то же самое, что unsigned long. Примеры правильной работы с интервалами я вам привёл. Какие именно вопросы вас интересуют?
Несколько переменных - это первый вариант. Простейший класс написать - это второй вариант. Какой именно для вас предпочтительней - тот и реализуйте. Я делаю несколько по-другому: в loop высчитываю дельту между предыдущим и текущим значением millis. Эту дельту скармливаю классам, которые внутри себя уже сами разбираются, что с ней делать. По итогу - для интервалов меньше чем 65535 миллисекунд нет надобности в каждом классе держать uint32_t, достаточно - uint16_t, экономия Выглядит это добро примерно так: Код (C++): unsigned long lastMillis; IntervalWorker worker(1000); void setup() { lastMillis = millis(); } void loop() { unsigned long curMillis = millis(); unsigned int delta = curMillis - lastMillis; lastMillis = curMillis; if(worker.Update(delta)) { // интервал настал } } где класс для работы по интервалу может выглядеть примерно так: Код (C++): class IntervalWorker { public: IntervalWorker(uint16_t interval) : _interval(interval), _delta(0) {} bool Update(uint16_t dt) { _delta += dt; if(_delta >= _interval) { _delta -= _interval; return true; } return false; } private: uint16_t _interval; uint16_t _delta; }; Более того - сделав абстрактный класс и определив виртуальную функцию, которая будет вызываться по достижению интервала - можно наплодить наследников класса, реализовав внутри каждого специфичный функционал. Т.е. подходов, как видите - over9000. З.Ы. Приведённый пример - тестовый и никакого отношения к реальной жизни не имеет, а призван лишь показать вариант подхода к решению проблемы.
Все таймеры переписал по канону, но не выходит каменный цветок. Пролетает таймер... Опишу чего я хочу сделать: RGB лампа настроения. Функция выдает 3 рандомных значения RGB. Через таймеры со случайными задержками каждый цвет доползает до заданных значений, и сообщает true. И я хочу чтобы таймер на 5 секунд начинал отсчет только после того как все цвета выдали true. Прошло 5 секунд, и новый рандом RGB. Кусок кода с таймером на fade: Код (C++): // Green Fader curGreenTime = millis(); if (curGreenTime - prevGreenTime >= FadeDelayG) { prevGreenTime = curGreenTime; if (G == CurG) { GDone = true; } else { if (G > CurG) { analogWrite(LG, 255-CurG); CurG++; } else { analogWrite(LG, 255-CurG); CurG--; } } }
Тогда вам надо включать таймер на 5 секунд только после того, как все цвета выдали true, очевидно же. Т.е. ввести флаг для последнего таймера, и включать его только тогда, когда остальные таймеры уже отработали. Вот доработанный класс: Код (C++): class IntervalWorker { public: IntervalWorker(uint16_t interval) : _interval(interval), _delta(0), _enabled(false), _continuousMode(true) {} bool Update(uint16_t dt) { if(!_enabled) return; _delta += dt; if(_delta >= _interval) { _delta -= _interval; setEnabled(!_continuousMode); return true; } return false; } void setEnabled(bool en) { _enabled = en; } void setContinuousMode(bool cm) { _continuousMode = cm; } private: uint16_t _interval; uint16_t _delta; bool _enabled; bool _continuousMode; }; Позволяет сделать однократное срабатывание таймера ( setContinuousMode(false) ), позволяет вкл/выкл таймер ( setEnabled(true/false) ).
Так я вроде и включаю только после входа в if. Где косяк? Пробовал ставить curRegTime = millis(); и до входа в if, и после, пролетает всеравно. Код (C++): if (AllDone) { curRegTime = millis(); digitalWrite(LL, HIGH); if (curRegTime - prevRegTime >= 5000) { prevRegTime = curRegTime; digitalWrite(LL, LOW); Randomizer(Min, Max); } } Я классы еще не вкурил. Только разбираюсь. Мне бы пока в таком варианте сделать чтобы работало нормально =)
Вы ими уже пользуетесь - всякие Serial и т.п. Что мешает поюзать ещё один? По теме: дайте полный код, плз.
Пользоваться пользуюсь, но суть еще не познал. Что именно в них нужно ставить, что им передавать, и что забирать. Вообщем для чего их лучше всего использовать. Чего я пытаюсь получить. Яркость диода ползет до нужных значений, если это Regular цвет, то ждем 3 секунды, и рандомим новый. Если это Solid, то ждем 7 секунд, чтобы насладится цветом. Ну и новый рандом. LED13 использую для контроля входа и выхода из if. Код (C++): #define LR 3 // Red LED #define LG 5 // Green LED #define LB 6 // BLue LED #define LL 13 // L-LED // Byte Vars byte R, G, B; byte CurR = 0; // Текущая яркость byte CurG = 0; byte CurB = 0; byte Min = 0; // Пределы яркости byte Max = 255; // byte minlim = 37; // Лимиты для проверки byte AverLim = 127; byte MAXLIM = 207; // --- // Int Vars uint16_t FadeDelayR = 37; uint16_t FadeDelayG = 37; uint16_t FadeDelayB = 37; uint16_t RegDelay = 3000; // Интервалы uint16_t SolidDelay = 7000; uint16_t curSolidTime, curRegTime, curRedTime, curGreenTime, curBlueTime; // Текущие таймеры uint16_t prevSolidTime, prevRegTime, prevRedTime, prevGreenTime, prevBlueTime; // Boolean Vars boolean Solid = false; boolean RDone, GDone, BDone, AllDone; boolean Debug = false; void setup() { // Setup Begin randomSeed(analogRead(0)); pinMode(LR, OUTPUT); pinMode(LG, OUTPUT); pinMode(LB, OUTPUT); pinMode(LL, OUTPUT); Randomizer(Min, Max); prevSolidTime = millis(); prevRegTime = millis(); prevRedTime = millis(); prevGreenTime = millis(); prevBlueTime = millis(); // End of Setup } void loop() { // Loop Begin // Red Fader curRedTime = millis(); if (curRedTime - prevRedTime >= FadeDelayR) { prevRedTime = curRedTime; if (R == CurR) { RDone = true; } else { if (R > CurR) { analogWrite(LR, 255-CurR); CurR++; } else { analogWrite(LR, 255-CurR); CurR--; } } } // Green Fader curGreenTime = millis(); if (curGreenTime - prevGreenTime >= FadeDelayG) { prevGreenTime = curGreenTime; if (G == CurG) { GDone = true; } else { if (G > CurG) { analogWrite(LG, 255-CurG); CurG++; } else { analogWrite(LG, 255-CurG); CurG--; } } } // Blue Fader curBlueTime = millis(); if (curBlueTime - prevBlueTime >= FadeDelayB) { prevBlueTime = curBlueTime; if (B == CurB) { BDone = true; } else { if (B > CurB) { analogWrite(LB, 255-CurB); CurB++; } else { analogWrite(LB, 255-CurB); CurB--; } } } if (RDone && GDone && BDone) { // Сверяем что все цвета вышли на заданные значения AllDone = true; } if (AllDone and !Solid) { // Если все готовы, и это не сплошной цвет curRegTime = millis(); digitalWrite(LL, HIGH); if (curRegTime - prevRegTime >= RegDelay) { prevRegTime = curRegTime; Randomizer(Min, Max); digitalWrite(LL, LOW); } } if (AllDone && Solid) { // Если все готовы и сплошной цвет curSolidTime = millis(); digitalWrite(LL, HIGH); if (curSolidTime - prevSolidTime >= SolidDelay) { SolidTime = currentTime; Solid = false; Randomizer(Min, Max); digitalWrite(LL, LOW); } } // End of loop } void Randomizer(byte Min, byte Max) { // Рандомайзер цвета с ограничителями, чтобы не выдавать цвета близкие к белому и тд NewRand: R = random(Min, Max); G = random(Min, Max); B = random(Min, Max); if (R < minlim && G < minlim && B < minlim) goto NewRand; if (R > MAXLIM && G > MAXLIM && B > MAXLIM) goto NewRand; if (R < AverLim && G < AverLim && B < AverLim) goto NewRand; // < if (R > AverLim && G > AverLim && B > AverLim) goto NewRand; // > RandDelay(7, 37); if (R > MAXLIM && G < minlim && B < minlim) { RandDelay(37, 73); Solid = true; goto Done; } // Red if (R < minlim && G > MAXLIM && B < minlim) { RandDelay(37, 73); Solid = true; goto Done; } // Green if (R < minlim && G < minlim && B > MAXLIM) { RandDelay(37, 73); Solid = true; goto Done; } // Blue if (R > MAXLIM && G > MAXLIM && B < minlim) { RandDelay(37, 73); Solid = true; goto Done; } // Yellow if (R < minlim && G > MAXLIM && B > MAXLIM) { RandDelay(37, 73); Solid = true; goto Done; } // Cyan if (R > MAXLIM && G < minlim && B > MAXLIM) { RandDelay(37, 73); Solid = true; goto Done; } // Purple Done: RDone = false; GDone = false; BDone = false; AllDone = false; } void RandDelay(byte Min, byte Max) { // Рандомайзер фэйдеров FadeDelayR = random(Min, Max); FadeDelayG = random(Min, Max); FadeDelayB = random(Min, Max); }
Сходу не разберёшься. Такой код, как у вас - очень трудно сопровождать, т.к. комментариев - ноль, декомпозиции - ноль, имена переменных - маловразумительные. Возможно, как у мну будет настроение - я покопаюсь, но сейчас - увы, сходу ничего подсказать не могу.
Добавил комментарии. Все переменные в составе которых есть R, G, B, относятся к цвету. В любом случае, спасибо за помощь.
Так, давайте по-другому: ещё раз напишите весь алгоритм словами, я постараюсь накидать код, и вы сравните, что отличается в двух вариантах - вашем и моём, ок?
1. Рандомим RGB. Цвета могут быть промежуточными, и сплошными (красный, фиолетовый и тд, флаг Solid) 2. Плавно делаем fade яркость светодиода до этих значений, и говорим - готово 3. Таймер один - если все цвета готовы, ждем мало (1-3 сек), и рандомим новые 4. Таймер два - если цвета готовы, и у нас есть флаг Solid, ждем дольше (5-7 сек), и все сначала. У меня весь затык в таймерах. С delay все прекрасно работает, но не хочу использовать, так как буду добавлять еще другой код. Да и delay не комильфо =)
1. Навскидку, флаг Solid не нужен. 2. По остальному - попробую набросать и отпишусь. Ещё вопрос: fade делать только вверх, вниз не надо?