Внешние прерывания STM32.
Плюсом внешних прерываний является то, что они фиксируют переход от одного логического уровня к другому, а не сам уровень.
Предположим нам надо зафиксировать когда изменится напряжение на какой-то ножке и экстренно выполнить определённый набор инструкций. Самый простой алгоритм выглядит следующим образом, через определённые интервалы времени опрашиваем вывод и анализируем изменилось ли его состояние. Если время между опросами будет большим, то мы можем пропустить переход, а если мало, то большая часть процессорного времени будет тратиться на опрос вывода и ожидание события. Понятно, что рассмотренный алгоритм нам не подходит. Что же делать? Решение этой задачи уже давно придумано за нас инженерами, разрабатывающими микроконтроллеры, название его — внешнее прерывание.
Как это работает? При изменении логического уровня на ножке микроконтроллера, он бросает все свои дела и бежит выполнять инструкции, расположенные по заданному адресу, наше дело разместить по этому адресу нужный код.
Теперь от общей теории перейдём к частным вариантам реализации. У AVR под внешние прерывания отведены конкретные выводы, у Atmega16 их 4. У STM32 дело с внешними прерываниями обстоит интереснее, управляет внешними прерываниями контроллер EXTI. Любой из выводов порта может быть настроен на работу с внешними прерываниями, но количество выводов GPIO одновременно настроенных на работу с внешними прерываниями не может превышать шестнадцать. Эти выводы соединяются с EXTI c помощью мультиплексоров.
Если посмотреть на картинку с мультиплексорами видно, что номер мультиплексора совпадает с номером вывода и мы не можем настроить на прерывание два вывода с одинаковыми номерами, то есть нельзя одновременно настроить на внешние прерывания PB0 и PA0.
Есть ещё 4 линии, которые подключаются не к GPIO, а к периферии:
Кроме привычных прерываний, контроллер EXTI может генерировать события и программные прерывания.
Событие — выставляется флаг, без перехода в обработчик прерывания.
Программное прерывание — то же прерывание, но вызывается вручную, записью '1' в соответствующий регистр.
Существует всего семь обработчиков внешних прерываний: EXTI0, EXTI1, EXTI2, EXTI3, EXTI4, EXTI9_5, EXTI15_10. Видно, что не каждое внешнее прерывание от GPIO имеет отдельный обработчик, некоторые из них совмещены.
Давайте рассмотрим регистры необходимые для настройки внешних прерываний.
Первым делом надо включить тактирование порта и альтернативных функций так, как прерывание — это альтернативная функция порта.
Вывод настроенный на работу с внешними прерываниями может быть сконфигурирован как: плавающий вход, вход с подтяжкой к питанию/земле.
Для настройки мультиплексоров выделены 4 регистра AFIO_EXTICRx разделённые на 4 секции, по 4 бита в каждой, мы рассмотрим один из них.
Для настройки нулевого вывода на работу с внешними прерываниями, надо в секцию EXTI0 записать код порта.
EXTI_IMR - регистр маскировки прерываний, запись '1' в соответствующий бит разрешает прерывания, по умолчанию там нули.
EXTI_EMR – регистр маскировки событий, запись '1' в соответствующий бит разрешает событие, по умолчанию там нули.
EXTI_FTSR и EXTI_RTSR – определяют по какому фронту будет возникать прерывание. При записи '1' в соответствующий бит EXTI_FTSR по спадающему фронту, в EXTI_RTSR – по возрастающему.
EXTI_SWIER – программный вызов события/прерывания, запись '1' в соответствующий бит вызывает генерацию события/прерывания.
EXTI_PR – флаг возникновения события/прерывания, сбрасывается вручную при записи '1'.
Теперь давайте рассмотрим простой пример, в котором по нажатию кнопки, которая подключена к нулевому выводу порта E, будет генерироваться прерывание.
Предположим нам надо зафиксировать когда изменится напряжение на какой-то ножке и экстренно выполнить определённый набор инструкций. Самый простой алгоритм выглядит следующим образом, через определённые интервалы времени опрашиваем вывод и анализируем изменилось ли его состояние. Если время между опросами будет большим, то мы можем пропустить переход, а если мало, то большая часть процессорного времени будет тратиться на опрос вывода и ожидание события. Понятно, что рассмотренный алгоритм нам не подходит. Что же делать? Решение этой задачи уже давно придумано за нас инженерами, разрабатывающими микроконтроллеры, название его — внешнее прерывание.
Как это работает? При изменении логического уровня на ножке микроконтроллера, он бросает все свои дела и бежит выполнять инструкции, расположенные по заданному адресу, наше дело разместить по этому адресу нужный код.
Теперь от общей теории перейдём к частным вариантам реализации. У AVR под внешние прерывания отведены конкретные выводы, у Atmega16 их 4. У STM32 дело с внешними прерываниями обстоит интереснее, управляет внешними прерываниями контроллер EXTI. Любой из выводов порта может быть настроен на работу с внешними прерываниями, но количество выводов GPIO одновременно настроенных на работу с внешними прерываниями не может превышать шестнадцать. Эти выводы соединяются с EXTI c помощью мультиплексоров.
Если посмотреть на картинку с мультиплексорами видно, что номер мультиплексора совпадает с номером вывода и мы не можем настроить на прерывание два вывода с одинаковыми номерами, то есть нельзя одновременно настроить на внешние прерывания PB0 и PA0.
Есть ещё 4 линии, которые подключаются не к GPIO, а к периферии:
- EXTI 17 – выход PVD
- EXTI 18 – событие от RTC_Alarm
- EXTI 19 – событие от USB_Wakeup
- EXTI 20 – событие от Ethernet_Wakeup
Кроме привычных прерываний, контроллер EXTI может генерировать события и программные прерывания.
Событие — выставляется флаг, без перехода в обработчик прерывания.
Программное прерывание — то же прерывание, но вызывается вручную, записью '1' в соответствующий регистр.
Существует всего семь обработчиков внешних прерываний: EXTI0, EXTI1, EXTI2, EXTI3, EXTI4, EXTI9_5, EXTI15_10. Видно, что не каждое внешнее прерывание от GPIO имеет отдельный обработчик, некоторые из них совмещены.
Давайте рассмотрим регистры необходимые для настройки внешних прерываний.
Первым делом надо включить тактирование порта и альтернативных функций так, как прерывание — это альтернативная функция порта.
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN | RCC_APB2ENR_IOPEEN;
Вывод настроенный на работу с внешними прерываниями может быть сконфигурирован как: плавающий вход, вход с подтяжкой к питанию/земле.
Для настройки мультиплексоров выделены 4 регистра AFIO_EXTICRx разделённые на 4 секции, по 4 бита в каждой, мы рассмотрим один из них.
Для настройки нулевого вывода на работу с внешними прерываниями, надо в секцию EXTI0 записать код порта.
//выбор порта и пина для внешнего прерывания
AFIO->EXTICR [0] |= AFIO_EXTICR1_EXTI0_PE;
//если хотим выбрать первый пин порта B
AFIO->EXTICR [0] |= AFIO_EXTICR1_EXTI1_PB;
EXTI_IMR - регистр маскировки прерываний, запись '1' в соответствующий бит разрешает прерывания, по умолчанию там нули.
EXTI_EMR – регистр маскировки событий, запись '1' в соответствующий бит разрешает событие, по умолчанию там нули.
EXTI_FTSR и EXTI_RTSR – определяют по какому фронту будет возникать прерывание. При записи '1' в соответствующий бит EXTI_FTSR по спадающему фронту, в EXTI_RTSR – по возрастающему.
EXTI_SWIER – программный вызов события/прерывания, запись '1' в соответствующий бит вызывает генерацию события/прерывания.
EXTI_PR – флаг возникновения события/прерывания, сбрасывается вручную при записи '1'.
Теперь давайте рассмотрим простой пример, в котором по нажатию кнопки, которая подключена к нулевому выводу порта E, будет генерироваться прерывание.
#include "stm32f10x.h"
void EXTI0_IRQHandler(void)
{
//сбрасываем флаг прерывания
EXTI->PR |= EXTI_PR_PR0;
}
int main(void)
{
//включаем тактирование порта и альтернативных функций
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN | RCC_APB2ENR_IOPEEN;
//настраиваем вывод на вход с подтяжкой
GPIOE->CRL &= ~GPIO_CRL_CNF0_0;
GPIOE->CRL |= GPIO_CRL_CNF0_1;
GPIOE->CRL &= ~GPIO_CRL_MODE0 ;
//подтягиваем к питанию
GPIOE->BSRR |= GPIO_BSRR_BR0;
//выбор порта и пина для внешнего прерывания
AFIO->EXTICR [0] |= AFIO_EXTICR1_EXTI0_PE;
//по спадающему фронту
EXTI->FTSR |= EXTI_FTSR_TR0;
//устанавливаем маску
EXTI->IMR |= EXTI_IMR_MR0;
//разрешаем прерывания EXTI0
NVIC->ISER[0] = NVIC_ISER_SETENA_6;
//разрешаем прерывания глобально
__enable_irq ();
while(1)
{
}
}
Похожие статьи