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

Динамические обработчики

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

Проект называется zfp_intr, а часть модулей, которые могут быть полезны и для других проектов на базе ZFP, вынесены в каталог sam4s-zfp-support.

На самом деле нет необходимости писать на ассемблере таблицу векторов прерываний, это можно сделать и на Ada в виде массива адресов функций и настроить регистр VTOR на эту таблицу (кроме вектора прерывания по RESET, в случае сборки для FLASH).

В нашем проекте мы сделаем что-то промежуточное - создадим таблицу, векторов в start-xxx.S, но из "реальных" исключений, там будет только SystTick, все остальные вызывают функции обёртки, для трапов - Fault_Handler, для прерываний - IRQ_Handler, которые уже будут вызывать настоящий обработчик прерывания или трапа.

Настоящие обработчики прерываний будем хранить в специальном массиве на Ada, которые мы сможем наполнять и изменять по ходу исполнения программы.

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

Заодно, если уж имеем таблицу векторов в start-xxx.S, то и VTOR логичнее настроить на неё тут же, не откладывая в дальний ящик.

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

Приоритеты прерываний

Следующая важная вещь, которую реализуем в этом проекте - назначение приоритетов для прерываний.

Микроконтроллер SAM4S поддерживает приоритетную систему прерываний с вытеснением, это означает, обработчик прерывания может быть может прерван другим более приоритетным запросом, после обработки которого управление возвращается предыдущему (или основному потоку программы).

Приоритет основного потока программы определяется значением в регистре процессора BASEPRI.

Приоритеты трапов (включая SystTick) назначаются через регистры системного контроллера SYS_CTRL.SHPR1 - SYS_CTRL.SHPR3, приоритеты прерываний - через регистры контроллера прерываний NVIC_IPRx [x=0..8].

Формат везде одинаков, под приоритет линии прерываний отводится один байт, при этом младшая тетрада игнорируется, т.е. фактически есть 15 приоритетов от 241 до 255 по степени убывания (большее число соответствует меньшему приоритету).

Значение 0 в BASEPRI отключает маскирование прерываний на основе приоритетов (т.е. основной поток программы может быть прерван любым прерыванием).

Реализация обработки прерываний

Реализация описанной схемы достаточно проста

sam4s_init.ads, sam4s_init.adb.

type IRQ_Handler is access procedure (Id : IRQ_Id);
  
procedure Set_IRQ_Handler (Id      : IRQ_Id;
                           Handler : IRQ_Handler;
                           Prior   : Priority := 8)

позволяет назначить обработчик Handler на прерывание с указанным IRQ_Id, установить ему приоритет и включить само прерывание в регистре контроллера прерываний NVIC_ISER.

При срабатывании прерывания, обёртка

procedure IRQ_Handler_Wrapper

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

Также интерфейс позволяет установить приоритет для основного потока

procedure Set_Current_Priority (Prior : Priority);
  

запретить прерывания (например, при доступе к разделяемым данным), и разрешить их, после выполнения требуемых действий:

procedure Disable_Interrupts;
procedure Enable_Interrupts;
  

Пример приложения

В этом примере используются светодиоды и кнопку платы расширения OLED1, но при некотором упрощении можно ограничиться использованием лишь платы процессора.

Программа, проверяя текущее время в цикле, периодически моргает светодиодом LED0. Отсчёт времени обеспечивает обработчик прерывания SysTick (пакет Timer).

Кроме того, обработчик прерывания таймера в своём потоке, через назначение callback, моргает светодиодом LED3 (на плате OLED1), что позволяет нам видеть его непрерывную работу. Таймер имеет максимальный приоритет прерываний.

Другой обработчик прерывания, с более низким приоритетом, назначен на порт ввода-вывода PIOA, и срабатывает по нажатию кнопки BUTTON 1 (на плате OLED1), подключенной к линии 0 этого порта.

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

За это время обработчик кнопки многократно прерывается таймером, мы видим, что светодиод таймера исправно мигает. При этом светодиод LED0, который переключается в основном потоке программы, имеющем самый низкий приоритет, замирает до завершения обработчика кнопки.

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

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

Назначим тот же самый обработчик на прерывание от другого порта по кнопке BUTTON 2, но с более низким приоритетом.

Ввиду указанного отличия приоритетов, при нажатии на кнопку BUTTON 2, в то время, когда обработчик BUTTON 1 ещё не завершился (LED1 горит), обработчик BUTTON 2 попадает в состояние ожидания, и начинает исполнение только по завершению первого. Но при нажатии кнопок в обратной последовательности, светодиод LED1 загорается немедленно, даже есть LED2 горит.

  • реализацию несложной, но достаточно гибкой подсистемы обработки прерываний;
  • системное время, позволяющее планировать события;
  • пример организации обработки прерывания от порта ввода-вывода PIO;
  • набор принципиально важных пакетов для построения других примеров и реальных однозадачных приложений;


© 2013-2018 Евгений Турышев. Материалы данного сайта нельзя использовать без предварительного согласия автора