Как использовать millis() в коде вместо delay()?

У меня возникли проблемы при попытке преобразовать мой код в неблокирующий. Мне удалось сделать это в некоторых частях, но в остальном все по-другому.

Когда я пытаюсь заменить задержку на оператор if, например, вращение и отключение датчиков, он не работает в нужное время, потому что переменная, которую я вычитаю из cur_ms, не обновляется.

То, что я пытаюсь сделать, это всегда переключать_dir() и update_code(), и когда second_btn.released() Я хочу, чтобы две другие функции продолжали работать, но также выполняли последовательность действий (с задержкой) без блокировки.

Я был бы рад получить вашу помощь.

Заранее спасибо :)

void loop()
{
    uint32_t cur_ms = millis();

    if (cur_ms - spin_ms >= SPINNING_MS) {
        switch_dir();
        spin_ms = cur_ms;
    }

    if (cur_ms - read_ms >= SENSORS_DEBOUNCE_MS) {
        update_code(get_sensors_state());
        read_ms = cur_ms;
    }

    if (second_btn.released()) {
        ball_servo.write(BALL_SERVO_MAX);   // отбросить шарик
        digitalWrite(BLOWER_PIN, LOW);  // вентилятор включен
        delay(BALL_DOOR_MS);
        ball_servo.write(BALL_SERVO_MIN);   // закрыть шаровую дверцу
        delay(BLOWER_MS);
        digitalWrite(BLOWER_PIN, HIGH); // вентилятор выключен
    }
}

, 👍2

Обсуждение

if (second_btn.released()) { должен запускать только первые две команды, сохранять ball_door_ms и устанавливать переменную состояния, например state = 2........ новый оператор if if (state == 2 и cur_ms - ball_door_ms >= BALL_DOOR_MS) { закрыл бы шаровую дверцу, сохранил бы blower-ms и установил state = 3 .... и так далее, @jsotola


3 ответа


0

Когда second_btn.released имеет значение true, его два оператора delay() заблокируют весь цикл. Вы можете избежать этого, переписав так, чтобы операторы delay() обрабатывались так, как вы уже обрабатываете spin_ms и cur_ms, что будет включать некоторую логику типа конечного автомата, так что (например) вы устанавливаете состояние при вводе задержки BALL_DOOR_MS, затем возвращаетесь к основному циклу, который будет иметь ветвь, которая принимается только в том случае, если состояние BALL_DOOR_MS активно. Когда это состояние заканчивается, оно устанавливает новое состояние, которое позволяет отдельной ветви основного цикла выполнять BALL_SERVO_MIN и запускать состояние BLOWER_MS и так далее. Цепочка переключателей / корпусов может быть лучшим способом справиться с этим, заменив существующий second_btn.released.

,

Эта ссылка может быть полезной: [Конечный автомат] (https://majenko.co.uk/blog/finite-state-machine )., @Edgar Bonet

Корпус переключателя заблокирует остальную часть цикла, не так ли? Не могли бы вы написать пример кода, чтобы продемонстрировать свою идею?, @KD Technology

Ссылка была очень полезной. Это работает. Большое вам спасибо :), @KD Technology


1

У вас была правильная идея с вашими первыми несколькими утверждениями if! Просто продолжайте проверять, прошло ли время, не откладывайте ()

uint32_t releasedTime = 0;
if (second_btn.released() && releasedTime == 0) {
    releasedTime = cur_ms; // помните, что кнопка была нажата сейчас
    ball_servo.write(BALL_SERVO_MAX);   // отбросьте шарик
    digitalWrite(BLOWER_PIN, LOW);  // вентилятор включен
} else if ((cur_ms - releasedTime > BALL_DOOR_MS) && (cur_ms - releasedTime < BALL_DOOR_MS + BLOWER_MS)) {
    ball_servo.write(BALL_SERVO_MIN);   // закрыть шаровую дверцу
} else {
    // должно быть после BALL_DOOR_MS + BLOWER_MS, время для запуска
    digitalWrite(BLOWER_PIN, HIGH); // вентилятор выключен
    releasedTime = 0; // так что это не продолжает работать
}
,

Re “releasedTime = 0; // так что это не продолжает работать”: это не будет работать, как обещано в комментарии. Строка выше (digitalWrite(BLOWER_PIN, HIGH);) будет выполняться снова и снова в режиме ожидания. Вам нужно было бы указать if (releasedTime != 0), чтобы избежать этого, но обратите внимание, что 0 - это совершенно допустимая временная метка, периодически возвращаемая millis(). Кроме того, строка ball_servo.write (BALL_SERVO_MIN); будет выполняться повторно в течение мс BLOWER_MS. Может быть, это и не имеет большого значения, но это замедляет выполнение других задач в цикле (). Обе проблемы были бы решены с помощью соответствующей переменной “состояния”., @Edgar Bonet


0

<Чтобы спроектировать в функции millis(), вы должны думать об этом как об использовании секундомера. Вы записываете время, в которое вы начали. Допустим, вы начали с 10000 мс. Затем время от времени вы проверяете текущее время. Допустим, текущее время составляет 10500 мс. Затем вы берете текущее время и вычитаете его из вашего времени начала, чтобы получить время, прошедшее с момента начала. Это дает вам 500 мс. Допустим, желаемое время задержки составляет 1000 мс. Поскольку прошедшее время не составляет 1000 мс, вы продолжаете выполнять другие действия до тех пор, пока текущее время запуска не станет больше или равно вашему времени задержки. Затем вы выполняете любую задачу, которую вам нужно выполнить, а затем записываете новое время начала и повторяете процесс заново. Это выглядело бы примерно так.>

#define DELAY_TIME     1000   // количество времени, которое вы хотите отложить `


unsigned long startTime = 0;
unsigned long currentTime = 0;

void setup()
{
   startTime = millis();  // Время начала записи
   currentTime = startTime; // Установите текущее время на время начала, чтобы прошедшее время было = 0
}

void loop()

{
  
currentTime = millis(); // обновить текущее значение времени
  
if((currentTime - startTime) >= DELAY_TIME)) // проверить количество прошедшего времени  
  {  
    // Количество времени задержки истекло
    // Делайте все, что вам нужно сделать здесь, что требует задержки

       startTime = millis(); // Сбросьте время начала, чтобы оно ожидало еще 1000 мс
       currentTime = startTime; // Установите текущее время = на время начала, чтобы избежать негативных последствий
    }
  // Затем поместите сюда остальную часть кода, который вы хотите запускать непрерывно 

}
,

То, что вы здесь советуете, - это именно то, что ОП уже делает. Он знает, как это сделать. Его проблема заключается в объединении этой техники с конечным автоматом для управления шаровой дверцей и воздуходувкой., @Edgar Bonet