2026-02-16 6:07 AM - edited 2026-02-16 6:09 AM
Hello, i am trying to store adc calibration data in flash on my H7A3 project.
Writing the data (64byte, flash addr 16byte aligned) is successful (at least i hope, since i get no errors), but when reading the same data back from flash via that same Bank2 memory address, i only read zeroes.
I was trying to use a same approach like the example program.
Can somebody review my code and point me in the right direction?
#include "stm32h7xx_hal.h"
#include "stm32h7xx_hal_flash.h"
#include "string.h"
#include <stdint.h>
#include "settings.h"
#define FLASH_USER_START_ADDR FLASH_BANK2_BASE /* Start @ of user Flash area in Bank2 */
#define FLASH_USER_END_ADDR (FLASH_BANK2_BASE + FLASH_BANK_SIZE - 1) /* End @ of user Flash area in Bank2 */
// get flash sector number from address
uint32_t GetSector(uint32_t Address) {
uint32_t sector = 0;
if (Address < (FLASH_BANK2_BASE + FLASH_BANK_SIZE)) {
sector = (Address - FLASH_BANK2_BASE) / FLASH_SECTOR_SIZE;
} else {
sector = (Address - (FLASH_BANK2_BASE + FLASH_BANK_SIZE)) / FLASH_SECTOR_SIZE;
}
return sector;
}
// requires data to be word-aligned
// writes 'words' 32-bit words from data to flash starting at FLASH_USER_START_ADDR
// flashword is 16 bytes (4 x 32-bit words) at a time
// returns 0 on success, HAL error code on failure
// note: this function erases the entire user flash area before writing
uint32_t flash_write_words(uint32_t* src_ptr, uint32_t dest_addr, uint16_t numberofwords) {
uint32_t flash_addr = dest_addr;
static FLASH_EraseInitTypeDef EraseInitStruct;
uint32_t SECTORError;
SCB_DisableICache();
HAL_FLASH_Unlock();
// Erase sectors
uint32_t StartSector = GetSector(dest_addr);
uint32_t EndSectorAddress = dest_addr + (numberofwords * 4) - 1;
uint32_t EndSector = GetSector(EndSectorAddress);
EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
EraseInitStruct.Sector = StartSector;
EraseInitStruct.NbSectors = EndSector - StartSector + 1;
if (HAL_FLASHEx_Erase(&EraseInitStruct, &SECTORError) != HAL_OK) {
HAL_FLASH_Lock();
SCB_EnableICache();
return HAL_FLASH_GetError();
}
uint32_t err = HAL_FLASH_ERROR_NONE;
// Write in 16-byte (FLASHWORD) chunks
uint8_t* byte_ptr = (uint8_t*) src_ptr; // pointer to data as bytes
uint32_t total_bytes = numberofwords * 4;
for (uint32_t offset = 0; offset < total_bytes; offset += 16) {
// pointer to 16-byte block in RAM
uint32_t block_ptr = (uint32_t) (byte_ptr + offset);
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, flash_addr + offset, block_ptr) != HAL_OK) {
err |= HAL_FLASH_GetError();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS_BANK1 | FLASH_FLAG_ALL_ERRORS_BANK2);
}
}
SCB_CleanDCache();
SCB_InvalidateDCache();
HAL_FLASH_Lock();
SCB_EnableICache();
return err;
}
int write_settings_data(const struct SettingsData* settings_data) {
static __attribute__((aligned(16))) struct SettingsData buffer;
buffer = *settings_data; // real copy
buffer.magic = MAGIC_NUMBER;
uint32_t* data_ptr = (uint32_t*) &buffer;
uint16_t words = sizeof(struct SettingsData) / sizeof(uint32_t);
uint32_t result = flash_write_words(data_ptr, FLASH_USER_START_ADDR, words);
if (result != HAL_FLASH_ERROR_NONE) {
return -1;
}
return 0;
}
/**
* @brief A generic, safe function to read 32-bit words from Flash.
* src_addr The starting physical address in Flash.
* dst_buf Pointer to the RAM buffer.
* num_words Number of 32-bit words to read.
* @return size_t Total bytes read.
*/
size_t flash_read_words(uint32_t src_addr, uint32_t* dst_buf, uint16_t num_words) {
if (dst_buf == NULL || num_words == 0)
return 0;
SCB_InvalidateDCache();
// Point to the Flash address as a volatile 32-bit source
// This prevents the compiler from optimizing away the reads.
volatile uint32_t* flash_ptr = (volatile uint32_t*) src_addr;
for (uint16_t i = 0; i < num_words; i++) {
dst_buf[i] = flash_ptr[i];
}
return (size_t) num_words * sizeof(uint32_t);
}
/**
* @brief Specific application function to load settings.
*/
int read_settings_data(struct SettingsData* settings_data) {
uint16_t word_count = sizeof(struct SettingsData) / sizeof(uint32_t);
return (int) flash_read_words(FLASH_USER_START_ADDR, (uint32_t*) settings_data, word_count);
}This is how i call my settings api:
struct calibration_data {
float voct_pitch_offset; // 4 bytes
float voct_pitch_scale; // 4 bytes
float cv_offset[NUM_CV_CHANNELS]; // 4 * 4 = 16 bytes
float pitchpot_min; // 4 bytes
float pitchpot_mid; // 4 bytes
float pitchpot_max; // 4 bytes
}; // total: 36 bytes
struct State {
uint8_t tobe_reserved0; // 1 byte
uint8_t tobe_reserved1; // 1 byte
uint8_t tobe_reserved2; // 1 byte
uint8_t tobe_reserved3; // 1 byte
}; // total: 4 bytes
struct SettingsData {
struct calibration_data calibration_data; // 36 bytes
struct State state; // 4 bytes
uint8_t padding[SETTINGS_SIZE - sizeof(struct calibration_data) - sizeof(struct State) -
sizeof(uint32_t)]; // 20 bytes to make total size 64 bytes
uint32_t magic; // 4 bytes magic number to verify valid data
} __attribute__((aligned(16))); // total: 64 bytes
struct SettingsData settings_data_write = {
.calibration_data =
{
.voct_pitch_scale = 123.4,
.voct_pitch_offset = 124.5,
.cv_offset = {1.1f, 2.2f, 3.3f, 4.4f},
},
.magic = MAGIC_NUMBER,
.state =
{
.tobe_reserved0 = 0xAA,
.tobe_reserved1 = 0xBB,
.tobe_reserved2 = 0xCC,
.tobe_reserved3 = 0xDD,
},
};
write_settings_data(&settings_data_write);
static __attribute__((aligned(16))) struct SettingsData settings_data_read;
read_settings_data(&settings_data_read);
thanks a lot in advance!
Best, Jonas
Solved! Go to Solution.
2026-02-16 6:38 AM
Your EraseInitStruct has a few fields which are not initialized. Notably, the bank number. You should initialize all fields to the correct value.
Perhaps show a complete example. You show functions but not how they are used and what values parameters are passed. There are no calls to flash_read_words being made.
2026-02-16 6:38 AM
Your EraseInitStruct has a few fields which are not initialized. Notably, the bank number. You should initialize all fields to the correct value.
Perhaps show a complete example. You show functions but not how they are used and what values parameters are passed. There are no calls to flash_read_words being made.
2026-02-16 6:46 AM
Quick question - did you look at the Flash content after writing? I mean with debugger using IDE memory viewer or STM32CubeProgrammer.
There's a lot of pointer math and casting but I don't see anything particularly wrong. That temporary static "buffer" feels a bit excessive, but not wrong either.
However data cache handling is strange. You don't just touch the settings structure cache, but everything. Maybe you posted isolated code here and it's not visible what else may break down due to that. You should use the address and size specific cache manipulations functions. And keep in mind that Cortex M7 cache line size is 32 bytes, which may expand outside of your settings structure. In addition, even if you do use address based cache then two questions arise:
Maybe you can try removing cache handling? And keep data (and instruction) cache off all the time. Once you get it working, use cache again.
2026-02-16 9:07 AM
Hey thanks for replying,
I removed the Cache tinkering.
Also i found the bug, i wasnt specifying the BANK when erasing. This led to undefined behavior. The program operation was not throwing an error, but reading from the address caused HardFault or resulted in only zeroes.
Now i am specifying the Bank in the Erase struct, works good now.