Stolbov 0 14 октября, 2021 Опубликовано 14 октября, 2021 · Жалоба Всем добрый день! На работе поручили заняться разработкой нового прибора на микроконтроллере 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", то светодиод будет гореть, но если так сделать в двух условиях, то всё равно будет гореть только один светодиод, будто бы задача по какой-то причине зависает. Что это может быть? Заранее всех благодарю за ответы. Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
x893 41 14 октября, 2021 Опубликовано 14 октября, 2021 · Жалоба Кнопки работают с дребезгом. Хотя это и так очевидно. Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
cybersonner 1 14 октября, 2021 Опубликовано 14 октября, 2021 · Жалоба А зачем семафор? По результату все это сложное работает как если бы задача poll_button просто проверяла состояние входов кнопок. Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
rkit 3 14 октября, 2021 Опубликовано 14 октября, 2021 (изменено) · Жалоба Семафоры нужны для передотвращения ситуации типа "А прочитал x, Б прочитал x, А записал измененную x, Б записал измененную x, результат изменения со стороны А потерян". А тут достаточно прямо читать кнопку, или volatile-переменной. Изменено 14 октября, 2021 пользователем rkit Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Stolbov 0 14 октября, 2021 Опубликовано 14 октября, 2021 · Жалоба 46 минут назад, x893 сказал: Кнопки работают с дребезгом. Хотя это и так очевидно. Это да, я сейчас всё на макетной плате собираю. Конденсаторы параллельно своим кнопкам не ставил. 5 минут назад, rkit сказал: Семафоры нужны для передотвращения ситуации типа "А прочитал x, Б прочитал x, А записал измененную x, Б записал измененную x, результат изменения со стороны А потерян". А тут достаточно прямо читать кнопку, или volatile-переменной. Спасибо! Т.е. и от прерываний тоже можно избавиться? Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 60 14 октября, 2021 Опубликовано 14 октября, 2021 · Жалоба 1 hour ago, Stolbov said: Программно я себе это представлял так: Лучше кнопки и клавиатуры обрабатывать циклически в прерывании по таймеру или в цикле задачи. Примерный период опроса 5 - 15 мс. С фильтрацией дребезга. Если Вы кнопку заведёте на прерывание по пину, то кнопка должна быть идеальной, т.е. бездребезговой. И помех на линиях кнопки не должно быть от слова совсем, иначе будут ложные срабатывания. 37 minutes ago, Stolbov said: Т.е. и от прерываний тоже можно избавиться? Необязательно. Но это прерывание должно быть не от GPIO, а от чего-то периодического. Таймера, например. В прерывании читать порт с кнопками, проверять, есть ли изменения. Если эти изменения стабильны в течение некоторого времени (подавление дребезга и помех), то отправлять в очередь (на один элемент, или несколько) состояние кнопки. Это может быть банальный скан-код, или структурка с более сложной информацией: кнопка нажата; кнопка отпущена; кнопка удерживается, более чем X мс и т.п. Эту очередь может читать Ваша задача управления двигателем. Очерь в данном случае это элемент синхнонизации между прерыванием и задачей, или между задачей (в которой опрашиваете циклически клавиатуру) и задачей управления двигателем. Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
razrab83 20 18 октября, 2021 Опубликовано 18 октября, 2021 · Жалоба 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. Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Stolbov 0 20 октября, 2021 Опубликовано 20 октября, 2021 · Жалоба Благодарю всех за ответы! Я понял, что слишком рано взялся за семафоры вообще, так как о данной ОСРВ имею лишь поверхностное представление. Начал читать материалы Курница и скачал с оф.сайта мануал по FreeRTOS в pdf, т.к. пока не пойму функционал ОС, я просто толком ничего не смогу сделать. Спасибо всем, кто потратил на мой вопрос немного времени. Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться