Reputation: 2576
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?
Upvotes: 0
Views: 6290
Reputation: 111
ST's "right way" is detailed in AN3969: EEPROM emulation in STM32F40x/STM32F41x microcontrollers
This is more or less the process:
This is insane, but I didn't come up with it.
Upvotes: 1
Reputation: 2576
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).
Basically, I expose a write(uint32_t startAddress, uint8_t count, uint8_t* values)
function that has a WRITE_SUCCESSFUL
return code and a CANNOT_WRITE_FLASH
in case of failure.
I also provide my user with a getSector(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 an eraseSector(uint8_t sectorID)
function that erase the flash sector whose id has been passed as a parameter.
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):
Note: Successive write operations are possible without the need of an erase operation when changing bits from ‘1’ to ‘0’. Writing ‘1’ requires a Flash memory erase operation. If an erase and a program operation are requested simultaneously, the erase operation is performed first.
So I use the macro CAN_WRITE(a,b)
, where a
is the original value in flash and b
the desired value. The macro is defined as:
!(~a & b)
which works because:
!
) will transform 0 to true
and everything else to false
, so ~a & b
must equal 0 for the macro to be true
;a
is at 0 in ~a
, so it will be 0 whatever its value in b
is (you can transform a 1 in 1 or 0);a
, then it is 1 in ~a
, if b
equals 1 then ~a & b != 0
and we cannot write, if b
equals 0 it's OK (you can transform a 0 to 0 only, not to 1). 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.
Upvotes: 0
Reputation: 7725
You have missunderstood how flash works - it is not for example as straight forward as writing EEPROM. The behaviour you are discribing is normal for flash.
To repeatidly write the same address of flash the whole sector must be first erased using FLASH_EraseSector. Generally any data that needs to preserved during this erase needs to be either buffered in RAM or in another flash sector.
If you are repeatidly writing a small block of data and are worried about flash burnout do to many erase write cycles you would want to write an interface to the flash where each write you move your data along the flash sector to unwriten flash, keeping track of its current offset from the start of sector. Only then when you run out of bytes in the sector would you need to erase and start again at start of sector.
Upvotes: 5