Flash Storage (или для краткости fstorage) это модуль для сохранения и стирания данных в постоянном хранилище, организованном в памяти FLASH микроконтроллера [1]. Этот модуль работает как обертка над SoftDevice flash API, принимая запросы на сохранение или стирание страниц с данными. Эти запросы ставятся в очередь для дальнейшей асинхронной обработки. Приложение оповещается о результатах операции через вызовы callback-функций для зарегистрированных обработчиков событий.
Fstorage автоматически делит на части большие по объему данных операции записи и стирания, позволяя тем самым коду SoftDevice обрабатывать эти запросы в перерывах между операциями радиосвязи. Неудачные операции с флэш-памятью, которые не приняты или не запланированы SoftDevice, повторяются.
Модуль Experimental: Flash Data Storage [2] использует внутри себя fstorage. Также fstorage может быть использован напрямую.
[Регистрация использования]
Модуль fstorage может использоваться разными приложениями, или разными частями приложения одновременно (но только не в нескольких потоках, т. е. не обеспечивается thread-safe функционирование). Каждый пользователь flash storage должен зарегистрироваться с модулем, чтобы запросить определенное количество страниц хранилища. Страница (page) в fstorage это физическая страница памяти FLASH микроконтроллера, размер которой зависит от модели чипа. После этого модуль дает доступ только к зарегистрированным страницам, не давая возможности обратиться к FLASH по адресам, выходящих за область зарегистрированных страниц. Все зарегистрированные пользователи используют свой собственный callback-обработчик, и принимают обновления страниц, которые они зарегистрировали для своего использования.
Регистрация использования осуществляется во время компиляции через переменные секции [3]. Пользователь должен предоставить указатель на функцию, которая будет использована для обработки событий (event callbacks), количество страниц для резервирования и приоритет, и приоритет, определяющий способ назначения пространства в памяти FLASH. Эти параметры задаются фиксировано во время компиляции (compile time), и не могут быть изменены во время работы приложения (run time).
Замечание: если Вы обновляете свое приложение, и хотите сохранить в целости имеющиеся в памяти FLASH данные приложения, то не должны менять приоритет или страницы FLASH, которые зарезервированы для каждого модуля. Чтобы добавить страницы для нового модуля, зарегистрируйте его с более низким приоритетом чем те приоритеты, которые уже используются.
Для регистрации использования fstorage вызовите макрос FS_REGISTER_CFG, который определен в заголовке fstorage.h. Поместите в приложение код, аналогичный приведенному примеру ниже, чтобы создать переменную секции fs_config, содержащую конфигурацию для fstorage:
#include "fstorage.h"
FS_REGISTER_CFG(fs_config_t fs_config) =
{
.callback = fs_evt_handler, // Функция для обработки событий (event callbacks).
.num_pages = NUM_PAGES, // Количество требуемых физических страниц памяти FLASH.
.priority = 0xFE // Приоритет использования FLASH.
};
[Обработчик события]
Следующий пример показывает декларацию обработчика событий (event handler) модуля fstorage:
static void fs_evt_handler(fs_evt_t const * const evt, fs_ret_t result)
{
if (result != FS_SUCCESS)
{
// Произошла ошибка.
}
}
[Приоритет для использования FLASH]
Для каждой регистрации модуля fstorage обязательно нужно указать приоритет, с которым fstorage назначает пространство FLASH для модуля. Для приоритета возможны значения от 0 до 255, причем 255 зарезервировано для использования модулем Peer Manager [4]. Все эти сконфигурированные приоритеты должны быть уникальны.
Flash storage назначает пространство FLASH с самым большим адресом памяти (сразу ниже загрузчика, если он присутствует) для модуля с самым присвоенным высоким приоритетом. Так что если, к примеру, у модуля 1 низкий приоритет, и ему нужна 1 страница, а у модуля 2 высокий приоритет, и ему нужно 2 страницы, то fstorage назначит эти 2 страницы для модуля 2 на самые старшие доступные адреса FLASH. Модуль 1 получит одну страницу сразу после этих двух страниц.
[Инициализация]
Следующий пример кода показывает, как инициализировать fstorage:
fs_ret_t ret = fs_init();
if (ret != FS_SUCCESS)
{
// Произошла ошибка.
}
[Сохранение и стирание данных]
Flash storage ставит в очередь запросы на операции с FLASH и взаимодействие с кодом SoftDevice, чтобы получить интервалы времени для обработки этих операций по порядку. Запросы на сохранение больших объемов данных делятся на несколько операций, чтобы оптимизировать взаимодействие с SoftDevice при получение желаемых слотов времени. Максимальное количество слов в одном сохранении может быть указано макросом FS_MAX_WRITE_SIZE_WORDS в заголовке fstorage_config.h. Запросы на стирание нескольких страниц делятся на операции стирания страниц по отдельности.
Если SoftDevice не принял запрос, то fstorage повторяет поставленную в очередь операцию до тех пор, пока SoftDevice не примет запрос. Если SoftDevice принял запрос, но при этом столкнулся с ошибкой в планировании операции FLASH и произошел таймаут (это может произойти, к примеру, из-за высокой активности BLE), то fstorage повторит запрос некоторое предварительно сконфигурированное количество раз. Это максимальное количество повторов может быть задано макросом FS_OP_MAX_RETRIES в заголовке fstorage_config.h. Зарегистрированный пользователь оповещается об успехе или отказе операции путем вызова event callback.
Следующий пример кода показывает, как сохранить данные. Предполагается, что fs_config это переменная секции, которая хранит конфигурацию fstorage:
static uint32_t data = 0xAAAAAAAA;
fs_ret_t ret = fs_store(&fs_config, fs_config.p_start_addr, &data, 1);
if (ret != FS_SUCCESS)
{
// Произошла ошибка.
}
Следующий пример кода показывает стирание страниц:
#define PAGE_SIZE_WORDS 1024
// Получение адреса страницы.
static uint32_t const * address_of_page(uint16_t page_num)
{
return fs_config.p_start_addr + (page_num * PAGE_SIZE_WORDS);
}
fs_ret_t ret;
// Стирание одной страницы (страница 0).
ret = fs_erase(&fs_config, address_of_page(0), 1);
if (ret != FS_SUCCESS)
{
// Произошла ошибка.
}
// Стирание двух страниц, начиная со страницы 3.
ret = fs_erase(&fs_config, address_of_page(3), 2);
if (ret != FS_SUCCESS)
{
// Произошла ошибка.
}
Успех или отказ каждой операции будет показан вызовами обработчика (event callbacks). Поэтому проверка кода возврата всего лишь покажет, была ли успешно вызвана функция для постановки операции в очередь, но этот код возврата ничего не будет говорить о том, выполнилась ли операция на самом деле.
[Ссылки]
1. nRF5 SDK v13 Experimental Flash Storage site:nordicsemi.com. 2. nRF5 SDK Flash Data Storage. 3. nRF5 SDK Section Variables. 4. nRF5 SDK Peer Manager. |