You are here

Подробнее о буферах потока

Source: https://freertos.org/RTOS-stream-buffer-example.html

Перевод может содержать ошибки. Читайте первоисточник.


[Буферы потоков и сообщений]

RTOS Stream Buffers - Подробнее о буферах потока

Оглавление страницы

  1. Введение
  2. Первые шаги
  3. Блокирующее чтение и пороговый уровень
  4. Блокирующая запись
  5. Макросы завершения передачи и приёма (for multicore use)

Введение

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

В отличие от большинства других коммуникационных примитивов FreeRTOS, буферы потока оптимизированы для сценариев с одним отправителем и одним получателем данных. Например, данные из обработчика прерывания могут передвавться в задачу RTOS, либо от одного ядра микроконтроллера другом в многоядерном процессоре.

Чтобы использовать функционал буферов потока необходимо включить в проект исходный файл FreeRTOS/source/stream_buffer.c

Реализация буферов потока использует механизм уведомлений. Следовательно, вызов функции API для работы с буфером потока, которая переводит вызывающую задачу в заблокированное состояние, может изменить состояние и значение уведомления вызывающей задачи. (Требуется уточнение, т.к. этого момента я не понял. Оставляю оригинальный текст: The stream buffer implementation uses direct to task notifications. Therefore, calling a stream buffer API function that places the calling task into the Blocked state can change the calling task's notification state and value).

ВАЖНОЕ ЗАМЕЧАНИЕ: Уникальность среди объектов FreeRTOS заключается в том, что буфер потока (а также буфер сообщений, т.к. он работает поверх буфера потока) предполагает, что есть только одна задача, которая пишет в буфер потока, и только одна задача, которая читает из буфера потока. Расположение "писателя" и "читателя" в разных задачах или обработчиках прерывания безопасно, но в отличие от большинства других объектов FreeRTOS небезопасно иметь несколько "писателей" либо несколько "читателей". Если всё же в приложении имеется несколько различных "писателей", каждый вызов функций API для записи в буфер (таких как xStreamBufferSend()) необходимо оборачивать в критическую секцию и использовать параметр send block time = 0. Аналогично если имеется несколько "читателей", то каждый вызов функций API для чтения из буфера (таких как xStreamBufferReceive()) необходимо оборачивать в критическую секцию и использовать параметр receive block time = 0.

Первые шаги

В исходном файле FreeRTOS/Demo/Common/Minimal/StreamBufferInterrupt.c приведён хорошо комментированный пример использования буфера потока для пересылки данных из обработчика прерывания в задачу RTOS.

См. раздел API stream buffer пользовательской документации для списка функций API, относящихся к буферам потока. Большая часть страниц содержит также фрагменты кода, показывающие, как эти функции могут быть использованы.

Блокирующее чтение и пороговый уровень - Blocking Reads and Trigger Levels

Для чтения данных из буфера потока из задачи RTOS используется вызов xStreamBufferReceive(), а из обработчика прерывания -  вызов xStreamBufferReceiveFromISR().

xStreamBufferReceive() позволяет указывать время, на которое задача RTOS может быть заблокирована. Если указано не нулевое время ожидания, когда задача использует xStreamBufferReceive() для чтения из буфера потока, который оказывается пустым в момент перехода задачи в заблокированное состояние (т.е. не использующее процессорное время и позволяющее выполняться другим задачам), пока в буфере потока не появится некоторое установленное количество данных, либо не истечёт указанное время блокировки задачи. Количество данных, которые должны быть помещены в буфер потока для того, чтобы ожидающая их задача была разблокирована, называется пороговым уровнем буфера потока (stream buffer's Trigger Level). Например:

  • Если задача заблокирована при чтении пустого буфера потока, у которого пороговый уровень равен 1, то задача будет разблокирована, когда один байт будет записан в буфер, либо когда истечёт время ожидания.
  • Если задача заблокирована при чтении из пустого буфера потока, у которого пороговый уровень задан равным 10, то задача не будет разблокирована, пока в буфере потока не появится как минимум 10 байт. Либо, как и в других случаях, пока не истечёт время ожидания.

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

Примечания:

  • Недопустимо устанавливать пороговый уровень, равным нулю. При попытке установить 0, результат будет таким же, как при установке порогового уровня, равного 1.
  • Также недопустимо устанавливать пороговый уровень больше, чем размер буфера потока.

Пороговый уровень буфера потока устанавливается при создании буфера потока, и может быть впоследствие изменён вызовом функции API xStreamBufferSetTriggerLevel().

Блокирующая запись

Для записи данных в буфер потока из задачи RTOS используется вызов функции API xStreamBufferSend(), а из обработчика прерывания xStreamBufferSendFromISR().

Если функция API xStreamBufferSend() вызывается с ненулевым временем ожидания для записи в буфер потока, который в настоящий момент полон, вызывающая задача перейдёт в заблокированное состояние (т.е. не будет использовать процессорное время, и другие задачи в это время смогут работать) до тех пор, пока в буфере не появится место. Ну или закончится указанное в вызове время ожидания.

Макросы завершения отправки и завершения приёма (for multicore use)

sbSEND_COMPLETED() и sbSEND_COMPLETED_FROM_ISR()

sbSEND_COMPLETED() это макрос, который вызывается (внутри функций API FreeRTOS), когда данные записываются в буфер потока. У него один параметр: дескриптор буфера потока, который был только что обновлён.

sbSEND_COMPLETED() проверяет, есть ли задача, заблокированная на ожидании данных из буфера потока, и, если так, выводит эту задачу из заблокированного состояния.

Программист может изменить эту логику работы по-умолчанию путём замены макроса sbSEND_COMPLETED() на макрос собственной реализации в файле FreeRTOSConfig.h. Это полезно, когда буфер потока используется для пересылки данных между ядрами на многоядерном процессоре. В этом сценарии sbSEND_COMPLETED() может быть реализован для выработки прерывания в другом ядре процессора, и обработчик прерывания может тогда использовать функцию API xStreamBufferSendCompletedFromISR(), чтобы проверить, и при необходимости разблокировать задачу, которая ожидала данные в буфере потока. В исходном файле FreeRTOS/Demo/Common/Minimal/MessageBufferAMP.c представлен хорошо комментированный пример использования именно такого сценария.

sbRECEIVE_COMPLETED() и sbRECEIVE_COMPLETED_FROM_ISR()

Макрос sbRECEIVE_COMPLETED() имеет назначение, похожее на sbSEND_COMPLETED(), но для приёма, а не для передачи. Он вызывается (внутри функций API FreeRTOS), когда данные считываются из буфера потока. Макрос проверяет, есть ли задача, заблокированная на ожидании свободного места в буфере потока, и если так, то выводит эту задачу из заблокированного состояния. Так же, как и sbSEND_COMPLETED(), логика работы по-умолчанию для макроса sbRECEIVE_COMPLETED() может быть изменена путём включения другой реализации в файл FreeRTOSConfig.h.

Hobby's category: