2026-03-27 4:46 AM
Hi people,
i have trouble with writing to the internal eeprom / flash. I can write values one time to the EEPROM and read it correct back from eeprom. When i write next time values to the eeprom and read them, the values changed but are incorrect. I tried many solutions, but know i am perplexed.
//EEPROM Funktionen
void EEPROM_Unlock(void)
{
while ((FLASH->SR & FLASH_SR_BSY) != 0) /* Wait for FLASH to be free */
{ /* insert timeout test */ }
if ((FLASH->PECR & FLASH_PECR_PELOCK) != 0) /* If PELOCK is locked */
{
FLASH->PEKEYR = FLASH_PEKEY1; /* Unlock PELOCK */
FLASH->PEKEYR = FLASH_PEKEY2;
}
//FLASH->PECR = FLASH->PECR | (FLASH_PECR_ERRIE | FLASH_PECR_EOPIE); /* enable flash interrupts */
}
// EEPROM lock
void EEPROM_Lock(void)
{
while ((FLASH->SR & FLASH_SR_BSY) != 0) /* Wait for FLASH to be free */
{ /* insert timeout test */
}
//FLASH->PECR = FLASH->PECR & ~(FLASH_PECR_ERRIE | FLASH_PECR_EOPIE); /* disable flash interrupts */
FLASH->PECR |= FLASH_PECR_PELOCK; /* Lock memory with PELOCK */
}
// -----------------------------
// 16-bit Array in EEPROM write
// -----------------------------
void WriteEEPROM32Array(uint32_t *src, size_t length)
{
uint32_t address = 0x08080000;
EEPROM_Unlock();
for(size_t i = 0; i < 64;i++)
{
*(uint32_t *)(address) = src[i];
__WFI();
address += 4;
}
EEPROM_Lock();
}
// -----------------------------
// 16-bit Array in EEPROM read
// -----------------------------
void ReadEEPROM32Array(uint32_t *dest, size_t length)
{
uint32_t address = 0x08080000;
for (size_t i = 0; i < 64; i++)
{
dest[i] = *(__IO uint32_t*)address;
address += 4;
}
}My write routine
void SetEEPROM()
{
uint8_t Rx[256];
Messung = 0; // Stopp Set EEprom
HAL_NVIC_DisableIRQ(USART1_IRQn);
HAL_UART_Abort(&huart1);
HAL_UART_Receive(&huart1,(uint8_t*) Rx, 256, 20000);
for(uint8_t i = 0;i<64;i++)
{
KalibWerte[i] |= Rx[(4*i)]<<24;
KalibWerte[i] |= Rx[(4*i)+1]<<16;
KalibWerte[i] |= Rx[(4*i)+2]<<8;
KalibWerte[i] |= Rx[(4*i)+3];
}
WriteEEPROM32Array(KalibWerte, 64);
HAL_UART_Transmit(&huart1, (uint8_t*)("s\r"), 2, 10);
HAL_NVIC_EnableIRQ(USART1_IRQn);
HAL_UART_Receive_IT(&huart1,rxByte, 1);
}Does anybody have an idea or solution?
KR
Stefan
Solved! Go to Solution.
2026-03-27 4:52 AM - edited 2026-03-27 4:52 AM
Welcome @Stefan-G, to the community!
Most likely root cause: Flash/EEPROM is not erased before the second write.
On STM32 you can change bits only from 1 to 0 when programming; to go back to 1 you must erase the page/word first. First write works, second write to same addresses gives “corrupted” data.
So you need, before WriteEEPROM32Array():
Additional issues (also fix, but secondary to the main problem): in WriteEEPROM32Array and ReadEEPROM32Array, you ignore length and always loop i < 64; use i < length. Don’t use __WFI() as “wait for flash”; instead, after each write:
while (FLASH->SR & FLASH_SR_BSY) { }
// then check/clear error flags and EOP
In SetEEPROM(), build KalibWerte with = instead of |= or clear it first:
KalibWerte[i] = (uint32_t)Rx[4*i] << 24;
KalibWerte[i] |= (uint32_t)Rx[4*i+1] << 16;
KalibWerte[i] |= (uint32_t)Rx[4*i+2] << 8;
KalibWerte[i] |= (uint32_t)Rx[4*i+3];
Hope that helps?
Regards
/Peter
2026-03-27 4:52 AM - edited 2026-03-27 4:52 AM
Welcome @Stefan-G, to the community!
Most likely root cause: Flash/EEPROM is not erased before the second write.
On STM32 you can change bits only from 1 to 0 when programming; to go back to 1 you must erase the page/word first. First write works, second write to same addresses gives “corrupted” data.
So you need, before WriteEEPROM32Array():
Additional issues (also fix, but secondary to the main problem): in WriteEEPROM32Array and ReadEEPROM32Array, you ignore length and always loop i < 64; use i < length. Don’t use __WFI() as “wait for flash”; instead, after each write:
while (FLASH->SR & FLASH_SR_BSY) { }
// then check/clear error flags and EOP
In SetEEPROM(), build KalibWerte with = instead of |= or clear it first:
KalibWerte[i] = (uint32_t)Rx[4*i] << 24;
KalibWerte[i] |= (uint32_t)Rx[4*i+1] << 16;
KalibWerte[i] |= (uint32_t)Rx[4*i+2] << 8;
KalibWerte[i] |= (uint32_t)Rx[4*i+3];
Hope that helps?
Regards
/Peter
2026-03-27 5:03 AM
Hi Peter,
thanks for the fast response.
I already try to erase the flash/eeprom with the following function in the past. But i had the same results.
void ClearEEPROM32Array(void)
{
uint32_t addr = 0x08080000;
EEPROM_Unlock();
__WFI();
FLASH->PECR |= FLASH_PECR_ERASE | FLASH_PECR_DATA;
for (size_t i = 0; i < 64; i++)
{
*(__IO uint32_t *)addr = (uint32_t)0;
__WFI();
addr += 4;
}
FLASH->PECR &= ~(FLASH_PECR_ERASE | FLASH_PECR_DATA);
EEPROM_Lock();
}And thanks for the bug fixing. The code is still a bit buggy cause of the massive testing.
I will try without __WFI(), maybe this will work.
KR
Stefan
2026-03-27 5:11 AM
@Peter BENSCH I think the L0 series has EEPROM that can simply be overwritten.
Here's my working EEPROM stuff (not HAL), used on L031 and L073:
uint8_t FlashEepWriteWord(uint32_t u32EepAddr, uint32_t u32Word)
{
/* address check */
if( (u32EepAddr % 4) != 0 ) return ERROR_EEP_ADDR;
if( (u32EepAddr < DATA_EEPROM_BASE) || (u32EepAddr > (DATA_EEPROM_END - 3)) ) return ERROR_EEP_ADDR;
/* lock check */
if( (FLASH->PECR & FLASH_PECR_PELOCK) != 0 )
{
/* unlock */
if( FlashEepUnlock() != 0 ) return ERROR_EEP_WRITE;
}
/* busy check */
if( FlashEepBusy((uint32_t)FLASHEEP_TO_BUSY_MS) != 0 ) return ERROR_EEP_BUSY;
/* writing happens here */
*(uint32_t *)u32EepAddr = u32Word;
/* busy check */
if( FlashEepBusy((uint32_t)FLASHEEP_TO_BUSY_MS) != 0 ) return ERROR_EEP_BUSY;
/* lock */
if( FlashEepLock() != 0 ) return ERROR_EEP_WRITE;
return 0;
}
2026-03-27 5:15 AM
Maybe you should add a BUSY check after each write.
Here it is, from RM0367 for L0x3, page 85:
Write to data EEPROM
...
Description
This operation aims at writing a word or a part of a word in the data EEPROM. The user
must write the right value at the right address and with the right size. The memory
interface automatically executes an erase operation when necessary (if all bits are
currently set to 0, there is no need to delete the old content before writing). Similarly, if
the data to write is at 0, only the erase operation is executed. When only a write
operation or an erase operation is executed, the duration is Tprog (3.2 ms); if both are
executed, the duration is 2 x Tprog (6.4 ms). It is possible to force the memory interface
to execute every time both erase and write operations set the FIX flag to 1.
2026-03-27 5:40 AM
Hi people,
I get it to work. Thanks for your help. I also will check the code from LCE, that looks short and easy.
I am not sure, what the problem was, but i made this.
And i did the erase not in the same function, where i do the programming. Maybe that is the reason??
Here is the working code
//EEPROM Funktionen
void EEPROM_Unlock(void)
{
while ((FLASH->SR & FLASH_SR_BSY) != 0) /* Wait for FLASH to be free */
{ /* insert timeout test */ }
if ((FLASH->PECR & FLASH_PECR_PELOCK) != 0) /* If PELOCK is locked */
{
FLASH->PEKEYR = FLASH_PEKEY1; /* Unlock PELOCK */
FLASH->PEKEYR = FLASH_PEKEY2;
}
//FLASH->PECR = FLASH->PECR | (FLASH_PECR_ERRIE | FLASH_PECR_EOPIE); /* enable flash interrupts */
}
// EEPROM sperren
void EEPROM_Lock(void)
{
while ((FLASH->SR & FLASH_SR_BSY) != 0) /* Wait for FLASH to be free */
{ /* insert timeout test */
}
//FLASH->PECR = FLASH->PECR & ~(FLASH_PECR_ERRIE | FLASH_PECR_EOPIE); /* disable flash interrupts */
FLASH->PECR |= FLASH_PECR_PELOCK; /* Lock memory with PELOCK */
}
// -----------------------------
// 16-bit Array in EEPROM schreiben
// -----------------------------
void WriteEEPROM32Array(uint32_t *src, size_t length)
{
uint32_t address = 0x08080000;
for(size_t i = 0; i < length;i++)
{
*(uint32_t *)(address) = src[i];
while ((FLASH->SR & FLASH_SR_BSY) != 0)
{
// wait for flash
}
address += 4;
}
}
// -----------------------------
// 16-bit Array in EEPROM lesen
// -----------------------------
void ReadEEPROM32Array(uint32_t *dest, size_t length)
{
uint32_t address = 0x08080000;
for (size_t i = 0; i < length; i++)
{
dest[i] = *(__IO uint32_t*)address;
address += 4;
}
}
void ClearEEPROM32Array(size_t length)
{
uint32_t addr = 0x08080000;
FLASH->PECR |= FLASH_PECR_ERASE | FLASH_PECR_DATA;
for (size_t i = 0; i < length; i++)
{
*(__IO uint32_t *)addr = (uint32_t)0;
while ((FLASH->SR & FLASH_SR_BSY) != 0)
{
}
addr += 4;
}
FLASH->PECR &= ~(FLASH_PECR_ERASE | FLASH_PECR_DATA);
}void SetEEPROM()
{
uint8_t Rx[256];
Messung = 0; // Stopp Set EEprom
HAL_NVIC_DisableIRQ(USART1_IRQn);
HAL_UART_Abort(&huart1);
HAL_UART_Receive(&huart1,(uint8_t*) Rx, 256, 20000);
for(uint8_t i = 0;i<64;i++)
{
KalibWerte[i] |= Rx[(4*i)]<<24;
KalibWerte[i] |= Rx[(4*i)+1]<<16;
KalibWerte[i] |= Rx[(4*i)+2]<<8;
KalibWerte[i] |= Rx[(4*i)+3];
}
EEPROM_Unlock();
WriteEEPROM32Array(KalibWerte, 64);
EEPROM_Lock();
HAL_UART_Transmit(&huart1, (uint8_t*)("s\r"), 2, 10);
HAL_NVIC_EnableIRQ(USART1_IRQn);
HAL_UART_Receive_IT(&huart1,rxByte, 1);
}
void GetEEPROM()
{
uint8_t Tx[256];
ReadEEPROM32Array(KalibWerte, 64);
for(uint8_t i = 0; i<64;i++)
{
Tx[4*i] = (uint8_t)(KalibWerte[i] >> 24);
Tx[(4*i)+1] = (uint8_t)(KalibWerte[i] >> 16);
Tx[(4*i)+2] = (uint8_t)(KalibWerte[i] >> 8);
Tx[(4*i)+3] = (uint8_t)(KalibWerte[i]);
}
HAL_UART_Transmit(&huart1, Tx, 256, 100);
}Somewhere in the sate machine
EEPROM_Unlock();
ClearEEPROM32Array(64);
EEPROM_Lock();Thanks
Stefan
2026-03-27 5:43 AM - edited 2026-03-27 5:46 AM
I'm pretty sure it's the BUSY check after each write, so please mark Peter's post as solution.
PS: if you have the flash space and time, never use any while loops without a timeout / break mechanism!
2026-03-27 5:48 AM
Okay, thanks. I marked him.