2026-05-16 5:13 AM - last edited on 2026-05-16 1:47 PM by Imen.D
Hi,
FLASH_NVM (rx) : ORIGIN = 0x0801B000, LENGTH = 2K
[magic: uint32_t (4 B)] [Config_t (40 B)] [checksum: uint32_t (4 B)]
The Config_t struct:
typedef struct {
uint16_t volume_shown;
uint16_t client_timeout_ms;
uint16_t cup_detection_threshold;
uint16_t flow_timeout_forward_ms;
uint16_t flow_timeout_backward_ms;
uint16_t foam_ms;
uint16_t foam_decay_minutes;
uint16_t foam_ms_after_decay;
uint16_t steps_forward;
uint16_t steps_backward;
uint16_t step_increment;
uint16_t tick;
uint8_t use_hall_positioning;
uint8_t offline_operation;
uint8_t com_mode;
uint8_t integration_zig;
float adjust;
char serial_number[4];
RGB_Color_t led_color; // 3 bytes (r, g, b)
} Config_t; // sizeof = 40 bytes
FlashStorage_Status_t FlashStorage_Erase(void)
{
FLASH_EraseInitTypeDef erase_config;
uint32_t page_error = 0U;
uint32_t base = storage_base_addr(); // 0x0801B000
uint32_t page = (base - FLASH_BASE) / FLASH_PAGE_SIZE;
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS);
erase_config.TypeErase = FLASH_TYPEERASE_PAGES;
erase_config.Page = page;
erase_config.NbPages = 1U;
HAL_StatusTypeDef status = HAL_FLASHEx_Erase(&erase_config, &page_error);
HAL_FLASH_Lock();
return (status == HAL_OK) ? FLASH_STORAGE_OK : FLASH_STORAGE_ERROR_ERASE;
}
FlashStorage_Status_t FlashStorage_Write(uint32_t offset, const void *data, uint32_t size)
{
uint32_t address = storage_base_addr() + offset; // 0x0801B000 + 0
const uint64_t *src=(const uint64_t *)data;
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS);
for (uint32_t i = 0U; i < (size / 8U); i++) {
HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address, src[i]);
address += 8U;
}
HAL_FLASH_Lock();
return FLASH_STORAGE_OK;
}
void Config_SaveToFlash(void)
{
uint8_t buf[128];
uint32_t magic = CONFIG_FLASH_MAGIC;
uint32_t new_checksum = compute_config_checksum(&config, sizeof(Config_t));
if (new_checksum == last_saved_checksum) return;
memset(buf, 0xFF, sizeof(buf));
memcpy(buf, &magic, 4);
memcpy(buf + 4, &config, sizeof(Config_t));
memcpy(buf + 4 + sizeof(Config_t), &new_checksum, 4);
FlashStorage_Erase();
FlashStorage_Write(0, buf, 48);
last_saved_checksum = new_checksum;
}
void Config_RequestSaveToFlash(void) {
save_pending = true;
save_request_tick = xTaskGetTickCount();
}
void Config_ProcessPendingSave(void) {
if (!save_pending) return;
if ((xTaskGetTickCount() - save_request_tick) < pdMS_TO_TICKS(3000)) return;
save_pending = false;
Config_SaveToFlash();
}
Solved! Go to Solution.
2026-05-19 1:30 AM
Hello,
Here are some answers
1/ Yes, any write/erase of the Flash stalls both CPU1 and CPU2. There is only one flash shared by both CPU so there is no safe area that could be written without stalling CPU2. If this happen during CPU2 radio operation, the link will be lost.
2/ Yes, this is mandatory and available on all STM32WBx products.
3/ The algorithm to be implemented is specified in AN5289 Chapter 4.7 Flash memory management.
The best would be to reuse the flash driver that implements this algorithm and is used and available in the project BLE_Ota (or any other BLE_xxx_Ota project).
It is provided in baremetal framework but can be used as is in OS framework. You may need to adapt to the OS the implementation of FD_WaitForSemAvailable() since the default implementation is a polling activity which will block your task instead of making it pending (although this could still be working fine when Time Slicing is used).
Regards.
2026-05-19 1:30 AM
Hello,
Here are some answers
1/ Yes, any write/erase of the Flash stalls both CPU1 and CPU2. There is only one flash shared by both CPU so there is no safe area that could be written without stalling CPU2. If this happen during CPU2 radio operation, the link will be lost.
2/ Yes, this is mandatory and available on all STM32WBx products.
3/ The algorithm to be implemented is specified in AN5289 Chapter 4.7 Flash memory management.
The best would be to reuse the flash driver that implements this algorithm and is used and available in the project BLE_Ota (or any other BLE_xxx_Ota project).
It is provided in baremetal framework but can be used as is in OS framework. You may need to adapt to the OS the implementation of FD_WaitForSemAvailable() since the default implementation is a polling activity which will block your task instead of making it pending (although this could still be working fine when Time Slicing is used).
Regards.
We’re moving the ST Community to a new platform to give you a better and more reliable community experience.