Аппаратное прерывание - это сигнал, сообщающий о каком-то событии. По его приходу выполнение программы приостанавливается, и управление переходит на функцию обработки прерывания (обработчик прерывания). После отработки функции управление возвращается в прерванный код программы.
С точки зрения программы прерывание это вызов функции по внешнему, не связанному напрямую с программным кодом, событию.
При определении понятия “прерывание” я употребил слово “событие” в общепринятом значении. Т.е. происшествие, нечто случившееся. Но в системе STM32 наряду с прерыванием (interrupt) существует еще одно строгое понятие – аппаратное событие (event).
Событие – это нечто происшедшее с аппаратным узлом микроконтроллера. Например, переполнился таймер, в порт UART пришло данное, на внешнем входе изменился уровень сигнала и т.п.
Аппаратное событие – это то, что вызывает прерывание. Вернее может вызвать прерывание.
Без события прерывание невозможно. Именно аппаратное событие инициирует прерывание.
События без прерываний могут происходить.
И дело не только в том, что прерывания можно запретить и игнорировать события. В системе STM32 возможно использование событий для управления периферийными устройствами микроконтроллера. Например, прием байта портом UART вызывает событие, которое в свою очередь запускает контроллер прямого доступа к памяти (DMA) для пересылки этого байта в память. Получается, что происходит обработка события без прерывания программы.
Обработка и управление прерываниями производятся аппаратным узлом микроконтроллера – контроллером приоритетных векторных прерываний (NVIC). При возникновении разрешенного события контроллер прерывает выполнение программы, сохраняет в стеке необходимые данные и передает управление по адресу функции обработки прерывания. После выхода из функции-обработчика управление возвращается на адрес, по которому была прервана основная программа.
С точки зрения программного управления контроллер прерывания не особо сложное устройство. Я бы выделил следующее.
Контроллер приоритетный. Т.е. обработка прерывания может быть прервана другим прерыванием с более высоким приоритетом. Прерывания с таким же или более низким приоритетом будут ожидать окончания обработки активного прерывания. Приоритеты можно разбивать на группы и подгруппы.
Как следствие предыдущего пункта для каждого прерывания может быть задан приоритет. Это число от 0 до 15. 0 – самый высокий приоритет. Для установки приоритетов существует специальный блок регистров. На каждое прерывание в нем отведен один байт.
Могут быть запрещены сразу все прерывания – глобальный запрет.
Могут быть запрещены или разрешены отдельные прерывания. Для этого существует регистр разрешения или маски прерываний. Его отдельные биты определяют разрешение для каждого прерывания.
Существует блок регистров ожидания обработки прерываний, каждый бит которых соответствует конкретному прерыванию и показывает, ожидает ли оно обработки. Биты регистров могут быть установлены, для принудительного перевода прерываний в состояние ожидания или сброшены для удаления.
Есть регистры действующих прерываний, которые показывают, находятся ли прерывания в стадии обработки.
Любое прерывание может быть вызвано программно. Для этого существует регистр программного вызова прерываний. Прерывание вызывается записью в него соответствующего номера.
Каждому из прерываний соответствует свой фиксированный адрес-вектор, по которому передается управление для обработки прерывания.
Формально обработкой и управлением прерываниями занимается контроллер прерываний. Но для возникновения прерывания необходимо выполнение нескольких условий связанных как с контроллером прерываний, так и с периферийными устройствами микроконтроллера. Поэтому будем рассматривать задачу в комплексе.
Для работы с аппаратными прерываниями необходимо:
Для этого существуют функции библиотеки CMSIS.
void NVIC_EnableIRQ(IRQn_Type IRQn) – разрешение прерывания с номером IRQn.
void NVIC_DisableIRQ(IRQn_Type IRQn) – запрет прерывания с номером IRQn.
В файле stm32f103xb.h есть символьные имена, соответствующие номерам прерываний. Они описаны в структуре IRQn_Type.

Команда разрешения прерывания перезагрузки таймера TIM1 будет выглядеть так.
NVIC_EnableIRQ(TIM1_UP_IRQn);
По состоянию сброса для всех прерываний устанавливается самый высокий приоритет (0). Для его изменения существует функция библиотеки CMSIS.
void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
Например, если мы хотим для таймера TIM1 установить самый низкий приоритет, то необходимо выполнить команду.
NVIC_SetPriority(TIM1_UP_IRQn, 15);
В программе бывают отрезки, которые недопустимо прерывать по самым разным соображениям. А аппаратные прерывания совершенно непредсказуемы. В этом случае можно запретить все прерывания, используя функцию глобального запрета.
__disable_irq (); // запретить прерывания
По окончанию непрерываемого блока надо разрешить прерывания специальной функцией.
__enable_irq (); // разрешить прерывания
Это действие касается настройки конкретного периферийного устройства и индивидуально для каждого из них. Будем рассматривать для каждого устройства отдельно. В следующем уроке настроим таймер.
В предыдущих уроках мы уже использовали функцию обработки прерывания таймера. Она была создана конфигуратором STM32CubeMX. Сейчас разберемся, откуда она появилась.
В микроконтроллерах STM32 поддерживается до 68 аппаратных прерываний. Каждому из них соответствует свой адрес – вектор. По этому адресу передается управление программы при возникновении прерывания.
Конкретные адреса для каждого источника можно посмотреть в документации на микроконтроллер. Выглядит это примерно так.

Из таблицы видно, что наш таймер TIM1 при перезагрузке формирует прерывания по адресу 00A4.
В файле startup\startup_stm32f103xb.s таблица векторов преобразуется в символьные имена функций обработки прерываний.
.word TIM1_BRK_IRQHandler
.word TIM1_UP_IRQHandler
.word TIM1_TRG_COM_IRQHandler
.word TIM1_CC_IRQHandler
.word TIM2_IRQHandler
.word TIM3_IRQHandler
.word TIM4_IRQHandler
Для нашего таймера это TIM1_UP_IRQHandler.
Необходимо создать функцию с таким именем и разместить в ее теле обработчик прерывания.
void TIM1_UP_IRQHandler(void) {
// код обработки прерывания
// код или функция завершения прерывания
}
В начале файла c/cpp или h надо объявить функцию, разместить прототип.
void TIM1_UP_IRQHandler(void);
При выходе из функции, еще необходимо выполнить определенные действия для завершения прерывания. Как правило, это сброс флага прерывания.
Иначе может произойти следующее. Устройство сформировало событие. Как следствие стал активным флаг прерывания. Произошло, и было обработано прерывание. Программа вернулась к месту, на котором была прервана. А флаг прерывания остался активным. Произойдет новое прерывание, вызванное ошибкой алгоритма. Перед возвратом надо сбросить флаг. Об этом подробно в следующем уроке.
Автор: Эдуард