Нет такой проблемы, поэтому обходить нечего. Главное, правильно писать код: Код (C++): if(millis() - lastMillis > interval) { // тут работа по интервалу } И всё. Ключевые слова - беззнаковая арифметика, в википедии есть статья, ознакомьтесь
Проблемы нет до тех пор пока необходимое значение для переменной 'interval' помещается в 32 бита (тип unsigned long), т.е. примерно 49..50 дней. А если нужны интервалы с более длительным сроком? То в таком случае пока вижу два решения: внешний модуль RTC; реализация функционала RTC с использованием вычислительных мощностей микроконтроллера. В первом случае достоинства: не расходуются вычислительные мощности микроконтроллера, микроконтроллер можно отправлять в спячку для экономии эл.энергии. Недостатки: дополнительные финансовые затраты, модуль нужно будет подключать к мк, соответственно потребуется пара выводов мк. Во втором случае наоборот: дополнительных финансовых затрат нет, пины все остаются свободными, но мк должен постоянно молотить, соответственно энергосбережение уже сложнее реализовать и тратятся вычислительные ресурсы микроконтроллера (но увеличение затрат ресурсов приемлемое, по крайней мере на скорость работы мк почти не влияет)
Благодарю. Я такую структуру и использую. Но через примерно день-два перестает работать код: Код (C++): void function_led_button1 (uint16_t time_0, uint16_t time_1) { static bool state = 0; // состояние светодиода static uint32_t future = 0; // время будущего перескока на другое состояние if (millis() < future) return; // если время не пришло(нужное значение millis() ) то выйти из функции state = !state; // время смены пришло поменяйте состояние future += state ? time_0 : time_1 ; // определите новое время для перескока если state = 1 то future=future+time_0 // если state = 0 то future=future+time_1 digitalWrite(led_button1, state); } Этим кодом управляется моргание светодиодом. Вызов: Код (C++): //мигание светодиодом кнопки1 if (flag_button1 == 1) { function_led_button1(250, 500); } if (off_light_holl == 1) { function_led_button1(250, 1000); } if ((off_light_holl == 0) && (flag_button1 == 0)) { digitalWrite(led_button1, HIGH); } Должно сработать первое условие, но на деле - третье (светик горит постоянно). Хотя значение переменных отслеживал в мониторе - норма.
Попробуй этот код. Код (C++): switch (flag_button1+off_light_holl+off_light_holl) { case 0: digitalWrite(led_button1, 1); break; case 1: function_led_button1(250, 500); break; case 2: function_led_button1(250, 1000); break; } Но скорее у вас что-то с кнопками. Переполнения там точно не должно быть
Предлагаю подумать, будет ли обновляться состояние светодиода, если flag_button1 == 0 Вы один раз вызываете функцию function_led_button1 при зажатой кнопке, а потом благополучно про ней забываете. При этом, если off_light_holl == 0, и flag_button1 == 0 - вы совершенно очевидно зажигаете светодиод. И получаете ровно то поведение, которое запрограммировали - светодиод будет гореть постоянно, с высокой долей вероятности (поскольку он может и мигать, если держать кнопку зажатой очень долго). Перепишите логику, и всё заработает как надо
Не совсем так. Код (C++): //диммер кладовка if (((time.Hours < 0 || time.Hours > 10) && sun_klad_2 == false && off_light_holl == false) || (flag_button1 == true)) { //включаем диммер digitalWrite(outPin5, HIGH); light_klad = true; } else { light_klad = false; digitalWrite(outPin5, LOW); } flag_button1 - своего рода метка, что была нажата кнопка и она будет 1 определенное время или пока повторно не нажата кнопка. Эта метка в числе других включает реле. Вот код, устанавливающий значение flag_button1: Код (C++): //проверка состояния кнопки1 - холл if ( bouncer1.update() ) { //если считано значение 1 if ( bouncer1.read() == HIGH) { if (light_holl == false) { previousMillis_button1 = currentMillis_button1; off_light_holl = false; flag_button1 = true; } if (light_holl == true) { if (flag_button1 == true) flag_button1 = false; else off_light_holl = true; } } } if (flag_button1 == true) { if (currentMillis_button1 - previousMillis_button1 > interval_button1) { flag_button1 = false; } } Оба фрагмента кода находятся в loop
Суть этого "безобразия": свет зажигается\гаснет или по времени (часы), или по значению датчика света, или принудительно с помощью кнопки (при этом горит он 30 минут).
Давайте немного подробнее. Функция millis() использует timer0 микроконтроллера и возвращает 4х байтовое число без знака, максимальное возвращаемое значение помещается в тип переменной unsigned long (целое число без знака) такое значение может принимать максимальное значение 4 294 967 295 миллисекунд, что соответствует примерно 49 дням и 17 часам, я понимаю, что значение возвращаемое данной функцией обнулится по истечение данного времени непрерывной работы микроконтроллера. Чтобы избежать ошибки цикла программ завязанных на данный таймер, предлагаю такое решение: Код (C++): /**************************************************************************** Включаем светодиод на 13 пине на одну секунду через заданный интервал времени **************************************************************************/ //Объявляем гобальную переменную для хранения времени последнего срабатывания счетчика: unsigned long previousMillis = 0; //объявляем куда подключен светодиод: #define ledPin 13 void setup() { pinMode(ledPin, OUTPUT); } void loop() { //узнаем текущее время непрерывной работы МК в миллисекундах: unsigned long currentMillis = millis(); /************************************************************************ Проверяем условие - при переполнении millis() и обнулении значения currentMillis условие сработает и мы корректируем значение времени последнего previousMillis: ***********************************************************************/ if(previousMillis > currentMillis){ previousMillis = currentMillis; } /*********************************************************************** Далее включаем светодиод не ранее раз в 360 сек или 6мин, условие истинно пока не обнулится значение переменной currentMillis далее без корректировки значения previousMillis условие будет ложно ***********************************************************************/ if(currentMillis - previousMillis > 360000){ // запоминаем время включения светодиода previousMillis = currentMillis; digitalWrite(ledPinOn,HIGH); delay(1000); digitalWrite(ledPinOn,LOW); } } По идее таким образом все должно работать без ошибок, даже если непрерывное время работы микроконтроллера превышает 50 дней. Остается одна незначительная проблемка - при обнулении времени пропускается один цикл срабатывания, как это избежать пока не догадался.
VitalyS Все это понятно.но зачем вы запулили delay() в программу где идет millis()? ума что ли не хватило убрать и это безобразие.
Этот кусок кода: Код (C++): /************************************************************************ Проверяем условие - при переполнении millis() и обнулении значения currentMillis условие сработает и мы корректируем значение времени последнего previousMillis: ***********************************************************************/ if(previousMillis > currentMillis){ previousMillis = currentMillis; } в некоторых случаях внесёт ошибку в логику работы программы. В Вашем случае: если светодиод один раз в 49 дней загорится не через 360 сек, а через, например, через 719 сек, то это не нарушит логики "не ранее 360 сек". Но в некоторых случаях нужно точно соблюдать период, и в таких случаях Ваше предложение не применимо. Это первое. Второе -- в данном случае имеем дело с беззнаковыми числами. И, если 'previousMillis' станет больше 'currentMillis', это не нарушит логику программы -- разница этих значений будет такой какой нужно. Поэтому проверять, стало ли 'previousMillis' больше, чем 'currentMillis', не нужно -- это лишнее. Третье -- если нужно считать время более, чем 49 дней, то в стандартной библиотеке avr-libc (во второй версии точно) есть реализация RTC (заголовочный файл time.h). После небольшой правки обработчика прерывания Timer0 AVR-ка будет подсчитывать время хоть до "второго потопа" (хотя надо посмотреть нет ли каких ограничений, например, в виде десятка веков).
Можно и убрать, но в данном коде это приемлемо т.к. никаких других задач, кроме моргания светодиодом нет и на таймер это, по-моему, никак не влияет.
Можно подробнее? По первому и третьему случаю согласен, а вот со вторым - нет. Мне кажется нарушит логику, т.к в случае обнуления previousMillis = 4 294 967 295 а currentMillis = 0 так ведь? если приводим к unsigned long значение выражения (0-4294967295) Условие все равно ложно и срабатывание светодиода, на мой взгляд не произойдет.
вот скетч.который имитирует millis() Код (C++): uint32_t mil = 0xFFFFFFF2; uint32_t old_mil ; uint8_t interval = 10; void setup() { Serial.begin(9600); old_mil = mil; for (int i = 0; i < 40; i++) { if (mil - old_mil > interval) old_mil = mil; Serial.print( mil, HEX); Serial.print(" "); Serial.print(old_mil, HEX); Serial.print(" "); mil++; Serial.print((mil - old_mil > interval)); Serial.println(" "); } } void loop() { } а по поводу Можно и убрать, но в данном коде это приемлемо т.к. никаких других задач, кроме моргания светодиодом нет и на таймер это, по-моему, никак не влияет. влияет и очень сильно на ваши ,прости госпади,мозги. Получается одна нога у вас на лыже, а другая на коньках и в результате херня. delay() нужен для упрощения демонтрационого кода. А millis() освобождения процессора от ненужного стояния и выполнения другой работы.
С точки зрения беззнаковых чисел 0-4294967295 = 1 (или 0x00000000 - 0xFFFFFFFF = 0x00000001). Что соответствует действительности -- после последнего запоминания 'previousMillis' прошла всего 1 мсек. И условие не сработает потому, что должно пройти 360 сек, а не 1 мсек. Условие сработает при 360000 - 4294967295 = 360001.
Я почему-то думаю, что "0" это целое число и unsigned long a = 0-4294967295; a будет равно нулю. Разве не так? Пойду проверю на скетче.... Проверил... unsigned long a = 0-4294967295; результат а = 1. согласен, оч. интересно. Получается, что описанной проблемы с millis() вообще нет?
а вы составте тестовый тест и испытайте. Программирование это практическая работа. Результат всег можно увидеть в железе. А вот альтернатива вашему коду но без delay() Код (C++): /* светодиод -> 13 (Led_pin) 1 горит/ 0 нет GND -> GND */ const int Led_pin = 13 ; // нога светодиода uint8_t Led ; void setup() { pinMode(Led_pin, OUTPUT); digitalWrite(Led_pin, Led = 0); } void loop() { //#1 static uint32_t past_1 = 0 ; static uint32_t past1_1 = 0 ; if (Led&&(millis() - past1_1 >= 1000)) { // если светодиод горит 1 сек digitalWrite(Led_pin, Led = 0);//то выключить } if (millis() - past_1 >= 360000) { // если прошло 360 сек past_1 = millis() ; digitalWrite(Led_pin, Led = 1); // включить светодиод past1_1 = millis() ; } }
Есть, если время "ожидания" больше, чем 49 "с копейками" суток, т.е. больше 4294967295 (или 0xFFFFFFFF). Но я с такой ситуацией не сталкивался.
Вот только разобрали все выше в форуме - получается, что нет проблемы ни какой, или объясните какая есть? На мой взгляд проблема только одна - невозможно задать цикличность события больше 49 с копейками дней.