ESP32/FreeRTOS, как остановить текущую задачу при запуске новой (избегая перекрытия)

Я работаю над кодом для управления двухмодульным реле доступа к двери. Я ищу способ остановить текущие задачи перед запуском новой (той же задачи). Все, что я хочу, это избежать дублирования.

void TaskOpenManRoom(void *parameter){
  Serial.println("Opening men room");
  digitalWrite(manRelay, LOW);
  vTaskDelay(6000 / portTICK_PERIOD_MS);
  digitalWrite(manRelay, HIGH);
  Serial.println("Closing men room");
  vTaskDelete(NULL);
};

xTaskCreate(
      TaskOpenManRoom,
      "TaskOpenManRoom",
      1000,
      (void *) &man,
      1,
      &TaskMen
    );

Моя цель - увеличить время, когда дверь должна быть открыта. Таким образом, в основном, когда первая задача была запущена, а затем через некоторое время вторая, дверь должна оставаться открытой еще 6000 мс.

В текущем коде, когда вызывается вторая задача, как в середине первой, дверь закрывается из-за вызова первой задачи digitalWrite(manRelay, HIGH);

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

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
69
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Много способов:

  1. используйте vTaskDelay, который переводит задачу в состояние «не выполняется» (она не блокирует
  2. Дождитесь семафора мьютекса, очереди или уведомления о задаче от другой задачи.

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

Это убьет текущую задачу:

vTaskDelete(NULL);

Я бы не стал убивать первую задачу при запуске второй.

Если вы вообще используете задачу, я бы переписал задачу во что-то вроде этой общей линии:

cast parameter to pointer to uint32
atomic increment open count, and if it was zero {
    open the door
    repeat {
        sleep six seconds
    } atomic decrement count, and exit loop if it was 1
    close the door
}
exit the task

... и когда вы создаете задачу, передайте указатель на uint32_t, чтобы он использовал для хранения количества открытых.

Таким образом, задача начинается с атомарного увеличения числа открытых, которое возвращает значение, которое ранее было в счете открытых. Если это было равно нулю, это означает, что дверь в настоящее время закрыта. В таком случае мы открываем его и ложимся спать.

Если задача снова запустится, пока она находится в спящем режиме, количество открытых операций теперь будет равно единице. Мы немедленно увеличиваем это значение, но когда мы проверяем предыдущее значение, оно было равно 1, поэтому мы не пытаемся снова открыть дверь — мы просто пропускаем все, что есть в операторе if, и выходим из задачи.

Когда первый экземпляр задачи просыпается, он уменьшает счетчик, и если он равен 1, он выходит из цикла, закрывает дверь и выходит из задачи. Но если задача снова запустится во время сна, счетчик все равно будет больше 1, поэтому она останется в цикле и еще немного спит.

Это открыто для небольшой оптимизации. В настоящее время он бездействует фиксированный период времени (шесть секунд), даже если текущий счетчик открытых данных больше 1. Если задача достаточно дорогая, чтобы оправдать небольшую дополнительную работу, мы могли бы выполнить атомарный обмен, чтобы получить текущий открытый счетчик и установите открытый счетчик на 0, умножьте полученное значение на 6000, затем спите в течение этого времени. Однако это добавляет немного дополнительной сложности, и в этом случае польза будет слишком мала, чтобы ее оправдать.

Это действительно зависит от того, не запустим ли мы задачу более 4 миллиардов раз, пока дверь открыта. Если бы мы это сделали, наше атомарное приращение переполнилось бы, и код работал бы неправильно. Для рассматриваемого случая (и большинства других) это вряд ли будет проблемой. В редких ситуациях, когда это может быть, очевидным решением является 64-битная переменная (и 64-битное атомарное увеличение и уменьшение). Увеличение переменной до переполнения 64-битной переменной, как правило, нереально (например, если вы увеличиваете на 1 ГГц, это займет столетия).

Ответ принят как подходящий

Задачи предназначены для длительного выполнения, потому что они относительно тяжеловесны. Не запускайте и не завершайте задачи для каждого действия пользователя и не откладывайте их на длительные периоды времени.

Вам вообще не нужна задача для вашей функциональности, вам просто нужен таймер для выполнения действия закрытия через 6000 мс. Затем вы можете сбросить его, когда вам нужно.

TimerHandle_t closeManRoomTimer;

void OpenManRoom() {
  xTimerReset(closeManRoomTimer, 100);    // <------ (re)arm the close timer
  Serial.println("Opening men room");
  digitalWrite(manRelay, LOW);
};

void CloseManRoom(TimerHandle_t) {
  Serial.println("Closing men room");
  digitalWrite(manRelay, HIGH);
};

// during program startup, setup a one-shot close timer
closeManRoomTimer = xTimerCreate("closeManRoomTimer", pdMS_TO_TICKS(6000), pdFALSE, 0, &CloseManRoom);

Это то, что я искал. Я новичок :P Большое спасибо, босс! :)

luj12 15.01.2023 13:39

Другие вопросы по теме