Физическая память FLASH микроконтроллеров BK72xx устроена довольно непривычным образом. Каждый блок памяти в 32 байта снабжен двухбайтовой контрольной суммой CRC16, вычисленной по полиному 0x8005 с начальным значением 0xFFFF.
[ блок 32 байта ][CRC16] [ блок 32 байта ][CRC16] [ блок 32 байта ][CRC16] ... [ блок 32 байта ][CRC16]
Чтение FLASH. Память программ и данных находится в памяти FLASH, и она доступна для ядра процессора в логическом виде, без контрольных сумм CRC16. Аппаратура доступа к памяти на лету преобразует логические адреса в физические:
[ блок 32 байта ] [ блок 32 байта ] [ блок 32 байта ] ... [ блок 32 байта ]
В коде программы нельзя напрямую читать и записывать адресное пространство flash, иначе (случайным образом) может происходить непредсказуемое поведение, обычно заканчивающееся зависанием с перезагрузкой по сторожевому таймеру. Для чтения памяти необходимо использовать специальный драйвер в модуле flash.c, который находится в каталоге ./beken378/driver/flash репозитория [1]. Ниже показан пример чтения физического содержимого памяти, вместе с контрольными суммами CRC16. Для вызова функций из модуля flash.c используется промежуточная модель драйвера на основе функций ddev_open, ddev_read, ddev_close.
uint8_t xbuf[EF_ERASE_MIN_SIZE];
// Вывод физического содержимого памяти FLASH MCU BK7231N:
Для практического использования данных FLASH в программе, чтобы читать и записывать данные не по физическим, а по логическим адресам, удобнее другой интерфейс, реализованный в модуле fal_flash_beken7231_crc_port.c (находится в каталоге samples/ota/port/). Пример чтения логического содержимого памяти FLASH:
// Вывод логического содержимого памяти FLASH MCU BK7231N:
Обратите внимание, что в этом примере вывода по адресу 00000020 отсутствут байты CRC16 от предыдущего блока 32 байт.
Запись FLASH. Запись в память FLASH реализуется аналогично, либо по физическим адресам через интерфейс flash.c, либо по логическим адресам через интерфейс fal_flash_beken7231_crc_port.c. Ниже приведен код записи физическим адресам. Он дан только для примера, это ошибочный код, потому что здесь блоки по 32 байта перед записью не снабжаются контрольной суммой CRC16.
// Снятие защиты на запись:
bk_flash_enable_security(FLASH_PROTECT_NONE);
// Перед записью записываемый сектор необходимо стереть:
address = FLASH_NVSETTINGS_ADDR; // физический адрес FLASH
len = EF_ERASE_MIN_SIZE;
GLOBAL_INT_DISABLE(); // запрет прерываний
ef_port_erase(address, len);
nvdata.seed = rt_tick_get();
nvdata.CRC32 = ef_calc_crc32(0, &nvdata, sizeof(TNVsettings)-sizeof(uint32_t));
// Запись по данных физическому адресу FLASH_NVSETTINGS_ADDR:
ddev_write(flash_hdl, (char*)&nvdata, sizeof(nvdata), FLASH_NVSETTINGS_ADDR);
GLOBAL_INT_RESTORE(); // восстановление прерываний// Возобновление защиты на запись:
bk_flash_enable_security(FLASH_UNPROTECT_LAST_BLOCK);
ddev_close(flash_hdl);
}
Для работы с памятью по физическим адресам на чтение и запись удобно использовать интерфейс драйвера fal_flash_beken7231_crc_port.c. Он сам заботится о добавлении контрольных сумм CRC16 в записываемые данные и об остановке и возобновлении прерываний. Ниже показан практический пример записи в FLASH по логическим адресам с помощью интерфейса erase и write драйвера. Имейте в виду, что перед использованием интерфейса этого драйвера необходимо вызывать функцию fal_init (обычно при старте приложения). Как видно, код получился намного проще: