Программирование STM32. Часть 15: Внешние прерывания EXTI

Внешние прерывания нужны для реакции прошивки МК на какие-либо быстро протекающие внешние события, которые проблематично регистрировать методом опроса состояния вывода GPIO. В stm32f103c8 для этих целей есть специальный блок EXTI, который мы рассмотрим в этой статье. Предыдущая статья здесь, все статьи цикла можно посмотреть тут: http://dimoon.ru/category/obuchalka/stm32f1.

Возможности контроллера внешних событий/прерываний EXTI

  • Установка края импульса на входе канала EXTI, по которому будет генерироваться прерывание. Можно установить триггер по нарастающему краю импульса, по спадающему, или установить оба триггера сразу.
  • Каждый канал имеет свой бит разрешения прерывания
  • Каждый канал имеет свой бит ожидания запроса прерывания, который необходимо очистить в обработчике соответствующего прерывания
  • Контроллер EXTI гарантированно обнаруживает импульсы, длительность которых больше длительность периода тактового сигнала шины APB2. Т.е. если частота шина APB2 равна 72 МГц, то EXTI будет корректно обнаруживать фронты сигналов с частотами ниже 72 МГц.

Каналы EXTI

Каналы EXTI имеют следующие названия: EXTI0, EXTI1, EXTI2 .. EXTI19. Всего в нашем распоряжении 20 каналов. Причем EXTI0 — EXTI15 могут быть подключены к одному из портов GPIO. EXTI16 подключен внутри МК к выходу программируемого детектора напряжения PVDEXTI17 к событию RTC AlarmEXTI18 к USB микроконтроллера, и EXTI19 к контроллеру Ethernet, если он конечно есть.

В данный момент нас интересуют те каналы EXTI, которые могут быть подключены к портам GPIO. И здесь есть один нюанс. На входе каждого канала EXTI стоит мультиплексор, который позволяет выбрать пин GPIO следующим образом:

Т.е. к EXTI0 можно подключить один из 0-ых выводов портов, к EXTI1 один из 1-ых выводов, и так далее. Для каждой линии значение мультиплексора можно выбрать независимо, т.е. EXTI0 можно подключить к PA0EXTI1 к PB1, и так далее. Однако такая организация подключений имеет некоторое ограничение, которое необходимо учитывать: мы не можем одновременно регистрировать события от, например, линий PA0 и PB0, так как они подключены к одному и тому же мультиплексору.

Внутреннее устройство

Блок-схема контроллера EXTI представлена ниже:

Входной сигнал на канал EXIT подается с Input Line. Далее, он идет на детекторы нарастающего и спадающего краев импульса. Далее, сигнал с детекторов идет на логический элемент ИЛИ, который будет генерировать сигнал на запрос прерывания либо при срабатывании детекторов края импульса, либо при установки бита Software interrupt event. Далее, сигнал запроса прерывания идет на 2 логических элемента И, которые пропускают этот сигнал на генерацию события (нижний элемент) и генерацию прерывания (верхний элемент), если установлены соответствующие биты. Далее, сигнал на генерацию прерывания устанавливает бит в регистре Pending request register, что приводит к запросу к контроллеру прерываний NVIC. После того, как будет изложено описание регистров EXTI, данная блок-схема станет понятней.

Настройка GPIO

Согласно Reference manual-у для работы вывода порта совместно с EXTI данный вывод должен быть настроен на вход. Дополнительных настроек GPIO не требуется.

Регистры конфигурации

Для настройки прерываний, фронтов срабатывания и так далее существуют регистры, расположенные в адресном пространстве контроллера EXTI. Однако, настройки мультиплексоров выбора вывода GPIO, который подключен к соответствующему каналу EXTI выполняется в регистрах AFIO (Alternate function I/O). Мы рассмотрим все регистры EXTI и регистры AFIO, которые относятся к настройки мультиплексоров EXTI.

Interrupt mask register (EXTI_IMR) — Регистр маски прерываний

 

MRx: Разрешения прерывания канала x

  • 0 — прерывание отключено
  • 1 — прерывание включено

 

Event mask register (EXTI_EMR) — Регистр макси событий

С событиями мы еще не разбирались, этот регистр можно просто игнорировать

 

Rising trigger selection register (EXTI_RTSR) — Регистр включения детектора нарастающего края импульса

TRx: Разрешить срабатывание триггера нарастающего края импульса канала x

  • 0 — триггер выключен
  • 1 — триггер включен

 

Falling trigger selection register (EXTI_FTSR) — Регистр включения детектора спадающего края импульса

TRx: Разрешить срабатывание триггера спадающего края импульса канала x

  • 0 — триггер выключен
  • 1 — триггер включен

 

Software interrupt event register (EXTI_SWIER) — Регистр программного прерывания

SWIERx: Программное прерывание линии x

Если прерывание данного канала EXTI включено в регистре EXTI_IMR, то запись ‘1’ в соответствующий бит, если до этого там было ‘0’, приводит к установке соответствующего бита в регистре EXTI_PR и генерируется запрос на прерывание данного канала EXTI.

Этот бит очищается при очистке соответствующего бита в регистре EXTI_PR.

 

Pending register (EXTI_PR) — Регистр ожидания

PRx: Бит ожидания обработки прерывания канала x

  • 0 — события триггеров запроса на прерывания не произошли
  • 1 — произошли события триггеров запроса на прерывание

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

Сброс бита в регистре EXTI_PR выполняется путем записи в соответствующий бит значения ‘1’. Запись значения ‘0’ не оказывает ни какого эффекта.

 

Регистры AFIO

Теперь разберем несколько регистров в AFIO, которые так же участвуют в настройке контроллера внешних прерываний EXTI.

 

External interrupt configuration register 1 (AFIO_EXTICR1) — Конфигурационный регистр 1 внешних прерываний

EXTIx[3:0]: Конфигурация мультиплексора канала x EXTI

  • 0000: Выбор пина PA[x]
  • 0001: Выбор пина PB[x]
  • 0010: Выбор пина PC[x]
  • 0011: Выбор пина PD[x]
  • 0100: Выбор пина PE[x]
  • 0101: Выбор пина PF[x]
  • 0110: Выбор пина PG[x]

 

External interrupt configuration register 2 (AFIO_EXTICR2) — Конфигурационный регистр 2 внешних прерываний

Тут все то же самое, что и для AFIO_EXTICR1, только для каналов EXTI4..7

 

External interrupt configuration register 3 (AFIO_EXTICR3) — Конфигурационный регистр 3 внешних прерываний

Конфигурация каналов EXTI8..11

 

External interrupt configuration register 4 (AFIO_EXTICR4) — Конфигурационный регистр 4 внешних прерываний

Конфигурация каналов EXTI12..15

 

Обработчики прерываний

Для каждого канала EXTI можно независимо от других каналов включить или выключить прерывание. Однако, запросы на прерывание от нескольких каналов EXTI могут быть завязаны на один и тот же обработчик. Приведу список вех обработчиков, относящихся к EXTI:

  • EXTI0_IRQHandler
  • EXTI1_IRQHandler
  • EXTI2_IRQHandler
  • EXTI3_IRQHandler
  • EXTI4_IRQHandler
  • EXTI9_5_IRQHandler
  • EXTI15_10_IRQHandler
  • PVD_IRQHandler
  • RTC_Alarm_IRQHandler
  • USBWakeUp_IRQHandler

EXTI0..4 ни с кем ни чего не делят и имеют по собственному обработчику прерываний. EXTI5..9 имеют один и тот же обработчик прерываний EXTI9_5_IRQHandler, каналы EXTI10..15 так же имеют общий обработчик EXTI15_10_IRQHandler. Если одному и тому же обработчику соответствует несколько каналов EXTI, то отличить запросы от разных каналов внутри обработчика можно с помощью регистра EXTI_PR.

Последние 3 обработчика относятся к линиям EXTI16EXTI17EXTI18 и нас сейчас особо не интересуют.

Пример программы

В качестве демонстрации настроим внешнее прерывание по нарастающему и спадающему краю импульса на пине PA0 микроконтроллера. Создадим функцию void EXTI_Init(void), в которой будем производить все необходимые настройки:

Приступаем к настройке. Первым делом включаем тактирование GPIOA и AFIO. Тактирование EXTI специальным образом включать не надо:

Далее, настроим PA0 на вход с подтяжкой вверх:

Про настройку GPIO можно почитать вот в этой статье.

Теперь настройка EXTI. Так как мы выбрали пин PA0, то прерывание будет висеть на канале EXTI0. Нам нужно в регистре AFIO_EXTICR1 с помощью группы бит EXTI0[3:0] выбрать PA0 в качестве источника сигнала. Делается это вот так:

Далее, выбираем по каким краям у нас будет возникать прерывание. В нашем случае по обоим:

Ну и несколько последних штрихов: на всякий случай сбрасываем флаг запроса на прерывание, включаем соответствующее прерывание в регистре EXTI_IMR и разрешаем прерывание EXTI0_IRQn в NVIC:

Все, готово 🙂

Вот полный код функции:

Не забываем про обработчик прерывания:

И вот такой простой main()

При изменении логического уровня на PA0 будет возникать прерывание EXTI0_IRQHandler().

Тут необходимо обратить внимание на кучу asm(«nop»)-ов в обработчике прерывания. Дело в том, что сразу после вызова EXTI->PR = EXTI_PR_PR0; необходимо хотя бы 2 такта процессора перед выходом из функции обработки прерывания, для того, чтобы флаг прерывания успел сброситься. Иначе обработчик прерывания будет вызван повторно. Поэтому сброс флага прерывания в регистре EXTI_PR желательно выполнять в начале обработчика.

Ну и напоследок небольшой эксперимент. В Reference manual-е сказано, что при работе пина микроконтроллера в качестве источника сигнала для EXTI он должен быть настроен как вход. А что будет, если этот пин настроить на выход? Как будет себя вести система?

В этом случае при программном изменении состояния PA0 система ведет себя точно так же, как и в предыдущем случае, возникает прерывание по изменению логического уровня на PA0. Возможно, если настроить вывод микроконтроллера на выход  Open-drain (открытый коллектор), то этим можно как-то пользоваться в некоторых экзотических случаях.

На этом предновогодняя 15-я статья по микроконтроллерам STM32 окончена, всех с наступающим 0x7E3 годом!  🌲

Метки: , . Закладка Постоянная ссылка.

14 комментариев: Программирование STM32. Часть 15: Внешние прерывания EXTI

  1. Дмитрий пишет:

    Спасибо за уроки!) Планируете еще уроки по cmsis? очень здорово у вас получается.

  2. AlexID пишет:

    Добрый день!
    Спасибо за хорошую серию. Очень толково.
    Ждем продолжения.

    По работе с USB планируете что то?

    • DiMoon пишет:

      Добрый день! Спасибо за комментарий)) По USB пока что нет, так как в нем куча нагромождений уровней, с ходу в него трудно въехать. Ethernet и то понятней 🙂 Сейчас я предпочитаю брать готовые мосты с USB на что то попроще

  3. Дмитрий пишет:

    Доброго времени суток. Приссоединяюсь ко всем сказаным словам благодарности за данные уроки по stm32! Тему ADC не нашел в ваших публикациях.

    • DiMoon пишет:

      Спасибо, стараюсь 🙂 По ADC пока ни чего нет, но планирую

      • Holic пишет:

        Вот бы ещё статьи по таймерам и прерываниям, а статей с использованием CMSIS действительно очень мало в интернете.

  4. Юрий пишет:

    Здравствуйте! У меня на эту строчку GPIOA->CRL |= (0x02 <CRL |= (0x02 << GPIO_CRL_CNF0_0);" , крестик пропадает. Компилится без ошибок и врайтингов. В железе не проверял. Буду пробовать в начале в Proteusе. Почему на "Pos" показывает ошибку? Заранее благодарен, Юрий.

  5. Денис пишет:

    Отличный курс.
    Вопрос — как узнать, по восходящему фронту сработало прерывание или по нисходящему? Или только читать состояние пина в прерывании? Накладок не получится, что вошел в прерывание, а пин уже сменил состояние?

    • DiMoon пишет:

      Спасибо, стараюсь 🙂
      Насколько понял, в вашем случае генерация прерывания настроена и по фронту, и по спаду? В этом случае только читать состояние ножки внутри прерывания.
      Если вошёл в прерывание, а ножка в это время изменила своё состояние, то тут 2 варианта. Если в прерывании сначала сбросили флаг этого прерывания в регистре EXTI_PR, а затем ножка изменила своё значение, то после выхода из прерывания будет сгенерировано ещё одно. Если сначала изменилось состояние ножки, а потом сбросили с помощью EXTI_PR, то повторный вызов прерывания вроде как произойти не должен.

  6. Дмитрий пишет:

    DiMoon, поклон тебе за просвещение. Изучаю STM32 на примере stm32f4discovery несколько лет в свободное время. Не смотря на это нахожу в твоих статьях и комментариях ответы на своивопросы. Поэтому что описание более чем подробное. По сравнению с другими источниками с данной тематикой. Вообще уважаю статьи в которых авторы описывают микроконтроллеры простыми словами, картинками с пояснениями и уточнениями. Если автор видит своего читателя не специалистом и пытается рассказать на простом языке сложные процессы происходящие в микроконтроллере, то вы можете рассчитывать на популярность ваших статей. Основная масса читателей данных сайтов далеко не профи. И очень приятно, когда находишь ответы на свои вопросы. Спасибо за труд. Для меня это просто хобби.

  7. Дмитрий пишет:

    А ещё хорошо не быть скупым. Обращение ко всем читателям. Если вы нашли что то полезное для себя, отправляйте немного денег Автору. Для их творческого развития. Что бы таких статей было больше.

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

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