Transcript

FreeRTOS

Философия разработки

FreeRTOS разработана как:- Простая- Портируемая-Маленькая

Система FreeRTOS находится в стадии активной разработки, которая была начата Ричардом Барри (Richard Barry) в 2002 году.

Некоторые возможности FreeRTOS• Выбор политики планирования• Всегда работает задача с наивысшим приоритетом

из доступных. Задачи с одинаковым приоритетом делят процессорное время.

• Coroutines(сопрограммы) - маленькие задачи, использующие очень мало RAM.

• БОльшая часть кода одинакова для всех средств разработки.

• Много портов и примеров.• Дополнительные возможности могут быть легко и

быстро добавлены.

• Многозадачность и параллельностьОбычный процессор может выполнять только одну задачу одновременно

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

• ПланированиеПланировщик - это часть ядра, отвечающий за то, какая задача должна

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

ПланированиеКаждая задача имеет приоритет, назначенный пользователем, который равен от 0 (самый низкий приоритет) и до значения времени компиляции configMAX_PRIORITIES-1 (самый высокий приоритет)

Планировщик гарантирует, что процессорное время отдаётся задаче с максимальным приоритетом из доступных(ready) задач. Даже если задачи с низким приоритетом тоже находятся в состоянии готовности(ready) длительное время.

Тактовая частота системы

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

/* Поиск очереди с наивысшим приоритетом, в которой есть готовые к запуску задачи. */

while( listLIST_IS_EMPTY( &( pxReadyTasksLists [uxTopReadyPriority ] ) ) ) {

configASSERT( uxTopReadyPriority );--uxTopReadyPriority; } /*

}

/* listGET_OWNER_OF_NEXT_ENTRY проходит по списку, поскольку задачи с одинаковым приоритетом получают одинаковое право пользоваться процессорным временем. */

listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopReadyPriority ] ) );

ЗадачиЗадачей является определяемая пользователем функция на языке C с заданным приоритетом. В tasks.c и task.h делается вся тяжелая работа по созданию, планированию и обслуживанию задач. Простая

Нет ограничений использования

Поддерживает полное вытеснение

Приоритет задаётся всей задаче

Каждая задача требует отдельного стека - это приводит к большому потреблению оперативной памяти

Повторная входимость должна быть тщательно продумана, если используется вытеснение

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

Совместная работа упрощает проблему повторного входа.

Хорошо переносимо между разными архитектурами

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

Отсутствие индивидуального стека требует особого рассмотрения

Ограниченный API

Кооперативная многозадачность только между сопрограммами.

Объявление задач• Задача должна иметь следующую структуру:

void vATaskFunction( void *pvParameters ){ for ( ; ; )

{ /* Код задачи пишется тут */ } }

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

Задачи создаются с помощью xTaskCreate() и удаляются с помощью vTaskDelete()

Блок управления задачей TCB (tasks.c) typedef struct tskTaskControlBlock {

volatile portSTACK_TYPE *pxTopOfStack; /* Указывает на месторасположение последнего элемента, размещенного в стеке задач. */ xListItem xGenericListItem; /* Элемент списка, используемый для помещения блока TCB в

очереди готовых и заблокированных задач. */ xListItem xEventListItem; /* Элемент списка, используемый для помещения блока TCB в

списки событий.*/ unsigned portBASE_TYPE uxPriority; /* Приоритет задачи; 0 является низшим приоритетом.*/ portSTACK_TYPE *pxStack; /* Указывает на начало стека. */ signed char pcTaskName[ configMAX_TASK_NAME_LEN ]; /* Описательное имя, которое

присваивается стеку, когда он создается. Используется только для отладки. */ #if ( portSTACK_GROWTH > 0 )

portSTACK_TYPE *pxEndOfStack; /* Используется для проверки стека на переполнение в тех архитектурах, где стек растет с младших адресов памяти. */ #endif #if ( configUSE_MUTEXES == 1 )

unsigned portBASE_TYPE uxBasePriority; /* Приоритет, назначенный задаче последним – используется механизм наследования

приоритетов. */ #endif

} tskTCB;

Списки

СпискиСписок в системе FreeRTOS является стандартным закольцованным двусвязным

списком с парой интересных дополнений.

struct xLIST_ITEM { portTickType xItemValue; /* Значение, помещаемое в список. В

большинстве случае используется для сортировки списка в порядке уменьшения значений */ volatile struct xLIST_ITEM * pxNext; /* Указатель на следующий

элемент xListItem в списке. */ volatile struct xLIST_ITEM * pxPrevious; /* Указатель на предыдущий элемент xListItem в списке. */ void * pvOwner; /* Указатель на объект (обычно блок TCB), в котором находится элемент списка. Таким образом, организуется двусвязный список между объектами, хранящимися в списке, и элементами самого списка. */ void * pvContainer; /* Указатель на список (если таковой имеется), в который этот элемент списка помещается. */

};

void * pvContainer это:

typedef struct xLIST {

volatile unsigned portBASE_TYPE uxNumberOfItems; volatile xListItem * pxIndex; /* Используется для прохода по списку. Указывает на последний элемент, возвращенный функцией

pvListGetOwnerOfNextEntry (). */ volatile xMiniListItem xListEnd; /* Элемент списка, в

котором находится максимально возможное значение, означающее, что он всегда находится в конце списка и, поэтому, используется как маркер. */

} xList;Размер списка в любое время хранится в переменной uxNumberOfItems, предназначенной для быстрого выполнения операций с размерами списков.

Списки

• Поскольку списки сортируются в порядке убывания, элемент xListEnd используется как маркер начала списка. А поскольку список кольцевой, этот элемент xListEnd также является маркером конца списка.

• В большинстве «традиционных» операций доступа к списку, которыми мы пользуемся, вся работа выполняется в одном цикле for() или в функции, вызываемой следующим образом:

for (listPtr = listStart; listPtr != NULL; listPtr = listPtr->next) {

// Что-то здесь делается с указателями listPtr ... }

Каждый объект struct xListItem является, на самом деле, объектом xGenericListItem из соответствующего блока TCB.

Очереди

Система FreeRTOS позволяет задачам с помощью очередей общаться и синхронизироваться друг с другом. Процедуры сервиса прерываний (ISR) также используют очереди для взаимодействий и синхронизации.

Базовая структура данных очереди выглядит следующим образом:

typedef struct QueueDefinition {

signed char *pcHead; /* Указывает на начало области хранения очереди. */ signed char *pcTail; /* Указывает на байт в конце области хранения очереди. Еще один байт требуется поскольку хранятся отдельные элементы очереди; он используется как маркер. */ signed char *pcWriteTo; /* Указывает на следующее свободное место в в области

хранения очереди. */ signed char *pcReadFrom; /* Указывает на последнюю позицию, откуда происходило чтение очереди. */ xList xTasksWaitingToSend; /* Список задач, которые блокированы, ожидая пока не

произойдет обращение к этой очереди; Запомнены в порядке приоритета. */ xList xTasksWaitingToReceive; /* Список задач, которые блокированы, ожидая пока

не произойдет чтение из этой очереди; Запомнены в порядке приоритета. */ volatile unsigned portBASE_TYPE uxMessagesWaiting; /* Количество элементов,

имеющихся в очереди в текущий момент. */ unsigned portBASE_TYPE uxLength; /* Длина очереди, определяемая как количество

элементов, находящихся в очереди, а не как количество байтов памяти, занимаемой очередью. */ unsigned portBASE_TYPE uxItemSize; /* Размер каждого элемента, который хранится

в очереди. */ } xQUEUE;

ОчередиКогда создается очередь, пользователь указывает длину очереди и размер

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

В системе FreeRTOS поддерживаются вставки и удаления в очереди с блокировкой и без блокировки.

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

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

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

Семафоры

Механизм семафоров основан на механизме очередей. По большому счету API-функции для работы с семафорами представляют собой макросы — «обертки» других API-функций для работы с очередями.

Семафор должен быть явно создан перед первым его использованием.

Двоичный семафорДвоичные семафоры предназначены для эффективной

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

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

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

Счетный семафор

Существует два основных применения счетных семафоров:• 1. Подсчет событий. В этом случае обработчик прерывания будет

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

• 2. Управление доступом к ресурсам. В этом случае значение счетного семафора представляет собой количество доступных ресурсов. Для получения доступа к ресурсу задача должна сначала получить (захватить) семафор — это уменьшит значение семафора на единицу. Когда значение семафора станет равным нулю, это означает , что доступных ресурсов нет. Когда задача завершает работу с данным ресурсом, она отдает семафор — увеличивает его значение на единицу.

Мьютекс

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

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

Реализация семафоров и мьютексов

В системе FreeRTOS реализован N-элементный семафор в виде очереди, в которой может быть N элементов.

Семафор следит за тем, сколько записей в текущий момент помещено в очередь, что осуществляется с помощью поля uxMessagesWaiting, имеющегося в очереди.

Семафор реализует «чистую синхронизацию» так, как это названо в вызовах заголовочного файла semphr.h системы FreeRTOS. Поэтому размер элемента в очереди указан равным нулю байтов (uxItemSize == 0). Каждое обращение к семафору увеличивает или уменьшает на единицу значение поля uxMessagesWaiting; копирование элементов очереди или данных выполнять не требуется.

Реализация семафоров и мьютексов

Точно также, как и семафоры, мьютексы реализованы в виде очередей, но в них с помощью определений #defines перегружены несколько полей xQUEUE:

/* Перепределение полей структуры xQUEUE. */ #define uxQueueType pcHead #define pxMutexHolder pcTail

Поскольку мьютекс не хранит никаких данных в очереди, ему не нужна внутренняя память, и, поэтому, не нужны поля pcHead и pcTail. Система FreeRTOS устанавливает поле uxQueueType (в действительности поле pcHead) равным 0, указывая, что эта очередь используется для мьютекса.

Список используемой литературы

1. http://wiki.fh-up.ru/?title=FreeRTOS2. http://rus-linux.net/MyLDP/BOOKS/Architecture-Open-Sourc

e-Applications/Vol-2/freertos-01.html3. http://robot-develop.org/archives/2777

top related