No Image

Stm32 прерывания по кнопке

759 просмотров
12 декабря 2019

Урок 74

Вот наконец-то и настало время нам попробовать поработать с внешними прерываниям. Данный урок, во-первых, был очень востребован, хотя он и кажется на первый взгляд несложным. Очень много было просьб и я не мог не откликнуться. Во-вторых, мы сейчас работаем с модулем LAN ENC28J60, у которго имеется выход, по которому мы можем получить прерывания по окончании определённых операций, которые я и хотел обработать. Также, если у меня получится, я хотел этим поделиться и с вами, но без первоначального представления о внешних прерываниях в контроллере STM32, это понять, я считаю, будет, мягко говоря, нелегко. Вот поэтому и созрел данный урок.

В качестве микроконтроллера мы возьмём тот же самый STM32F103RCT6, расположенный на недорогой плате, с которой мы и занимаемся в процессе программирования модуля LAN, а в качестве программатора также недорогой маленький ST-Link V2.

Кратко о внешних прерываниях. Внешние прерывания — это такие прерывания, которые обрабатываются вследствие возникновения некоторых событий на определённой ножке порта микроконтроллера. Таких событий может быть несколько не смотря на всего 2 логических состояния. Разнообразие данных событий легко увидеть, раскрыв их список в Cube MX в настройке ножек портов

Можно разделить данные типы на 2 группы пополам. первая группа — External Interrupt — это обработка внешних прерываний. А второй — обработка событий. Разница здесь лишь в том, что в первом случае вызывается обработчик прерываний, а во втором только поднимается соответствующий флаг. Каждая группа уже делится на три вида событий:

1. Обнаружен восходящий фронт (изменение уровня 0 на 1),

2. Обнаружен нисходящий фронт (изменение уровня 1 на 0),

3. Обнаружен любой из вышеперечисленных фронтов.

Также ещё есть программные типы прерываний, но нам с вами они точно не понадобятся.

Вот и всё по типам прерываний.

Существует несколько регистров для обработки внешних прерываний:

EXTI_IMR: Регистр масок прерываний,

EXTI_EMR: Регистр масок событий,

EXTI_RTSR: Регистр срабатывания по восходящему фронту,

EXTI_FTSR:Регистр срабатывания по нисходящему фронту,

EXTI_SWIER: Регистр софтверного запуска прерывания,

EXTI_PR: регистр флагов событий, по которым происходят вызовы прерываний.

Подробно все эти регистры расписаны в технической документации Reference Manual ко всем контроллерам STM. Мы их подробно рассматривать не будем, оставим это на совести библиотеки HAL.

Вот логическая схема обработчика прерываний в контроллере

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

Также можно посмотреть схему организации линии внешних прерываний

Здесь мы видим 16 линий, подключенных через мультиплексоры к одноимённым пинам всех портов. То есть одновременно мы можем обработать 16 ножек контроллера, но, как видно из мультиплексивной организации, что все они должны быть с разными номерами. То есть мы можем обработать одновременно ножки PA1 и PC2, но не можем обработать PA1 и PC1.

Также существуют ещё 4 линии, которые подключены не к портам общего назначения, а к определённым видам периферии

The four other EXTI lines are connected as follows:

• EXTI line 16 is connected to the PVD output

• EXTI line 17 is connected to the RTC Alarm event

• EXTI line 18 is connected to the USB Wakeup event

• EXTI line 19 is connected to the Ethernet Wakeup event (available only in connectivity line devices)

То есть мы можем ещё обработать внешние события от программируемого детектора напряжений, от будильника RTC, от "пробуждений" USB и Ethernet.

Вот сколько всего по прерываниям, и это ещё не всё. На данную тему в технической документации написано целых 18 страниц, так что кому интересно — обязательно почитайте.

Наша задача — грамотно эти прерывания обработать в нашем коде, используя функции библиотеки HAL.

Создадим в генераторе проектов Cube MX новый проект, выбрав наш контроллер

Включим кварцевый резонатор

Выберем программатор по интерфейсу SWD

Задействуем ножку, отвечающую за светодиод

Включим внешние прерывания на ножке PA1

Настроим делители и умножители на максимальную производительность в разделе "Clock Configuration" (нажмите на картинку для увеличения изображения)

Перейдём в "Configuration" и в разделе GPIO настроим ножку PC13 на среднюю скорость

Для ножки PA1 применим подтягивающий регистр к общей шине, чтобы она не болталась в воздухе, так как прерывания мы будем ловить от кнопки, а сама кнопка никуда у нас не притянута, иначе состояние будет неопределённое. Тип внешнего прерывания оставим по умолчанию — восходящий фронт

Применим настройки портов и перейдём в настройки глобальных прерываний по кнопке "NVIC", включим там прерывания EXTI1

Также применим данные настройки.

Также в силу того, что у меня тормозит видеозапись при работе с Keil, я решил воспользоваться средой разработки System Workbench, с которой мы уже неплохо знакомы. Вы можете также работать с Keil или с любой другой средой разработки, разницы нет.

Настроим проект, присвоив имя EXTI01 и выбрав среду разработки SW4STM32

Сгенерируем проект, откроем его в System Workbench, как всегда и удалим в настройках отладчика файл отладки.

Соберём проект, попытаемся его запустить, но скорей всего у нас ничего не получится и мы увидим в консоли вот такое сообщение, что прошить контроллер не удалось из-за того, что его не удалось перезагрузить

Читайте также:  Гидроцилиндр своими руками для пресса

Конечно, можно вовремя держать и отпускать на плате кнопку RESEТ. Но мы пойдём другим путём. Я почитал некоторое количество форумов и ничего там не нашёл, за исключением правки конфигурационных файлов. Откроем наш файл отладки в свойствах проекта

В открывшемся диалоге зайдём в закладку "Debugger" и нажмём там кнопку "Show generator options"

В открывшемся внизу поле "Reset mode" выберем пункт "Software System reset"

Сохраним настройки, теперь должно всё прошиться.

Откроем main.c и отключим наш светодиод в функции main(), так как мы знаем, что он подключен инверсно (можно конечно это сделать и в Cube MX, но так нагляднее)

/* USER CODE BEGIN 2 */

HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);

/* USER CODE END 2 */

Далее в main.c добавим обработчик внешних прерываний

/* USER CODE BEGIN 4 */

void HAL_GPIO_EXTI_Callback( uint16_t GPIO_Pin)

/* USER CODE END 4 */

Затем мы здесь отследим прерывание именно от 1 линии и зажжем светодиод

void HAL_GPIO_EXTI_Callback( uint16_t GPIO_Pin)

if (GPIO_Pin== GPIO_PIN_1) <

HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET );

__NOP();

/* USER CODE END 4 */

Подключим кнопку между PA1 и проводом питания (нажмите на картинку для увеличения изображения)

Соберём проект, прошьём контроллер. Затем нажмём на кнопку — светодиод должен будет засветиться (нажмите на картинку для увеличения изображения)

Давайте разнообразим немного наш проект, чтобы он не был таким скучным. Для этого зайдём в Cube MX, и, так как у нас не Keil, проект закрывать не надо, и включим внешние прерывания ещё и на ножке PA2

Причем включим мы уже другой тип — срабатывание прерывания по низходящему фронту, а резистор мы уже наоборот подтянем к источнику напряжения. Для этого внесём следующие настройки в "Configuration" в GPIO для ножки PA2

Применим настройки, сгенерируем проект, вернёмся в System Workbench и сделаем проекту Refresh.

Допишем следующим образом наш обработчик, чтобы по прерыванию на ножке PA1 наш светодиод зажигался, а в случае PA2 — потухал

void HAL_GPIO_EXTI_Callback( uint16_t GPIO_Pin)

if (GPIO_Pin== GPIO_PIN_1) <

HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET );

> else if (GPIO_Pin== GPIO_PIN_2) <

HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET );

Подключем ещё одну кнопку, но уже к PA2, причём вторым выводом уже к общему проводу (нажмите на картинку для увеличения изображения)

Теперь, если мы соберём код и прошьём контроллер, то первая кнопка будет у нас светодиод зажигать, а вторая тушить.

Казалось бы, что тут такого? А то, что происходит это уже независимо от хода самой программы, не в бесконечном цикле, когда во время там написанного обработчика по нажатию кнопки мы можем вообще не находиться и будет уже не ясно, обработается ли наше событие. Поэтому, изучив внешние прерывания, мы сделали большой шаг вперёд к независимости обработки тех или иных событий на ножках портов микроконтроллера. Конечно, учитывая наш уже теперь очень богатый опыт, нам было это проделать не очень тяжело.

Всем спасибо за внимание!

Отладочную плату можно приобрести здесь STM32F103C8T6

Смотреть ВИДЕОУРОК (нажмите на картинку)

Урок 74

Вот наконец-то и настало время нам попробовать поработать с внешними прерываниям. Данный урок, во-первых, был очень востребован, хотя он и кажется на первый взгляд несложным. Очень много было просьб и я не мог не откликнуться. Во-вторых, мы сейчас работаем с модулем LAN ENC28J60, у которго имеется выход, по которому мы можем получить прерывания по окончании определённых операций, которые я и хотел обработать. Также, если у меня получится, я хотел этим поделиться и с вами, но без первоначального представления о внешних прерываниях в контроллере STM32, это понять, я считаю, будет, мягко говоря, нелегко. Вот поэтому и созрел данный урок.

В качестве микроконтроллера мы возьмём тот же самый STM32F103RCT6, расположенный на недорогой плате, с которой мы и занимаемся в процессе программирования модуля LAN, а в качестве программатора также недорогой маленький ST-Link V2.

Кратко о внешних прерываниях. Внешние прерывания — это такие прерывания, которые обрабатываются вследствие возникновения некоторых событий на определённой ножке порта микроконтроллера. Таких событий может быть несколько не смотря на всего 2 логических состояния. Разнообразие данных событий легко увидеть, раскрыв их список в Cube MX в настройке ножек портов

Можно разделить данные типы на 2 группы пополам. первая группа — External Interrupt — это обработка внешних прерываний. А второй — обработка событий. Разница здесь лишь в том, что в первом случае вызывается обработчик прерываний, а во втором только поднимается соответствующий флаг. Каждая группа уже делится на три вида событий:

1. Обнаружен восходящий фронт (изменение уровня 0 на 1),

2. Обнаружен нисходящий фронт (изменение уровня 1 на 0),

3. Обнаружен любой из вышеперечисленных фронтов.

Также ещё есть программные типы прерываний, но нам с вами они точно не понадобятся.

Вот и всё по типам прерываний.

Существует несколько регистров для обработки внешних прерываний:

EXTI_IMR: Регистр масок прерываний,

EXTI_EMR: Регистр масок событий,

EXTI_RTSR: Регистр срабатывания по восходящему фронту,

EXTI_FTSR:Регистр срабатывания по нисходящему фронту,

EXTI_SWIER: Регистр софтверного запуска прерывания,

EXTI_PR: регистр флагов событий, по которым происходят вызовы прерываний.

Читайте также:  Брус завод кострома отзывы негативные

Подробно все эти регистры расписаны в технической документации Reference Manual ко всем контроллерам STM. Мы их подробно рассматривать не будем, оставим это на совести библиотеки HAL.

Вот логическая схема обработчика прерываний в контроллере

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

Также можно посмотреть схему организации линии внешних прерываний

Здесь мы видим 16 линий, подключенных через мультиплексоры к одноимённым пинам всех портов. То есть одновременно мы можем обработать 16 ножек контроллера, но, как видно из мультиплексивной организации, что все они должны быть с разными номерами. То есть мы можем обработать одновременно ножки PA1 и PC2, но не можем обработать PA1 и PC1.

Также существуют ещё 4 линии, которые подключены не к портам общего назначения, а к определённым видам периферии

The four other EXTI lines are connected as follows:

• EXTI line 16 is connected to the PVD output

• EXTI line 17 is connected to the RTC Alarm event

• EXTI line 18 is connected to the USB Wakeup event

• EXTI line 19 is connected to the Ethernet Wakeup event (available only in connectivity line devices)

То есть мы можем ещё обработать внешние события от программируемого детектора напряжений, от будильника RTC, от "пробуждений" USB и Ethernet.

Вот сколько всего по прерываниям, и это ещё не всё. На данную тему в технической документации написано целых 18 страниц, так что кому интересно — обязательно почитайте.

Наша задача — грамотно эти прерывания обработать в нашем коде, используя функции библиотеки HAL.

Создадим в генераторе проектов Cube MX новый проект, выбрав наш контроллер

Включим кварцевый резонатор

Выберем программатор по интерфейсу SWD

Задействуем ножку, отвечающую за светодиод

Включим внешние прерывания на ножке PA1

Настроим делители и умножители на максимальную производительность в разделе "Clock Configuration" (нажмите на картинку для увеличения изображения)

Перейдём в "Configuration" и в разделе GPIO настроим ножку PC13 на среднюю скорость

Для ножки PA1 применим подтягивающий регистр к общей шине, чтобы она не болталась в воздухе, так как прерывания мы будем ловить от кнопки, а сама кнопка никуда у нас не притянута, иначе состояние будет неопределённое. Тип внешнего прерывания оставим по умолчанию — восходящий фронт

Применим настройки портов и перейдём в настройки глобальных прерываний по кнопке "NVIC", включим там прерывания EXTI1

Также применим данные настройки.

Также в силу того, что у меня тормозит видеозапись при работе с Keil, я решил воспользоваться средой разработки System Workbench, с которой мы уже неплохо знакомы. Вы можете также работать с Keil или с любой другой средой разработки, разницы нет.

Настроим проект, присвоив имя EXTI01 и выбрав среду разработки SW4STM32

Сгенерируем проект, откроем его в System Workbench, как всегда и удалим в настройках отладчика файл отладки.

Соберём проект, попытаемся его запустить, но скорей всего у нас ничего не получится и мы увидим в консоли вот такое сообщение, что прошить контроллер не удалось из-за того, что его не удалось перезагрузить

Конечно, можно вовремя держать и отпускать на плате кнопку RESEТ. Но мы пойдём другим путём. Я почитал некоторое количество форумов и ничего там не нашёл, за исключением правки конфигурационных файлов. Откроем наш файл отладки в свойствах проекта

В открывшемся диалоге зайдём в закладку "Debugger" и нажмём там кнопку "Show generator options"

В открывшемся внизу поле "Reset mode" выберем пункт "Software System reset"

Сохраним настройки, теперь должно всё прошиться.

Откроем main.c и отключим наш светодиод в функции main(), так как мы знаем, что он подключен инверсно (можно конечно это сделать и в Cube MX, но так нагляднее)

/* USER CODE BEGIN 2 */

HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);

/* USER CODE END 2 */

Далее в main.c добавим обработчик внешних прерываний

/* USER CODE BEGIN 4 */

void HAL_GPIO_EXTI_Callback( uint16_t GPIO_Pin)

/* USER CODE END 4 */

Затем мы здесь отследим прерывание именно от 1 линии и зажжем светодиод

void HAL_GPIO_EXTI_Callback( uint16_t GPIO_Pin)

if (GPIO_Pin== GPIO_PIN_1) <

HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET );

__NOP();

/* USER CODE END 4 */

Подключим кнопку между PA1 и проводом питания (нажмите на картинку для увеличения изображения)

Соберём проект, прошьём контроллер. Затем нажмём на кнопку — светодиод должен будет засветиться (нажмите на картинку для увеличения изображения)

Давайте разнообразим немного наш проект, чтобы он не был таким скучным. Для этого зайдём в Cube MX, и, так как у нас не Keil, проект закрывать не надо, и включим внешние прерывания ещё и на ножке PA2

Причем включим мы уже другой тип — срабатывание прерывания по низходящему фронту, а резистор мы уже наоборот подтянем к источнику напряжения. Для этого внесём следующие настройки в "Configuration" в GPIO для ножки PA2

Применим настройки, сгенерируем проект, вернёмся в System Workbench и сделаем проекту Refresh.

Допишем следующим образом наш обработчик, чтобы по прерыванию на ножке PA1 наш светодиод зажигался, а в случае PA2 — потухал

void HAL_GPIO_EXTI_Callback( uint16_t GPIO_Pin)

Читайте также:  Дизайн детской комнаты с угловым шкафом

if (GPIO_Pin== GPIO_PIN_1) <

HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET );

> else if (GPIO_Pin== GPIO_PIN_2) <

HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET );

Подключем ещё одну кнопку, но уже к PA2, причём вторым выводом уже к общему проводу (нажмите на картинку для увеличения изображения)

Теперь, если мы соберём код и прошьём контроллер, то первая кнопка будет у нас светодиод зажигать, а вторая тушить.

Казалось бы, что тут такого? А то, что происходит это уже независимо от хода самой программы, не в бесконечном цикле, когда во время там написанного обработчика по нажатию кнопки мы можем вообще не находиться и будет уже не ясно, обработается ли наше событие. Поэтому, изучив внешние прерывания, мы сделали большой шаг вперёд к независимости обработки тех или иных событий на ножках портов микроконтроллера. Конечно, учитывая наш уже теперь очень богатый опыт, нам было это проделать не очень тяжело.

Всем спасибо за внимание!

Отладочную плату можно приобрести здесь STM32F103C8T6

Смотреть ВИДЕОУРОК (нажмите на картинку)

В статье пользователя Rough был описан способ обработки нажатия пользовательской кнопки с использованием чтения состояния портов ввода/вывода, а так же таймера. В случае, когда разрабатываемое устройство должно незамедлительно реагировать на какое — либо событие (например, нажатие кнопки, или срабатывание датчика), более уместно использовать внешние прерывания.

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

Микроконтроллеры семейства STM32F4xx имеют 23 линии внешних прерываний. Первые 16 линий используются для формирования запросов на прерывание от любых из пинов микроконтролерра для портов от A до H, к оставшимся же линиям «привязаны» события от периферии, например USB, Ethernet, часов реального времени, а так же PVD (Programmable voltage detector), модуля, позволяющего контролировать изменение напряжения питания микроконтроллера[1].

Использование альтернативных функций, которые совмещены с портами ввода/вывода, позволяют обрабатывать до 83 различных событий, полный перечень которых можно найти в Reference manual [2]. Но здесь есть одно ограничение, если процедура прерывания сконфигурирована таким образом, что к линии EXTI_Line0 «привязан» пин PA0, то мы не имеем права добавлять к этой линии любой другой источник прерывания от выводов PB0 – PH0. Зато у нас есть возможность выставить до 16 уровней приоритета на обработку прерываний, а так же настроить вывод на регистрацию фронта, спада, или же любого изменения уровня сигнала.

При работе с внешними прерываниями мы будем использовать дополнительные библиотеки stm32f4xx_exti.h, stm32f4xx_syscfg.h и misc.h. Первые две необходимы для конфигурации процедуры прерывания, а последняя, для настройки обработчика запроса на прерывание. Среда разработки CooCox v1.7.6.

Чтобы можно было визуально подтвердить то, что микроконтроллер обрабатывает внешнее прерывание сформулируем такое техническое задание: при нажатии пользовательской кнопки светодиоды, расположенные на плате начинают загораться с некоторым интервалом времени, при повторном нажатии светодиоды гаснут.

Для реализации этого задания нам необходимо выполнить следующие основные пункты:

  1. Сконфигурировать порты ввода/вывода для светодиодов;
  2. Сконфигурировать процедуру прерывания и обработчик запроса на прерывание;
  3. Написать код обработчика прерывания.

Итак, подключаем библиотеки, объявляем переменные, создаем функцию init_led_port, которая будет отвечать за инициализацию выводов, к которым подключены светодиоды. Каждая строка кода имеет подробное описание, поэтому будем двигаться дальше.

Теперь перейдем к функции, в которой будем задавать конфигурацию нашей процедуры прерывания.

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

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

Далее следует настройка остальных параметров задающих приоритет, вид изменения сигнала, для генерации запроса на прерывание, а так же конфигурация обработчика запроса на прерывания (структура nvic)

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

Осталось написать последний элемент — обработчик прерывания. Имя функции-обработчика должно обязательно соответствовать номеру линии, на которой мы ожидаем прерывание, то есть для нулевой линии это будет EXTI0_IRQHandler, для первой EXTI1_IR QHandler и так далее.

Здесь мы сталкиваемся с небольшой проблемой — дребезг контактов. Существует два основных метода программной борьбы с этим явлением — временная задержка и подсчет числа совпадающих значений сигнала. Мы воспользуемся вторым методом. Наш цикл в течении 140 итераций проверяет остается ли нажатой кнопка, и в случае появления низкого потенциала (кнопка оказалось отпущенной или дребезг еще не закончился) счетчик сбрасывается. В нашем случае необходимо достаточно большое количество итераций цикла. Это связано с видом кнопок, которые мы используем, применение мембранных кнопок позволяет сократить время дребезга примерно в 10 раз. Наличие оператора return может показаться избыточным, но по субъективным ощущениям это повышает надежность функционирования обработчика прерывания.

Формируем основное тело программы:

Список используемой литературы и источников:

Комментировать
759 просмотров
Комментариев нет, будьте первым кто его оставит

Это интересно
Adblock detector