I'm using a STM32F401VCT6U "discovery" board, and I need to provide a way for the user to write addresses in memory at runtime.
I wrote what can be simplified to the following function:
uint8_t Write(uint32_t address, uint8_t* values, uint8_t count)
{
uint8_t index;
for (index = 0; index < count; ++index) {
if (IS_FLASH_ADDRESS(address+index)) {
/* flash write */
FLASH_Unlock();
if (FLASH_ProgramByte(address+index, values[index]) != FLASH_COMPLETE) {
return FLASH_ERROR;
}
FLASH_Lock();
} else {
/* ram write */
((uint8_t*)address)[index] = values[index]
}
}
return NO_ERROR;
}
In the above, address
is the base address, values
is a buffer of size at least count
which contains the bytes to write to memory and count
the number of bytes to write.
Now, my problem is the following: when the above function is called with a base address
in flash and count=100
, it works normally the first few times, writing the passed values
buffer to flash. After those first few calls however, I cannot write just any value anymore: I can only reset bits in the values in flash, eg an attempt to write 0xFF to 0x7F will leave 0x7F in the flash, while writing 0xFE to 0x7F will leave 0x7E, and 0x00 to any value will be successful (but no other value will be writable to the address afterwards).
I can still write normally to other addresses in the flash by changing the base address
, but again only a few times (two or three calls with count=100
).
This behaviour suggests that the maximum write count of the flash has been reached, but I cannot imagine it can be so fast. I'd expect at the very least 10,000 writes before exhaustion. So what am I doing wrong?
I have a working and tested solution, but it is rather different from @Ricibob's answer, so I decided to make this an answer.
Since my user can write anywhere in select flash sector, my application cannot handle the responsability of erasing the sector when needed while buffering to RAM only the data that need to be preserved.
As a result, I transferred to my user the responsability of erasing the sector when a write to it doesn't work (this way, the user remains free to use another address in the sector to avoid too many write-erase cycles).
Solution
Basically, I expose a
write(uint32_t startAddress, uint8_t count, uint8_t* values)
function that has aWRITE_SUCCESSFUL
return code and aCANNOT_WRITE_FLASH
in case of failure. I also provide my user with agetSector(uint32_t address)
function that returns the id, start address and end address of the sector corresponding to the address passed as a parameter. This way, the user knows what range of address is affected by the erase operation. Lastly, I expose aneraseSector(uint8_t sectorID)
function that erase the flash sector whose id has been passed as a parameter.Erase Policy
The policy for a failed write is different from @Ricibob's suggestion of "erase if the value in flash is different of FF", as it is documented in the Flash programming manual that a write will succeed as long as it is only bitreset (which matches the behavior I observed in the question):
So I use the macro
CAN_WRITE(a,b)
, wherea
is the original value in flash andb
the desired value. The macro is defined as:which works because:
!
) will transform 0 totrue
and everything else tofalse
, so~a & b
must equal 0 for the macro to betrue
;a
is at 0 in~a
, so it will be 0 whatever its value inb
is (you can transform a 1 in 1 or 0);a
, then it is 1 in~a
, ifb
equals 1 then~a & b != 0
and we cannot write, ifb
equals 0 it's OK (you can transform a 0 to 0 only, not to 1).List of flash sector in STM32F4
Lastly and for future reference (as it is not that easy to find), the list of sectors of flash in STM32 can be found on page 7 of the Flash programming manual.