Jump to content

    
Sign in to follow this  
Stolbov

FreeRTOS - передача семафора в функцию.

Recommended Posts

Всем добрый день!

На работе поручили заняться разработкой нового прибора на микроконтроллере STM32 (исходя из технической задачи мой выбор пал на STM32F407VE) с обязательным условием - использование ОСРВ FreeRTOS.
С микроконтроллерами знаком, разрабатывал на их основе пару приборов, но с данной ОСР не работал никогда, и поэтому хочу попросить здесь совета. Работаю в STM32CubeIDE.

Одна из возможностей моего девайся - это ручное управление коллекторным двигателем. В проекте я настроил две ножки на внешние прерывания GPIO_EXTI_3 и GPIO_EXTI_4. Включена подтяжка по питанию
и срабатывание прерывания по любому изменению фронта сигнала (прибор должен реагировать на нажатие/отпускание кнопок. Пока одна из кнопок нажата - крутим вверх или вниз, а если отпустили, то остановка).
Схемотехнически нажатие кнопки коммутирует вход МК с землёй. Программно я себе это представлял так: 

Срабатывание прерывания на одном из входов -> В перывании определяем, какой именно фронт пришёл -> Если фронт низкий, то крутим двигатель || Если фронт высокий, то останавливаемся. 

Вставлять в прерывание функцию вращения двигателем я, разумеется не стал. Вместо этого я решил в зависимости от статуса кнопки пересылать в функцию управления двигателем семафор. Условно говоря, код выглядит так:

void EXTI3_IRQHandler(void) //Кнопка вращения двигателя вверх
{
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3);

  if(HAL_GPIO_ReadPin(GPIOD,GPIO_PIN_3) == GPIO_PIN_SET) //Если на входе высокий уровень, то пока ничего не делаем
  {
	asm("nop");
  }
  else //Если уровень изменился, то
  {
	  xSemaphoreGiveFromISR(sem_curr_upHandle,NULL); //Даём семафор из прерывания
  }
}

//

void EXTI4_IRQHandler(void) //Кнопка вращения двигателя вниз
{
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);

  if(HAL_GPIO_ReadPin(GPIOD,GPIO_PIN_4) == GPIO_PIN_SET) //Если высокий уровень, то ничего не делаем
  {
	asm("nop");
  }
  else //В противном случае выдаём семафор
  {
	  xSemaphoreGiveFromISR(sem_curr_downHandle,NULL);
  }
}

Задача, в которой я собираюсь вращать двигателем, выглядит так:

void poll_button(void const * argument)
{
  for(;;)
  {
	  if(xSemaphoreTake(sem_curr_upHandle,portMAX_DELAY) == pdPASS) //Если семафор на вращение вверх принят функцией и равен одному (время ожидания семафора максимальное)
	  {
		  HAL_GPIO_WritePin(GPIOA,GPIO_PIN_9,GPIO_PIN_SET);
	  }
	  else if(xSemaphoreTake(sem_curr_downHandle,portMAX_DELAY) == pdPASS) //Если семафор на вращение вниз принят функцией и равен одному (время ожидания семафора максимальное)
	  {
		  HAL_GPIO_WritePin(GPIOA,GPIO_PIN_10,GPIO_PIN_SET);
	  }
  }
}

В качестве отлаживающего механизма использую светодиоды (их и пытаюсь зажечь на отладочной плате в зависимости от полученного семафора), но после прошивки платы ничего не происходит.
Я понимаю, что код ещё не дописан, так как после отжатия кнопки мне надо вернуть семафор обратно (правильно ли - "вернуть"?), но по идее данная конструкция уже должна его "выдать", а 
задача - "принять", и в соответствии с этим что-то сделать. Поэтому мои вопросы заключаются в следующем:

1. Правильна ли моя идея с использованием семафоров в принципе для данной ситуации? В любом случае, эту ОСРВ мне надо изучать по работе;
2. После отдачи семафора по нажатию кнопки - как его вернуть обратно после её отжатия?
3. Задача poll_button не реагирует на полученный семафор. Если в одном из условий изменить "== pdPass" на "!= pdPass", то светодиод будет гореть, но если так сделать в двух условиях, то
всё равно будет гореть только один светодиод, будто бы задача по какой-то причине зависает. Что это может быть?

Заранее всех благодарю за ответы.

Share this post


Link to post
Share on other sites

Кнопки работают с дребезгом. Хотя это и так очевидно.

Share this post


Link to post
Share on other sites

А зачем семафор? По результату все это сложное работает как если бы задача poll_button просто проверяла состояние входов кнопок.

Share this post


Link to post
Share on other sites

Семафоры нужны для передотвращения ситуации типа "А прочитал x, Б прочитал x, А записал измененную x, Б записал измененную  x, результат изменения со стороны А потерян".

А тут достаточно прямо читать кнопку, или volatile-переменной.

Edited by rkit

Share this post


Link to post
Share on other sites
46 минут назад, x893 сказал:

Кнопки работают с дребезгом. Хотя это и так очевидно.

Это да, я сейчас всё на макетной плате собираю. Конденсаторы параллельно своим кнопкам не ставил.

5 минут назад, rkit сказал:

Семафоры нужны для передотвращения ситуации типа "А прочитал x, Б прочитал x, А записал измененную x, Б записал измененную  x, результат изменения со стороны А потерян".

А тут достаточно прямо читать кнопку, или volatile-переменной.

 

Спасибо! Т.е. и от прерываний тоже можно избавиться?

Share this post


Link to post
Share on other sites
1 hour ago, Stolbov said:

Программно я себе это представлял так: 

Лучше кнопки и клавиатуры обрабатывать циклически в прерывании по таймеру или в цикле задачи. Примерный период опроса 5 - 15 мс. С фильтрацией дребезга. Если Вы кнопку заведёте на прерывание по пину, то кнопка должна быть идеальной, т.е. бездребезговой. И помех на линиях кнопки не должно быть от слова совсем, иначе будут ложные срабатывания.

37 minutes ago, Stolbov said:

Т.е. и от прерываний тоже можно избавиться?

Необязательно. Но это прерывание должно быть не от GPIO, а от чего-то периодического. Таймера, например. В прерывании читать порт с кнопками, проверять, есть ли изменения. Если эти изменения стабильны в течение некоторого времени (подавление дребезга и помех), то отправлять в очередь (на один элемент, или несколько) состояние кнопки. Это может быть банальный скан-код, или структурка с более сложной информацией: кнопка нажата; кнопка отпущена; кнопка удерживается, более чем X мс и т.п.  Эту очередь может читать Ваша задача управления двигателем.

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

Share this post


Link to post
Share on other sites
14.10.2021 в 16:02, Stolbov сказал:

1. Правильна ли моя идея с использованием семафоров в принципе для данной ситуации?

1) не правильная. используйте события.

2) сп п.1, вопрос 2 решается в событиях.

ps

3) вы заходите в первый if, вызываете xSemaphoreTake(). Этот метод никогда (с аргументом portMAX_DELAY)  вам не вернет что-то отличное от pdPASS.

xSemaphoreTake() блокирует выполнение задачи до получения семафора. Из прерывания отпускается семафор UP, задача разблокируется и вы зажигаете светодиод на ПИН_9. Что будет дальше? Что вы ожидаете от своего кода? Дальше заного начинает выполняться код в главном цикле, т.е. for(;;). Вы опять попадаете в ПЕРВЫЙ if. Опять блокируетесь бесконечно долго на на xSemaphoreTake() в ожидании нажатя кнопки "вверх". При нажатии "вверх" вы опять зажигаете светодиод на ПИН_9. Как вы хотели попасть во второй if? Ваш код ни когда не  попадет во второй if.

 

 

Share this post


Link to post
Share on other sites

Благодарю всех за ответы!

Я понял, что слишком рано взялся за семафоры вообще, так как о данной ОСРВ имею лишь поверхностное представление. Начал читать материалы Курница и скачал с оф.сайта мануал по FreeRTOS в pdf, т.к. пока не пойму функционал ОС, я просто толком ничего не смогу сделать. Спасибо всем, кто потратил на мой вопрос немного времени.

Share this post


Link to post
Share on other sites
Guest
This topic is now closed to further replies.
Sign in to follow this