Nucleus RTOS имеет четыре вызова API, которые предоставляют вспомогательные функции связанные с очередями: сброс очереди, получение информации об очереди, получение количества очередей в приложении и получение указателей на все очереди в приложении. Первые три функции реализованы в Nucleus SE.
Код функции NUSE_Queue_Reset (после проверки параметров) довольно прост. Индексам головы и хвоста очереди, а также счетчику сообщений в очереди присваивается нулевое значение.
Если блокировка задач активирована, дополнительный код отвечает за восстановление приостановленных задач:
while (NUSE_Queue_Blocking_Count[queue] != 0)
{
U8 index; /* check whether any tasks are blocked */
/* on this queue */
for (index=0; index<NUSE_TASK_NUMBER; index++)
{
if ((LONIB(NUSE_Task_Status[index]) == NUSE_QUEUE_SUSPEND)
&& (HINIB(NUSE_Task_Status[index]) == queue))
{
NUSE_Task_Blocking_Return[index] = NUSE_QUEUE_WAS_RESET;
NUSE_Task_Status[index] = NUSE_READY;
break;
}
}
NUSE_Queue_Blocking_Count[queue]--;
}
#if NUSE_SCHEDULER_TYPE == NUSE_PRIORITY_SCHEDULER
NUSE_Reschedule(NUSE_NO_TASK);
#endif
Каждой приостановленной задаче в очереди присваивается статус «готова» с кодом возврата NUSE_QUEUE_WAS_RESET. После того, как этот процесс завершен, если используется планировщик Priority, вызывается функция NUSE_Reschedule(), так как одна или несколько задач с высоким приоритетом могут быть готовыми к выполнению.
STATUS NU_Queue_Information(NU_QUEUE *queue, CHAR *name, VOID **start_address, UNSIGNED *queue_size, UNSIGNED *available, UNSIGNED *messages, OPTION *message_type, UNSIGNED *message_size, OPTION *suspend_type, UNSIGNED *tasks_waiting, NU_TASK **first_task);
queue – указатель на предоставленный пользователем блок управления очередью;
name – указатель на 8-символьную область для имени сообщения в очереди;
start_address – указатель на указатель, в который будет записан адрес начала области данных очереди;
queue_size – указатель на переменную для хранения общего количества элементов типа UNSIGNED в очереди;
available – указатель на переменную для хранения количества доступных элементов UNSIGNED в очереди;
messages – указатель на переменную для хранения текущего количества сообщений в очереди;
message_type – указатель на переменную для хранения типа сообщений, поддерживаемых очередью. Допустимые значения — NU_FIXED_SIZE и NU_VARIABLE;
message_size – указатель на переменную для хранения количества элементов данных типа UNSIGNED в каждом сообщении очереди. Если очередь поддерживает сообщения переменной длины, это число показывает максимальную длину сообщения;
suspend_type – указатель на переменную для хранения типа приостановки задач. Допустимые значения — NU_FIFO и NU_PRIORITY;
tasks_waiting – указатель на переменную для хранения количества приостановленных на этой очереди задач;
first_task – указатель на указатель задачи, в который помещается указатель первой приостановленной задачи.
STATUS NUSE_Queue_Information(NUSE_QUEUE queue, ADDR *start_address, U8 *queue_size, U8 *available, U8 *messages, U8 *tasks_waiting, NUSE_TASK *first_task);
queue – индекс очереди, о которой запрашивается информация;
start_address – указатель на переменную типа ADDR, в которой будет храниться адрес начала области данных очереди;
queue_size – указатель на переменную типа U8, в которой будет храниться общее количество сообщений, способное поместиться в очередь;
available – указатель на переменную типа U8, в которой будет храниться количество свободных мест в очереди;
messages – указатель на переменную типа U8, в которой будет храниться текущее количество сообщений в очереди;
tasks_waiting – указатель на переменную, в которой будет храниться количество задач, приостановленных на этой очереди (ничего не возвращается, если блокировка задач отключена);
first_task – указатель на переменную типа NUSE_TASK, в которой будет храниться индекс первой приостановленной задачи (ничего не возвращается, если блокировка задач отключена).
*start_address = NUSE_Queue_Data[queue];
*queue_size = NUSE_Queue_Size[queue];
*available = NUSE_Queue_Size[queue] - NUSE_Queue_Items[queue];
*messages = NUSE_Queue_Items[queue];
#if NUSE_BLOCKING_ENABLE
*tasks_waiting = NUSE_Queue_Blocking_Count[queue];
if (NUSE_Queue_Blocking_Count[queue] != 0)
{
U8 index;
for (index=0; index<NUSE_TASK_NUMBER; index++)
{
if ((LONIB(NUSE_Task_Status[index]) ==
NUSE_QUEUE_SUSPEND)
&& (HINIB(NUSE_Task_Status[index]) == queue))
{
*first_task = index;
break;
}
}
}
else
{
*first_task = 0;
}
#else
*tasks_waiting = 0;
*first_task = 0;
#endif
Функция возвращает статус очереди. Затем, если блокировка задач активирована, возвращается количество ожидающих задач и индекс первой из них (в противном случае, обоим параметрам присваивается значение 0).
Этот служебный вызов возвращает количество очередей, сконфигурированных в приложении. В Nucleus RTOS их количество со временем может меняться, а возвращаемое значение будет показывать текущее количество очередей. В Nucleus SE возвращаемое значение задается на этапе сборки и не может измениться.
Очереди используют пять или шесть структур данных (которые находятся либо в ОЗУ, либо в ПЗУ), являющихся наборами таблиц (как и другие объекты Nucleus SE), количество и размер которых соответствует количеству очередей в приложении и выбранным параметрам.
NUSE_Queue_Head[] – массив указателей типа U8, имеет одну запись для каждой сконфигурированной очереди и указывает на голову очереди сообщений. Используется в качестве индекса адресов в NUSE_Queue_Data[] (см.ниже);
NUSE_Queue_Tail[] – массив типа U8, имеет одну запись для каждой сконфигурированной в приложении очереди и указывает на хвост очереди сообщений. Используется в качестве индекса адресов в NUSE_Queue_Data[] (см. ниже);
NUSE_Queue_Items[] – массив типа U8, имеет одну запись для каждой сконфигурированной очереди и является счетчиком сообщений в очереди. Эти данные можно считать излишними, так как эти значения можно получить через индексы начала и конца очереди, однако хранение счетчика упрощает код;
NUSE_Queue_Blocking_Count[] – этот массив типа U8 содержит счетчики количества задач, приостановленных на каждой очереди. Этот массив создается, только если активирована поддержка блокировки задач.
RAM U8 NUSE_Queue_Head[NUSE_QUEUE_NUMBER];
RAM U8 NUSE_Queue_Tail[NUSE_QUEUE_NUMBER];
RAM U8 NUSE_Queue_Items[NUSE_QUEUE_NUMBER];
#if NUSE_BLOCKING_ENABLE
RAM U8 NUSE_Queue_Blocking_Count[NUSE_QUEUE_NUMBER];
#endif
Пользователь ответственен за предоставление области ОЗУ для хранения каждой очереди. Размер этой области должен вмещать массив типа ADDR, в котором каждая запись соответствует одному сообщению в очереди.
NUSE_Queue_Data[] – массив типа ADDR, имеет одну запись для каждой сконфигурированной очереди и указывает на область данных очереди (см. Пользовательские данные ОЗУ);
NUSE_Queue_Size[] – массив типа U8, имеет одну запись для каждой сконфигурированной очереди и показывает максимальное количество сообщений, которое может принять каждая очередь.
ROM ADDR *NUSE_Queue_Data[NUSE_QUEUE_NUMBER] =
{
/* addresses of queue data areas ------ */
};
ROM U8 NUSE_Queue_Size[NUSE_QUEUE_NUMBER] =
{
/* queue sizes ------ */
};
Объем данных ядра в ОЗУ (в байтах) для всех очередей в приложении при активированной блокировке задач вычисляется следующим образом:
STATUS NU_Create_Queue(NU_QUEUE *queue, char *name, VOID *start_address, UNSIGNED queue_size, OPTION message_type, UNSIGNED message_size, OPTION suspend_type);
queue – указатель на предоставленный пользователем блок управления, используется для управления очередями в других вызовах API;
name – указатель на 7-символьное имя очереди с нулевым терминирующим байтом;
start_address – адрес начала очереди;
message_type – тип сообщения, поддерживаемый очередью. Может принимать значения NU_FIXED_SIZE или NU_VARIABLE_SIZE;
message_size – если очередь поддерживает сообщения фиксированной длины, этот параметр задает точную длину каждого сообщения, в противном случае, если очередь поддерживает сообщения переменной длины, это значение является максимальной длиной сообщения;
suspend_type – определяет тип приостановки задач в очереди. Может принимать значения NU_FIFO и NU_PRIORITY, которые означают принцип FIFO (First-In-First-Out) или принцип приоритета приостановки задач, соответственно.
NU_SUCCESS – вызов был успешно завершен;
NU_INVALID_QUEUE – нулевой указатель на блок управления очередью (NULL), или указатель уже используется;
NU_INVALID_MEMORY – некорректная область памяти, указанная в start_address;
NU_INVALID_MESSAGE – некорректный параметр message_type;
NU_INVALID_SIZE – очередь не поддерживает сообщения такой длины, либо размер очереди и/или длина сообщения равна 0;
NU_INVALID_SUSPEND – некорректный параметр suspend_type.
Этот вызов API удаляет созданную ранее очередь. В Nucleus SE в этом нет необходимости, так как очереди создаются статически и не могут быть удалены.
Этот вызов API строит последовательный список указателей на все очереди в системе. В Nucleus SE в этом нет необходимости, так как очереди идентифицируются при помощи простого индекса, а не указателя.
pointer_list – указатель на массив указателей NU_QUEUE. Этот массив будет заполнен указателями на созданные в системе очереди;
maximum_pointers – максимальное количество указателей в массиве.
Этот вызов API передает сообщение всем задачам, приостановленным в очереди, которые ожидают сообщений от указанной очереди. Данная функция не реализована в Nucleus SE так как она добавляет избыточную сложность.
queue – указатель на блок управления очередью;
message – указатель на передаваемое сообщение;
size – количество элементов типа UNSIGNED в сообщении. Если очередь поддерживает сообщения переменной длины, этот параметр должен быть равен или меньше длины сообщения, поддерживаемой очередью. Если очередь поддерживает сообщения фиксированной длины, этот параметр должен быть равен длине сообщения, поддерживаемой очередью;
suspen – указывает, нужно ли приостанавливать вызывающую задачу, если очередь уже заполнена. Может принимать значения NU_NO_SUSPEND, NU_SUSPEND или значение таймаута.
Как и в случае со всеми другими объектами Nucleus SE, моей целью было обеспечение максимальной совместимости кода приложений с Nucleus RTOS. Очереди не являются исключением и, с точки зрения пользователя, они реализованы также, как и в Nucleus RTOS. Есть и определенная несовместимость, которую я посчитал допустимой, с учетом того, что в результате код станет более понятным и более эффективным с точки зрения объема требуемой памяти. В остальном, вызовы API Nucleus RTOS могут быть практически напрямую перенесены на Nucleus SE.
В Nucleus RTOS все объекты описываются структурой данных (блоками управления), который имеет определенный тип данных. Указатель на этот блок управления служит идентификатором очереди. Я решил, что в Nucleus SE для эффективного использования памяти необходим другой подход: все объекты ядра описываются набором таблиц в ОЗУ и/или ПЗУ. Размер этих таблиц определяется количеством сконфигурированных объектов каждого типа. Идентификатор конкретного объекта – индекс в этой таблице. Таким образом, я определил NUSE_QUEUE в качестве эквивалента U8, переменная (а не указатель) этого типа служит идентификатором очереди. С этой небольшой несовместимостью легко справиться, если код портируется с Nucleus SE на Nucleus RTOS и наоборот. Обычно над идентификаторами объектов не выполняются никакие операции, кроме перемещения и хранения.
Nucleus RTOS также поддерживает присваивание имен очередям. Эти имена используются только при отладке. Я исключил их из Nucleus SE, чтобы сэкономить память.
В Nucleus RTOS очередь может быть сконфигурирована таким образом, чтобы обрабатывать сообщения, состоящие из любого количества элементов типа unsigned. В Nucleus SE Очереди упрощены и поддерживают только одиночные сообщения типа ADDR. Каналы передачи данных в Nucleus SE немного более гибкие и могут стать полезной альтернативой очередям в некоторых случаях. Каналы будут рассмотрены в двух следующих статьях этого цикла.
Nucleus SE также поддерживает очереди с переменной длиной сообщений, в которых при создании указывается только максимальная длина сообщения. Сообщения переменной длины не поддерживаются Nucleus SE.
В Nucleus SE максимально количество сообщений в очереди равно 256, так как все переменные и константы имеют тип U8. Nucleus RTOS не имеет таких ограничений.
Об авторе: Колин Уоллс уже более тридцати лет работает в сфере электронной промышленности, значительную часть времени уделяя встроенному ПО. Сейчас он – инженер в области встроенного ПО в Mentor Embedded ( подразделение Mentor Graphics). Колин Уоллс часто выступает на конференциях и семинарах, автор многочисленных технических статей и двух книг по встроенному ПО. Живет в Великобритании. Профессиональный блог Колина: https://blogs.mentor.com/colinwalls/, e-mail: colin_walls@mentor.com