В этой статье будут рассмотрены сигналы, являющиеся простейшими механизмами взаимодействия между задачами в Nucleus SE. Они предоставляют малозатратный способ передачи простых сообщений между задачами.
Сигналы отличаются от всех других типов объектов ядра тем, что они не автономны: сигналы связаны с задачами и без них существовать не могут. Если приложение сконфигурировано на использование сигналов, то у каждой задачи имеется набор из восьми сигнальных флагов.
Любая задача может устанавливать сигналы другой задачи. Считывать сигналы может только задача-владелец сигнала. В процессе чтения сигналы сбрасываются. Задачи не могут читать или сбрасывать сигналы других задач.
В Nucleus RTOS существует средство, которое позволяет задачам назначать функции, которые запускаются, когда другая задача устанавливает один или более сигнальных флагов. Это чем-то напоминает процедуру обработки прерывания. Такая возможность не поддерживается в Nucleus SE, здесь задачи должны запрашивать сигнальные флаги явным образом.
Как и в большинстве объектов Nucleus SE, настройка сигналов определяется директивами #define в nuse_config.h. Основным параметром является NUSE_SIGNAL_SUPPORT, который активирует поддержку функциональности (для всех задач в приложении). Вопрос об указании числа сигналов не стоит: на каждую задачу выделяется по 8 флагов.
Установка этого разрешающего параметра служит главным активатором сигналов. Это обеспечивает строго определенную структуру данных, имеющую соответствующий размер. Кроме того, этот параметр активирует настройки API.
Каждая функция API (служебный вызов) в Nucleus SE активируется директивой #define в nuse_config.h. Для сигналов к ним относятся:
NUSE_SIGNALS_SEND
NUSE_SIGNALS_RECEIVE
По умолчанию, им присвоено значение FALSE, отключая, таким образом, каждый служебный вызов и препятствуя включению реализующего их кода. Для настройки сигналов в приложении необходимо выбрать нужные вызовы API и присвоить соответствующим директивам значение TRUE.
Ниже приведена выдержка из файла nuse_config.h по умолчанию:
#define NUSE_SIGNAL_SUPPORT FALSE /* Enables support for signals */
#define NUSE_SIGNALS_SEND FALSE /* Service call enabler */
#define NUSE_SIGNALS_RECEIVE FALSE /* Service call enabler */
Активированная функция API при выключенной поддержке средств сигналов приведет к ошибке компиляции. Если ваш код использует вызов API, который не был активирован, возникнет ошибка компоновки, так как код реализации не был включен в приложение. Само собой, включение двух функций API в некотором роде излишне, так как в активировании поддержки сигналов нет смысла при отсутствии этих API. Активаторы были добавлены для совместимости с другими функциями Nucleus SE.
Nucleus RTOS поддерживает четыре служебных вызова относящихся к сигналам, которые предоставляют следующий функционал:
Реализация каждого из этих вызовов подробно рассматривается ниже.
Фундаментальные операции, которые могут быть выполнены над набором сигналов задачи – отправка данных (может быть выполнено любой задачей) и чтение данных (следовательно, и очищение данных, может быть выполнено только задачей-владельцем). Nucleus RTOS и Nucleus SE предоставляют два базовых вызова API для этих операций, которые будут описаны ниже.
Так как сигналы являются битами, их лучше всего визуализировать как двоичные числа. Так как стандарт С исторически не поддерживает представление двоичных констант (только восьмеричных и шестнадцатеричных), Nucleus SE имеет полезный заголовочный файл nuse_binary.h, который содержит символы #define вида b01010101 для всех 256 8-битных значений. Ниже приведена выдержка из файла nuse_binary.h:
#define b00000000 ((U8) 0x00)
#define b00000001 ((U8) 0x01)
#define b00000010 ((U8) 0x02)
#define b00000011 ((U8) 0x03)
#define b00000100 ((U8) 0x04)
#define b00000101 ((U8) 0x05)
Любая задача может отправлять сигналы любой другой задаче в приложении. Отправка сигналов подразумевает установку одного или нескольких сигнальных флагов. Это операция ИЛИ (OR), которая не влияет на установленные ранее флаги.
STATUS NUSE_Signals_Send(NUSE_TASK task, U8 signals)
{
#if NUSE_API_PARAMETER_CHECKING
if (task >= NUSE_TASK_NUMBER)
{
return NUSE_INVALID_TASK;
}
#endif
NUSE_CS_Enter();
NUSE_Task_Signal_Flags[task] |= signals;
NUSE_CS_Exit();
return NUSE_SUCCESS;
}
Код очень прост. После любой проверки параметров значения сигналов проходят через операцию ИЛИ на сигнальные флаги указанной задачи. Блокировка задач на сигналы не влияет.
Задача может читать только собственный набор сигнальных флагов. В процессе чтения значения флагов сбрасываются.
U8 NUSE_Signals_Receive(void)
{
U8 signals;
NUSE_CS_Enter();
Signals = NUSE_Task_Signal_Flags[NUSE_Task_Active];
NUSE_Task_Signal_Flags[NUSE_Task_Active] = 0;
NUSE_CS_Exit();
return signals;
}
Код очень прост. Значение флагов копируется, начальное значение сбрасывается, а копия возвращается функцией API. Блокировка задач не влияет на сигналы.
Так как сигналы не являются самостоятельными объектами, использование памяти зависит от задач, которым они принадлежат. Ниже приведена некоторая информация для полноты понимания. Сигналы используют одну структуру данных (в ОЗУ), которая, как и другие объекты Nucleus SE, является таблицей, размеры которой соответствуют количеству задач в приложении. Эта структура данных используется только если включена поддержка сигналов.
Я настоятельно рекомендую чтобы код приложения не обращался к этой структуре данных напрямую, а использовал имеющиеся функции API. Это позволяет избежать несовместимости с будущими версиями Nucleus SE, нежелательных побочных эффектов, а также упрощает портирование приложения на Nucleus RTOS. Структура данных подробно рассмотрена ниже для упрощения понимания принципов работы служебных вызовов и отладки.
NUSE_Task_Signal_Flags[] – массив типа U8 с одной записью на каждую сконфигурированную задачу, в этом массиве хранятся сигнальные флаги.
Эта структура данных инициализируется нулями функцией NUSE_Init_Task() при загрузке Nucleus SE.
Как и для всех объектов ядра Nucleus SE, объем требуемой памяти для сигналов предсказуем.
Объем данных в ОЗУ для всех сигналов в приложении равен 0.
Объем памяти для хранения данных в ПЗУ (в байтах) для всех сигналов в приложении равен количеству сконфигурированных задач (NUSE_TASK_NUMBER). Но фактически, эти данные принадлежат задачам и описаны в предыдущей статье о задачах.
Этот вызов API устанавливает процедуру обработки сигналов (функцию) для текущей задачи. В Nucleus SE в этом нет необходимости, так как обработчики сигналов не поддерживаются.
Эта служба активирует и/или деактивирует сигналы для текущей задачи. Для каждой задачи доступно 32 сигнала. Каждый сигнал представлен битом в signal_enable_mask. Добавление бита в signal_enable_mask включает соответствующий сигнал, а удаление бита его отключает.
При разработке Nucleus SE моей целью было сохранить максимальный уровень совместимости программного кода с Nucleus RTOS. Сигналы не являются исключением, и, с точки зрения разработчика, они реализованы практически таким же образом, как и в Nucleus RTOS. Существуют некоторые несовместимости, которые я посчитал допустимыми, учитывая, что финальный код гораздо проще для понимания и может более эффективно использовать память. В остальном, вызовы Nucleus RTOS API могут быть практически напрямую перенесены на вызовы Nucleus SE.
В Nucleus RTOS задачи могут иметь по 32 сигнальных флага. В Nucleus SE я решил уменьшить их количество до восьми, так как этого будет достаточно для более простых приложений и позволяет экономить ресурсы ОЗУ. В случае необходимости сигналы могут быть полностью отключены.
Nucleus RTOS поддерживает четыре служебных вызова для работы с сигналами. Из них, два не были реализованы в Nucleus SE. Их описание можно найти выше, в разделе «Нереализованные вызовы API».
Об авторе: Колин Уоллс уже более тридцати лет работает в сфере электронной промышленности, значительную часть времени уделяя встроенному ПО. Сейчас он – инженер в области встроенного ПО в Mentor Embedded ( подразделение Mentor Graphics). Колин Уоллс часто выступает на конференциях и семинарах, автор многочисленных технических статей и двух книг по встроенному ПО. Живет в Великобритании. Профессиональный блог Колина: https://blogs.mentor.com/colinwalls/, e-mail: colin_walls@mentor.com