При разработке проекта для микроконтроллера часто возникает необходимость сохранения данных во Flash-память перед выключением устройства, например глобальные структуры, содержащие информацию о настройках различной периферии, данные с внешних датчиков и прочее. В этом посте я хочу показать простой механизм записи структуры во FLASH память микроконтроллера STM32, которым я сам часто пользуюсь в своих проектах.
Для начала рассмотрим работу с flash на контроллере STM32F103C8T6 (blue pill). Начнем с изучения документации. Смотрим, как разбита память, в reference manual, в разделе Embedded Flash memory. Либо можно подключить контроллер через программатор и посмотреть в Cube Programmer.
Память контроллера разбита на страницы объемом 1 Кбайт. В datasheet приведена длительность на операцию стирания - максимум 40 мс.
Пользовательская прошивка хранится в начале памяти, поэтому для записи данных я использую страницы с конца микроконтроллера. Будем писать в 126 страницу по адресу 0x0801F800. 127 страница остается как запасная, если данные не помещаются на одну страницу.
#define flashADDR 0x0801F800
Далее создадим структуру, содержимое которой будет записываться в ячейку памяти:
struct
{
uint8_t var1;
uint16_t var2;
uint32_t var3;
double var4;
} test_struct;
Заполняю ее случайными значениями:
test_struct.var1 = 200;
test_struct.var2 = 59999;
test_struct.var3 = 98765;
test_struct.var4 = 45.11;
Теперь перейдем к записи во Flash. Более подробно про это можно почитать тут и тут, но если коротко, то в начале нам необходимо разблокировать память, стереть нужную нам страницу, после уже произвести запись и заблокировать обратно.
Создадим отдельную функцию и переменные:
uint8_t writeFlash (uint32_t addr)
{
HAL_StatusTypeDef status;
uint32_t structureSize = sizeof(test_struct);
FLASH_EraseInitTypeDef FlashErase;
uint32_t pageError = 0;
После этого отключаем прерывания, чтобы не нарушать процесс записи и разблокируем память:
__disable_irq();
status = HAL_FLASH_Unlock();
Настроим процесс стирания - укажем, что удалять будем постранично (еще можно массово), с какого адреса начнем и сколько страниц это затронет. Последнее рассчитаем, исходя из размера структуры.
FlashErase.TypeErase = FLASH_TYPEERASE_PAGES;
FlashErase.PageAddress = addr;
FlashErase.NbPages = structureSize / 1024 + 1;
Теперь очистим необходимое нам пространство. Если случилась ошибка в процессе стирания, то закрываем память и сообщаем об этом.
if (HAL_FLASHEx_Erase(&FlashErase, &pageError) != HAL_OK)
{
HAL_FLASH_Lock();
return HAL_ERROR;
}
Теперь все готово к записи нашей структуры. Создаем указатель на неё и с помощью функции HAL_FLASH_Program записываем все её содержимое. Функция первым аргументом запрашивает, каким объемом будут писать данные - по 1, 2 или четырем байтам за раз (FLASH_TYPEPROGRAM_BYTE/HALFWORD/WORD соответственно). Забавно то, что дальше эта функция, вне зависимости от вашего выбора, будет писать строго по 2 байта