Обработка нажатия кнопки

Мы уже познакомились с понятием прерывания (и даже использовали таблицу векторов прерываний) для реализации задержки с SysTick. Теперь же нам предстоит работать с кнопкой, и если с точки зрения программной реализации нам всё понятно — действия аналогичны тому, как мы работали со светодиодом (только в этом случае мы не пишем в ODR, а читаем из IDR — если вы делаете это в основном цикле), — то с прерываниями не всё так просто. Как отмечалось раньше, они бывают двух видов: внутренние и внешние по отношению к ядру. Так как SysTick — составная часть ядра, то и прерывание от него реализовано по-другому (вы включаете его непосредственно из регистров SysTick). В случае с GPIO (или любой другой периферией) это происходит через модуль внешних прерываний EXTI (англ. external interrupt). Но перед тем, как мы начнем разбираться с ним, давайте настроим ножку, используя библиотеку StdPeriph.

Исходя из принципиальной схемы устройства, ножка, к которой подсоединена кнопка — PA5. Добавим функцию инициализации в файл button.c

Настройка завершена. Наша цель — по нажатию кнопки менять состояние светодиода (выключать, если он был включен, и наоборот), который мы уже настраивали ранее. Теперь осталось написать код, который будет реагировать на нажатие. Конечно, мы можем проверять регистр IDR в главном цикле, но это не самое лучшее решение.

Замечание

Посмотрите внимательно на принципиальную схему устройства, в частности, на то, как подключена кнопка. В случае подключения её без «обвеса» (RC-фильтра) будет происходить так называемый «дребезг» механического контакта.

encoder bounce

Это приведет к непредсказуемому результату, т. е. получится так, что кнопка нажимается несколько раз, и в каком состоянии окажется светодиод – неизвестно. Чтобы «отфильтровать» шум, можно поставить задержку на 30-40 мс (принято считать, что дребезг составляет именно столько времени). Далее мы еще раз проверим, нажата ли кнопка: если да, то, очевидно, это был не «шум», и нам действительно нужно переключить состояние светодиода. Для того чтобы избежать такого же дребезга, но уже на этапе отпускания кнопки, необходимо добавить цикл while, аргументом которого будет состояние регистра нужной ножки. Вот часть программы:

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

encoder rc

Гораздо лучше использовать для таких целей прерывания. Обратимся к документации (это делается для более глубокого понимания — эту информацию можно получить и из наименования макросов в библиотеке), к разделу «8 Interrupts and events» ⇒ «External interrupt/event controller (EXTI)». На странице 133 можно найти функциональную схему работы этого модуля.

exti diagram

Изображение из Reference Manual, Figure 20. External interrupt/event controller block diagram

Блок «Edge detect circuit» может реагировать как на передний фронт (импульса), так и на задний или на оба сразу. Это задается соответствующими битами в соответствующих регистрах. Всё в той же документации можно найти схему подключения ножек к модулю EXTI:

exti mux

Изображение из Reference Manual, Figure 21. External interrupt/event GPIO mapping

Как видно, 3-я ножка порта A подключена к EXTI5. Чтобы глобально разрешить прерывания с EXTI5, необходимо произвести работу с регистром ISER(контроллера NVIC). К счастью, в файле core_cm3.h уже имеется готовая функция:

IRQn_Type объявлен в файле stm32f10x.h, следовательно:

Перейдем к регистрам.

exti imr

Регистр IMR из Reference Manual

Регистр IMR отвечает за включение/отключение прерывания. Чтобы разрешить прерывание EXTI5, нужно записать «1» в MR3:

Последующие два регистра отвечают за прерывание по переднему фронту и заднему соответственно. Мы будем реагировать на передний, поэтому следует настроить RTSR:

exti rst

Регистр RTSR из Reference Manual

Так как мы работаем с 5-й ножкой, нас интересует бит TR5:

Допишем код.

Настройка прерывания закончена, осталось написать обработчик, название которого мы также возьмем в startup_<mcu>.s файле.

exti pr

Регистр PR из Reference Manual

Последний значимый для нас регистр именуется PR (англ. pending register). Дело в том, что прерывание может прийти от разных портов (что было видно на схеме выше), поэтому вам самостоятельно нужно проверять, с какого именно порта оно пришло, и сбрасывать «флаг», который записывается в соответствующий бит регистра PR.

Таким образом, код для обработчика примет следующий вид:

В стандартную библиотеку входят два файла для работы с модулем EXTI. Опять же, для повышения уровня абстракции мы предлагаем вам переписать работу с EXTI на функции стандартной библиотеки. В дальнейшем мы не будем рассматривать периферию так же подробно (на уровне регистров). Нашей целью было показать принцип — мы этой цели достигли.

Код урока можно найти на github: CMSIS.


Назад | Оглавление | Дальше