STM Урок 74. HAL. EXTI или внешние прерывания



 

Урок 74

 

HAL. EXTI или внешние прерывания

 

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

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

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

 

image00

 

Можно разделить данные типы на 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.

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

 

image01

 

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

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

 

image02

 

Здесь мы видим 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 новый проект, выбрав наш контроллер

 

image03

 

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

 

image04

 

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

 

image05

 

 

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

 

image06

 

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

 

image07

 

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

 

image08_0500

 

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

 

image09

 

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

 

image10

 

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

 

image11

 

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

 

 

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

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

 

image21

 

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

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

 

image22

 

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

 

image23

 

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

 

image24

 

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

 

image25

 

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

Откроем 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);

  } else{

    __NOP();

  }

}

/* USER CODE END 4 */

 

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

 

image13_0500

 

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

 

image14_0500

 

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

 

image15

 

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

 

image16

 

Применим настройки, сгенерируем проект, вернёмся в 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);

  } else{

    __NOP();

  }

}

 

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

 

image17_0500

 

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

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

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

 

 

Предыдущий урок Программирование МК STM32 Следующий урок

 

Исходный код

 

 

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

 

 

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

 

STM HAL. EXTI или внешние прерывания

13 комментариев на “STM Урок 74. HAL. EXTI или внешние прерывания
  1. Денис:

    А как избавиться при использовании прерываний "дребезга контактов"?

    • Cергей:

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

      off — у меня в одном из проектов таймер автоматом менял уровни на двух линиях матричной клавиатуры, и следил нажаты ли какая то кнопка или нет, при нажатии кнопки увеличивал ее счетчик, при отпускании сбрасывал в 0, при дастижени определенного значения выставлял флаг события. на 407 камне это почти не занимало времени при опросе 2*5 кнопок примерно 100 раз в секунду.

      • Денис:

        Я вначале тоже хотел RC цепочку сделать, а потом подумал и на таймере "опросник" сделали. Если в течение 200мс после основного нажатия были нажатия — игнорируем.

  2. михаил:

    Большое спасибо за Ваш титанический труд. Благодаря Вашим урокам решил слезть с AVR и перейти на stm32.
    Ваши уроки беру за основу и пытаюсь сделать что-то другое. И вот как раз с внешним прерыванием случился «затык».
    Задача: По внешнему прерыванию отправить байт по SPI. Создал проект в CubeMx, задал SPI1 и EXTI4. Создал проект и перешел в TrueStudio(они с недавнего времени бесплатно раздают «For STM32» без ограничения по коду). В файле stm32f1xx_it.c CubeMx создал пустой обработчик прерывания EXTI4_IRQHandler. Вставляю туда строчку вызова функции HAL
    HAL_SPI_Transmit(&hspi1, TxBuf, 1, 10) — компилятор ругается на hspi1. Объявлял extern hspi1 — все равно ругается.
    Сможете подсказать как решить проблему? Заранее благодарен.

  3. михаил:

    Разобрался. В stm32f1xx_it.c подключить stm32f1xx_hal_spi.h Невнимательность.

  4. MoronRandy:

    А как отключить прерывание? Ну то есть, имеем например кнопку, и при нажатии нужно отключить внешнее прерывание и запустить таймер, и уже в таймере, когда дотикает, нужно снова включить прерывания (пусть 20 мс). Есть какая-то функция вкл/выкл?

    • Есть, но при работе с библиотекой HAL не предусмотрена и не приветствуется. Смотрите REF Manual.

      • David:

        Объясните пожалуйста, в чем проблема, когда ставлю прерывание по кнопке, но не что бы она загорелась, а что бы светодиод моргнул через задержку, но вместо этого он просто горит и все
        void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
        {
        if(GPIO_Pin== GPIO_PIN_4)
        {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_RESET);
        HAL_DELAY(1000);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
        }
        else
        {
        __NOP();
        }

    • Andy_ry:

      HAL_NVIC_DisableIRQ(EXTI15_10_IRQn); // OFF
      HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); // ON
      Прерывания отключать можно и нужно. Иначе дребезг контактов затормозит выполнение основной программы и менее приоритетных прерываний.

  5. Сергей:

    Если нельзя отключать включать менять фронт прерываний во время выполнения программы это полный отстой !

  6. Игорь:

    Скажите пожалуйста , если я поставил срабатывание по восходящему и спадающим фронтам , каким образом мне узнать по какому фронту произошло прерывание ?

  7. Сергей:

    здравствуйте! а зачем резистор?

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*