Как сделать очень долгую функцию delay(), несколько часов

Я пытаюсь сделать открывающуюся и закрывающуюся дверцу, которая должна открываться и закрываться каждые 12 часов. Мне было интересно, могу ли я просто сделать небольшой зацикленный скрипт с задержкой delay() на 12 часов, delay(43 200 000 000); Наверное? Однако я понятия не имею, возможно ли это и/или рекомендуется. Неплохие отзывы/альтернативы (при необходимости) :)

, 👍9

Обсуждение

хорошо, я думаю, что задержка имеет максимум 65535 мкс, но теперь мне нужна альтернатива..., @Fred Pannekoek

Синхронизация в более зрелых системах, состоящих только из MCU, обычно выполняется путем программирования канала аппаратного таймера MCU для периодического срабатывания прерывания, а затем их подсчета, что позволяет процессору тем временем заниматься другими делами и в совокупности является таким же точным, как и часовой хрусталь., @Chris Stratton

Использование задержки увеличит очень маленькую служебную ошибку. Лучше использовать прерывание для определения времени заведомо удачного периода, а затем считать оттуда. Вот доказательство концепции на моем личном сайте: http://blog.linformatronics.nl/213/electronics/timed-1-millisecond-interrupt-routine-for-arduino., @jippie

Если это не обязательно должно быть идеально рассчитано по времени, вы можете использовать нетрадиционные вещи, такие как датчик освещенности, чтобы чувствовать утро и вечер., @The Guy with The Hat

да, я думал об этом, однако у меня есть только небольшая часть детектора света, и я не знаю, как защитить ее от непогоды (маленькая дверца снаружи), @Fred Pannekoek

Я хочу 20 минут низкой и 3 минуты высокой структуры программы. у меня есть плата ардуино уно, @Janaka rathnasiri


6 ответов


Лучший ответ:

10

Метод часов реального времени является наиболее точным, но в остальном используется миллис

unsigned long startMillis = millis();
while (millis() - startMillis < LONG_DELAY_MS);

Это задержит прибл. 4294967295 мс (2^32-1) или 49 дней, после чего таймер догонит значение startMillis

,

Что плохого в простом использовании delay(LONG_DELAY_MS)? [Реализация Arduino](https://github.com/arduino/Arduino/blob/master/hardware/arduino/cores/arduino/wiring.c#L109-119) принимает длинные значения без знака. Я также не совсем уверен, что ваш код работает правильно, когда millis() зацикливается и меньше, чем startMillis, @Gerben

Если я прав, задержка делает ваш Arduino полностью неактивным во время ожидания. Я не знаю, как это будет действовать, когда миллис вернется к 0., @Fred Pannekoek

@Gerben, хорошие вещи, поставь это как ответ!, @geometrikal

Переполнение @FredPannekoek будет работать нормально, если используется *unsigned* long., @geometrikal

Итак, вы хотите, чтобы прерывания происходили каждую мс в течение 12 часов? Почему бы просто не перевести контроллер в спящий режим на 12 часов?, @23ars

@23ars Как ты собираешься будить контроллер ровно через 12 часов? Самым элегантным решением является внешний RTC с будильником на 12 часов., @geometrikal

взгляните на ответ Рикардо. Или, если вам не нужен сторожевой таймер и спящий режим, решением может быть установка таймеров на генерацию прерывания каждую секунду (TIMERX_COMPA) и отсчет секунд, а не миллис. с миллисом вы генерируете прерывание каждую мс, и это не совсем нормально. Даже если это Arduino, я как-то против использования функций из библиотек, типа millis и других., @23ars

@23ars Основная причина успеха Arduino — это простая в использовании библиотека аппаратных абстракций. Если вы против функций из библиотек, вы несколько ограничиваете себя. В любом случае, функция комментариев — улучшить ответ. Если у вас есть лучшее решение, напишите свой ответ. ;), @geometrikal

@Gerben: Почему вы говорите, что реализация Arduino принимает беззнаковые длинные значения? В справочнике Arduino указано, что аргумент для задержки() должен быть беззнаковым целым числом. (Ваша ссылка на «реализацию Arduino» больше не работает)., @PimV

Обновленная ссылка @PimV: https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/wiring.c#L106. [Справочный веб-сайт Arduino](https://www.arduino.cc/reference/en/language/functions/time/delay/) также сообщает, что delay принимает unsigned long. Поэтому я не уверен, о какой ссылке вы говорите, где говорится, что это должно быть беззнаковое целое число., @Gerben

@Гербен: Спасибо. Прошу прощения, вы правы. Не могу найти обратно, наверное носом смотрел., @PimV


1

Есть ли у вас спящий режим (целые числа без знака в секундах)?

В противном случае это позволит вам задерживать() очень долго:

for (unsigned int bigloop=0; bigloop<65535; bigloop++)
{
   for (unsigned int smallloop=0; smallloop<65535; smallloop++)
   {
      for (unsigned int tinyloop=0; tinyloop<65535; tinyloop++)
      {
         delay(65535);
      }
   }
}
,

Я мог бы попробовать это, если мне не удастся получить rtc, как сказал Том. Спасибо за помощь!, @Fred Pannekoek


7
У

delay() есть свое применение, но для длительных задержек это бесполезно. Он просто говорит микроконтроллеру ничего не делать в течение x тактов. В это время ваш Arduino больше ничего не может делать.

Лучше всего использовать часы реального времени (RTC). Эти чипы специально созданы для отслеживания времени, и вы можете легко подключить их к Arduino. Вот пример того, как это можно сделать.

,

+1 — Решение RTC особенно хорошо, если вам нужна большая точность, чем может дать MCU., @Ricardo

@Ricardo - RTC вряд ли будет более точным, чем MCU с тактовым кристаллом, использующим один из своих аппаратных таймеров для запуска периодического прерывания; обычно он дает вам отслеживание потери мощности и, возможно, некоторое знание календарных схем., @Chris Stratton

Afaik Uno не использует кварцевый бит и керамический резонатор для своих часов, поэтому точность гораздо меньшая, чем у RTC., @jfpoilpret

@ChrisStratton - Верно. Дело принято. RTC будет гораздо лучшим вариантом, если ОП придется открывать или закрывать дверь в определенное время суток., @Ricardo


7

Вы можете использовать прерывание сторожевого таймера, чтобы MCU засыпал во время ожидания и экономил энергию.

Но учтите, что вы сэкономите электроэнергию только в том случае, если ваша плата также будет ее экономить. Это означает, что у вас должен быть регулятор низкого напряжения покоя вместо обычных регуляторов, которыми оснащены наиболее распространенные платы Arduino, такие как Uno. В противном случае не имеет значения, экономит ли ваш MCU энергию, если ваша плата этого не делает.

Вот код (непроверенный):

#include <avr/sleep.h>
// Эта переменная делается изменчивой, потому что она изменяется внутри функции прерывания
volatile int sleep_count = 0; // Следите за тем, сколько циклов сна было завершено.
const int interval = 720; // Интервал в минутах между пробуждением и выполнением задач.
const int sleep_total = (interval*60)/8; // Приблизительное количество циклов сна
// необходимо до истечения указанного выше интервала. Не то, чтобы это делало целочисленную математику.

void setup(void) {
    watchdogOn(); // Включаем сторожевой таймер.
    // Отключите АЦП, установив бит ADEN (бит 7) в ноль.
    ADCSRA = ADCSRA & B01111111;
    // Отключите аналоговый компаратор, установив бит ACD (бит 7) в единицу.
    ACSR = B10000000;
    // Отключите буферы цифровых входов на всех аналоговых входных контактах, установив биты 0-5 в единицу.
    DIDR0 = DIDR0 | B00111111;
}

void loop(void) {
    goToSleep(); // ATmega328 засыпает примерно на 8 секунд
    // и продолжает выполнение кода после пробуждения
    if (sleep_count == sleep_total) {
        // КОД, ВЫПОЛНЯЕМЫЙ ПЕРИОДИЧЕСКИ
    }
}

void goToSleep() {
    set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Установить спящий режим.
    sleep_enable(); // Включить спящий режим.
    sleep_mode(); // Входим в спящий режим.
    // После пробуждения от сторожевого прерывания выполнение кода продолжается
    // для выполнения с этой точки.
    sleep_disable(); // Отключаем спящий режим после пробуждения.
}

void watchdogOn() {
    // Очистить флаг сброса, бит WDRF (бит 3) MCUSR.
    MCUSR = MCUSR & B11110111;
    // Установите бит WDCE (бит 4) и бит WDE (бит 3) WDTCSR.
    WDTCSR = WDTCSR | B00011000; 
    // Установите значение предделителя тайм-аута сторожевого таймера на 1024 K
    // что даст интервал времени ожидания около 8,0 с.
    WDTCSR = B00100001;
    // Разрешить прерывание сторожевого таймера.
    WDTCSR = WDTCSR | B01000000;
    MCUSR = MCUSR & B11110111;
}

ISR(WDT_vect) 
{
    sleep_count ++; // отслеживать, сколько циклов сна было завершено.
}

Код, который я скопировал, взят с этой страницы: Низкий - Питание Arduino с помощью сторожевого таймера.

,

1

Это сработает:

longDelayInSeconds = 120; //пара минут;
while (p < longDelayInSeconds) {

        delay(1000);
        p++;

}
,

не лучшее решение, и ОП запросил 12 часов, а не 2 минуты., @Madivad


1

Я просто использую циклы for, когда не хочу делать промежуточные действия:

for (int Hours = 0; Hours < 12; Hours++) {            // Создает 12 часов
  for (int Minutes = 0; Minutes < 60; Minutes++) {    // Создает 1 час
    for (int Seconds = 0; Seconds < 60; Seconds++) {  // Создает 1 минуту
      delay(1000);                                    // Создает 1 секунду
    }
  }
}
,

Ну, для начала было бы проще изменить продолжительность ожидания. Просто измените число для каждого часа, минуты и секунды без необходимости конвертировать в миллисекунды., @GeneCode