Меню Закрыть

Прерывания в STM32. Внешние прерывания

Внешние прерывания

Давайте разберёмся с таким важным механизмом работы микроконтроллера как прерывания. Каких видов они бывают, как ими пользоваться и на практике попробуем использовать внешнее прерывание.

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

Виды прерываний

В STM32 существует: внутренние,  внешние и системные прерывания. Внешние прерывания (EXTI) срабатывают при изменении состояния пинов ввода-вывода, например, нажатии кнопки или активации датчика. Внутренние прерывания генерируются внутренними модулями микроконтроллера, такими как таймеры, UART, ADC и другие, и сигнализируют о завершении операции или наступлении важного события. Системные это встроенные прерывания ядра Cortex-M обеспечивающие базовую работу самой системы и операционной среды микроконтроллера.

Модуль NVIC

В STM32 за управление прерываниями отвечает специальный модуль — NVIC (Nested Vectored Interrupt Controller). Он позволяет не только включать и отключать нужные прерывания, но и задавать им приоритеты, чтобы важные события могли прерывать менее важные. Благодаря NVIC микроконтроллер умеет обрабатывать прерывания «вложено» — одно может прерывать другое, если оно важнее. Настройка происходит через удобные функции HAL, такие как HAL_NVIC_SetPriority() и HAL_NVIC_EnableIRQ(), а если нужна более точная настройка, можно использовать CMSIS или LL-библиотеку.

Понятие “Вектор прерывания”

У каждого прерывания есть свой вектор прерывания. Вектор прерывания — это указатель на функцию, которую микроконтроллер должен вызвать при срабатывании конкретного прерывания. Когда происходит прерывание, STM32 «смотрит» в специальную таблицу — векторную таблицу прерываний, чтобы узнать, куда перейти для обработки прерывания. Для примера возьмём наш микроконтроллер STM32F407VGT6 у него 82 возможных вектора прерываний. Все они есть в таблице в reference manual. Я приведу небольшую часть этой таблицы.

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

Шаги для работы с прерываниями.

  1. Разрешаем глобальные прерывания. Если вы используете HAL то это происходит в функции HAL_Init()
  2. Настраиваем само устройство ( например таймер ) на работу с прерываниями
  3. Разрешаем нужное прерывания для этого конкретного устройства
  4. Создаём функцию обработчик прерывания в самом коде программы

Внешние прерывания при нажатии на кнопку

Рассмотрим работу с внешними прерываниями на примере платы STM32F4 Discovery, построенной на микроконтроллере STM32F407VGT6.  У неё на борту уже есть встроенный светодиод и кнопка , что удобно для нашего примера. Логика работы следующая. При нажатии кнопки будет срабатывать внешнее прерывание. В нём мы запоминаем состояние кнопки. Затем запускаем счетчик таймера на 10 мс. Как только произойдет срабатывание таймера, сравниваем состояние кнопки и если оно такое же как и было то переключаем светодиод. Это гарантирует защиту от случайных нажатий и дребезга. 

По шагам разберёмся с настройками. Делаем всё в CubeIDE. Открываем наш файл .ioc

1.Будем использовать внутренний кварцевый генератор. 

2.По схеме кнопка подключена на пин PA0. Настроим его для работы с внешними прерываниями. Для этого кликнем по нему и выберем GPIO_EXTI0(работа с прерываниями).

3.Во вкладке GPIO выберем нашу строку с пином. Включим срабатывание по возрастающему фронту. GPIO mode — External Interrupt Mode with Rising edge trigger detection.

4.Вкладка NVIC. Разрешим прерывания по нашей линии Exti line 0.

5.Настроим встроенный на плате светодиод на выход. Пускай это будет PD12,светодиод зелёного цвета.

6.Настроим наш таймер. Подробнее про таймеры есть в этой статье (ссылка на статью про таймер). Я выбрал таймер 2, в нашем случае не критично, можете выбирать любой.

Такие настройки гарантирую что при частоте  16МГц переполнение произойдет через 10мс.

Не забудьте разрешить прерывания таймера.

С настройками закончили. Перейдём к коду. Зададим переменную состояния кнопки .

/* USER CODE BEGIN PV */
volatile uint8_t button_state_snapshot = 0;
/* USER CODE END PV */

Далее нас интересуют две функции. Функция вызова при внешнем прерывании.

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if (GPIO_Pin == GPIO_PIN_0) // Проверяем кнопку PA0
{
button_state_snapshot = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
__HAL_TIM_SET_COUNTER(&htim2, 0); // Сброс счётчика
HAL_TIM_Base_Start_IT(&htim2); // Запуск таймера с прерыванием
}
}

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

Функция обработчик прерывания таймера.

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM2) {
    uint8_t current_state = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
    if (current_state == button_state_snapshot && current_state == GPIO_PIN_SET)
    {
    HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
    }
    HAL_TIM_Base_Stop_IT(&htim2); // Остановить таймер
}
}

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

На этом мы завершаем. В этом примере мы разобрались с принципом работы прерываний и применили их для управления светодиодом.

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

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