Обработка в суперцикле
Этот способ можно назвать «в лоб». Суперцикл программы микроконтроллера — это цикл while (хотя может быть и другой), который бесконечное число раз выполняет команды в своём теле. Почему плохо детектировать нажатие кнопок или любые другие импульсные сигналы в этом цикле?
Потому что при таком подходе:
- мы рискуем проворонить срабатывание кнопки, в течение времени, когда выполняются другие команды цикла.
- мы заставляем делать микроконтроллер много лишней работы, постоянно, много сотен раз в секунду опрашивая ножки.
Гораздо правильнее делать это с помощью прерываний, о чем мы поговорим позже. А пока, ради знакомства с функцией чтения входных GPIO сигналов сделаем таки «в лоб».
Настройка контактов на вход
Так же, как и в предыдущем уроке, откроем среду STM32CubeIDE и создадим новый проект. Выберем для кнопок два контакта: PB0 и PB1. Жмем на них левую кнопку мыши и выбираем режим GPIO_Input.
Обычно, при подключении кнопок следует соединить задействованный контакт микроконтроллера с землёй через резистор (в уроке для Ардуино мы так и делали). Это называется подтяжкой. Без подтяжки, контакт будет находиться в неопределенном состоянии, что приведет к спонтанному ложному срабатыванию кнопок.
К нашему счастью, все GPIO контакты STM32 можно программно подтянуть как к земле, так и к питанию. Это очень удобно, позволяет сэкономить на внешних резисторах и упрощает схему.
На вкладке настройки GPIO, в поле GPIO Pull up/Pull down установим значение Pull down (подтяжка к земле). А заодно назначим псевдонимы BTN1 и BTN2 этим контактам.
Настройка встроенного индикатора
Чтобы проверить работу кнопок нам понадобится какой-нибудь индикатор. В отличие от предыдущего урока, мы не будем подключать внешний светодиод, а используем встроенный — на контакте PC13.
Правда, надо учесть, что этот встроенный светодиод включен по обратной схеме. Его катод подключен к PC13, а анод к питанию +3.3В (с резистором, конечно). Учтём это позже, при составлении программы. А пока отметим PC13 в конфигураторе как GPIO_Output.
Плюс, как и прежде назначим псевдоним LED.
В конце, не забываем выделить две ножки для программатора. В противном случае, мы испытаем проблемы при очередной загрузке программы в микроконтроллер.
Программа
Для чтения сигнала на контакте используем функцию из библиотеки HAL:
HAL_GPIO_ReadPin(порт, контакт)
Здесь порт — идентификатор порта, контакт — индекс контакта в порту. Поскольку мы создали псевдонимы для ножек, используем в качестве аргументов соответствующие константы: BTN1_GPIO_PORT и BTN1_Pin. В общем, всё как у известной нам функции HAL_GPIO_WritePin.
Если в момент вызова этой функции на контакте будет высокий уровень сигнала, то функция вернет истину (ну или 1). В противном случае — вернет ложь (эквивалентно нулю).
Программа будет в суперцикле постоянно опрашивать то одну, то другую кнопку и в зависимости от наличия сигнала на соответствующих контактах гасить или зажигать светодиод.
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if( HAL_GPIO_ReadPin(BTN1_GPIO_Port, BTN1_Pin) ){
// гасим светодиод
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
}
if( HAL_GPIO_ReadPin(BTN1_GPIO_Port, BTN2_Pin) ){
// зажигаем светодиод
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
}
}
/* USER CODE END 3 */
Почему в этом коде GPIO_PIN_SET — гасит, а GPIO_PIN_RESET — зажигает? Как-будто неправильно, в предыдущем уроке было иначе. А потому, что логика светодиода на ножке PC13 инверсная! Помните?
Загружаем программу на плату. Сразу после запуска светодиод будет гореть, так как изначально на ножках микроконтроллера низкий уровень. Нажимаем первую кнопку — светодиод гаснет. Нажимаем вторую — загорается. Работает!