Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Кармин Новиелло - Освоение STM32.pdf
Скачиваний:
2751
Добавлен:
23.09.2021
Размер:
47.68 Mб
Скачать
UART_IRQ_Hanler()
ISR.

Запуск FreeRTOS

642

...

__enable_irq();

// Теперь все IRQ разрешены снова, и восстановлено нормальное поведение ОСРВ

Реализация критической секции с использованием API-интерфейсов CMSIS не является тривиальной задачей, поскольку мы должны позаботиться о специальных аппаратных ситуациях, которые могут возникнуть. Тем не менее, FreeRTOS предоставляет нам четыре процедуры, которые мы можем использовать для определения критических секций в нашем приложении.

Функции taskENTER_CRITICAL() и taskEXIT_CRITICAL() позволяют определять критиче-

скую секцию внутри потока. Эти процедуры предназначены для отслеживания вложенности, то есть каждый раз, когда taskENTER_CRITICAL() вызывается, счетчик увеличивается и уменьшается при последующем вызове функции taskEXIT_CRITICAL(). Это означает, что мы должны обязательно соблюдать порядок вызова.

taskENTER_CRITICAL(); // Внутренний счетчик увеличивается до 1

...

taskENTER_CRITICAL(); // Внутренний счетчик увеличивается до 2

...

taskEXIT_CRITICAL(); // Внутренний счетчик уменьшается до 1

...

taskEXIT_CRITICAL(); // Внутренний счетчик уменьшается до 0

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

Функции taskENTER_CRITICAL() и taskEXIT_CRITICAL() никогда не должны вызываться из ISR: соответствующие функции taskENTER_CRITICAL_FROM_ISR() и taskEXIT_CRITI-

CAL_FROM_ISR() подходят для этого применения. Для получения дополнительной информации обратитесь к документации FreeRTOS.

23.6.3. Обработка прерываний совместно с ОСРВ

Общее практическое правило процедур обработки прерываний заключается в том, что они должны быть быстрыми. Медленная ISR может вызвать потерю других событий, как сгенерированных тем же периферийным устройством, так и другими источниками, если эта ISR имеет более высокий приоритет.

Некоторые функции ОСРВ могут упростить обработку прерываний, отложив действующее обслуживание прерывания потоку. Отложенное исполнение (deferred execution) состоит в делегировании другому выполняющемуся потоку, не работающему на том же самом «низком уровне» процедур прерывания, обрабатывающих действующие прерывания. Например, в Главе 8 мы видели, что прерывание USARTx_IRQn генерируется, когда новые данные готовы для передачи из регистра данных UART: ISR эффективно берет эти байты из регистра и помещает их в буфер. Однако мы также видели, что

выполняет много других операций, которые замедляют выполнение

В этом сценарии мы могли бы иметь отдельный поток для каждой ISR. Этот поток будет проводить много времени в состоянии «заблокирован» в ожидании заданного сигнала.

Запуск FreeRTOS

643

Когда срабатывает IRQ, мы можем запустить этот сигнал, в результате чего заблокированный поток возобновит выполнение работы, которая будет выполнена соответствующей ISR. Назначая потокам разные приоритеты, мы можем установить порядок исполнения в случае конкурентных ISR. Другой подход заключается в использовании очереди для передачи данных, поступающих на периферийное устройство, в рабочий поток, который будет обрабатывать их позже. Это особенно полезно, когда поток потребителя медленнее, чем ISR периферийного устройства, которая в этом случае действует как поток потребителя.

FreeRTOS предоставляет еще один удобный способ отложить выполнение ISR для дру-

гого потока выполнения. Это называется централизованной обработкой отложенных пре-

рываний (centralized deferred interrupt processing), и она состоит в том, чтобы отложить ис-

полнение процедуры в демон-задаче FreeRTOS34. Этот метод использует xTimerPendFunctionCallFromISR(), которая описана в руководстве FreeRTOS35.

Однако имейте в виду, что либо откладывание исполнения в другом потоке, либо использование очереди для обмена данными подразумевает, что ЦПУ выполняет несколько операций, и это может повлиять на надежность управления ISR. Если ваше периферийное устройство работает очень быстро, лучше использовать другие способы передачи данных, например, используя DMA. Всегда рассматривая пример передачи UART, если наше приложение обменивается сообщениями фиксированной длины через UART, мы можем сконфигурировать DMA для передачи сообщения и затем использовать IRQ контроллера DMA для помещения всего сообщения в очереди. Это, безусловно, минимизирует накладные расходы, касающиеся передачи отдельных байт.

23.6.3.1.Приоритеты прерываний и API-функций FreeRTOS

До сих пор мы видели, что FreeRTOS предоставляет некоторые API-интерфейсы, специально спроектированные для вызова из ISR. Для такой функции FreeRTOS существует соответствующая ISR-безопасная процедура, оканчивающаяся на FromISR() (например, xQueueReceiveFromISR() для процедуры xQueueReceive()). Эти процедуры спроектированы таким образом, чтобы прерывания маскировались (путем перехода в и затем выхода из критической секции), предотвращая выполнение других прерываний, которые могли бы генерировать условия гонки, вызывая другие функции FreeRTOS.

Маскирование прерываний необходимо, поскольку прерывания являются источником многозадачности, осуществляемой аппаратными средствами. В то время как потоки – это разные программные потоки, обрабатываемые ОСРВ, которая избегает условий гонки, просто приостанавливая выполнение планировщика, ISR генерируются аппаратными средствами, и мы мало что можем сделать, чтобы избежать условий гонки, если не маскировать их выполнение или не определять строгий приоритетный порядок исполнения. Более того, механизм вложенности ядер Cortex-M увеличивает риск возникновения условий гонки в нашем коде. Например, ISR, начинающая получение семафора, может быть вытеснена другой ISR с более высоким приоритетом, выполняющей ту же операцию. Это наверняка будет иметь катастрофический эффект.

34Демон-задача FreeRTOS также называется задачей обслуживание таймеров (timer service), поскольку это поток, который обрабатывает выполнение процедур обратного вызова таймеров, которые мы проанализируем позже.

35http://www.freertos.org/xTimerPendFunctionCallFromISR.html