Переменные секции (section variables) это переменные, которые помещаются в именованную секцию памяти различными независимыми частями кода (например файлами или модулями), чтобы они могли совместно использоваться. Модуль переменных секции (section variables module) упрощает обработку таких переменных, реализуя абстракцию от кода, требуемого определенным компилятором.
Общее использование переменных происходит во время компиляции приложения, compile time (или если быть более точным, то во время линковки, link time). Один модуль (регистратор) создает именованную секцию, которая может хранить переменные. Другие модули (которые регистрируются) могут после этого зарегистрировать переменные, чтобы поместить из в эту секцию во время процесса линковки приложения. К каждой такой переменной может получить доступ как регистратор, так и код, зарегистрировавший переменную.
Основные причины использования переменных секции:
• Разобщение кода: переменные секции декларируются в коде, который их регистрирует, без необходимости менять код регистратора.
• Прозрачность: регистрирующий код может получить доступ к переменным секции точно так же, как и к любым другим переменным. • Управление доступом: переменные могут быть декларированы как static кодом, который их регистрирует, и к ним все еще может обращаться регистратор. • Простота в использовании: регистратор может обращаться ко всем зарегистрированным переменным через макрос, предоставленный в модуле section variables. • Нет дубликатов переменных: переменная, к которой обращается регистратор, эта та же самая переменная, которую создал регистрирующий код, не её копия. • Эффективность использования памяти: требуемое пространство хранение динамически подстраивается во время процесса линковки (link time), но остается фиксированным во время выполнения приложения (run time).
Типовой пример использование переменных секции - инициализация модуля, который допускает регистрацию нескольким пользователям, чтобы они могли использовать общий ресурс, и обслуживание доступа к этому ресурсу (хороший пример - энергонезависимое хранилище Flash Storage [2] в памяти программ FLASH микроконтроллера). Каждый пользователь общего ресурса предоставляет конфигурационную структуру, хранящуюся в его модуле. Без использования переменных секции, эти структуры могли бы динамически регистрироваться run time, или обрабатываться через общий заголовочный файл. С регистрацией run-time модуль никогда не будет знать конечное количество регистрантов. С общим файлом заголовка Вы можете изменить код регистратора (код регистратора может быть библиотекой, который не желательно было бы менять). С переменными секции количество регистрантов известно сразу же в момент запуска приложения, и нужно будет изменить только лишь код регистранта.
Примечание: регистратор это модуль, который управляет доступом к общему ресурсу (например память FLASH), он один. Регистрант это модуль, который использует общий ресурс (таких модулей может быть несколько).
Например, переменные секции используются модулями энергонезависимого хранилища Flash Storage [2] и облегченной файловой системой Flash Data Storage [3], чтобы сконфигурировать свои области используемых страниц FLASH (и некоторые другие параметры).
Следующий пример кода показывает, как использовать переменные секции в приложении. В типовом случае использования существует один модуль регистратора, который создает именованную секцию и использует данные в ней, а также один или большее количество модулей-регистрантов, которые регистрируют свои данные (переменные) в этой именованной секции.
[Создание именованной секции]
Создание именованной секции (named section) потребует выполнения двух шагов:
1. В коде модуля регистратора создайте и секцию и присвойте ей имя (в примере ниже это section_name), с указанием типа данных переменной, которые будут сохраняться в этой секции (в примере ниже это data_type_t):
NRF_SECTION_DEF(section_name, data_type_t);
Обратите внимание, что все переменные в именованной секции должны иметь одинаковый тип.
2. Если Вы используете компилятор GCC, то укажите линкеру, что существует именованная секция. Для этого добавьте следующее определение в корневой уровень скрипта линкера, который используется при компиляции (замените section_name реальным именем Вашей секции):
SECTIONS
{
.section_name :
{
PROVIDE(__start_section_name = .);
KEEP(*(.section_name))
PROVIDE(__stop_section_name = .);
} > RAM
} INSERT AFTER .data;
[Регистрация набора переменных секции]
В коде модуля-регистранта используйте макрос NRF_SECTION_ITEM_REGISTER, чтобы зарегистрировать переменную секции. Следующий пример кода показывает, как зарегистрировать переменную var_name типа data_type_t в именованной секции section_name, и присвоить ей значение var_value:
NRF_SECTION_ITEM_REGISTER(section_name, data_type_t var_name) = {var_value};
Если регистрант не требует обновления значения переменной секции, то определите эту переменную с модификатором const. Если это выполняется для всех переменных секции, которые помещаются в эту именованную секцию, то секция будет помещена в память FLASH:
NRF_SECTION_ITEM_REGISTER(section_name, const data_type_t var_name ) = {var_value};
Для упрощения регистраций Вы можете определить макрос, которой заполнит имя именованной секции. Вот соответствующий пример, который можно увидеть в модуле Flash Storage [2]:
#define FS_REGISTER_CFG(cfg_var) NRF_SECTION_ITEM_REGISTER(fs_data, cfg_var)
[Использование переменных секции]
Модуль section variables предоставляет несколько макросов, которые регистратор может использовать для получения переменных секции и информации об именованной секции и переменных, которые она содержит.
Следующий пример кода показывает, как сделать итерацию по всем переменным, зарегистрированным в именованной секции, чтобы инициализировать модуль регистратора для всех модулей-регистрантов:
static void init_func(void)
{
int vars_cnt = NRF_SECTION_ITEM_COUNT(section_name, data_type_t);
for (int i = 0; i < vars_cnt; i++)
{
data_type_t * p_var_name = NRF_SECTION_ITEM_GET(section_name, data_type_t, i);
// Добавьте сюда код инициализации:
...
}
}
Примечание: при использовании этого модуля невозможно управлять порядком следования переменных в именованной секции, чтобы этот порядок сохранялся между версиями компилятора и между событиями компиляции. Такая функция предоставляется итератором переменных секции [4].
[Ссылки]
1. nRF5 SDK v13 Experimental Section variables site:nordicsemi.com. 2. nRF5 SDK Flash Storage. 3. nRF5 SDK Flash Data Storage. 4. nRF5 SDK Experimental Section variables iterator site:nordicsemi.com. |