Some microcontrollers have a dedicated Non-Volatile-Memory (NVM) bank for storing calibration data, program settings and so on. The STM32F103C8T6 does not have NVM like this but it’s Flash program memory can be used with care for the same purpose. The Flash memory in this chip is divided into 1kiB sectors and there are 64 if them (0 to 63). The code to erase, write and read a sector is shown below:
int writeSector(uint32_t Address,void * values, uint16_t size) { uint16_t *AddressPtr; uint16_t *valuePtr; AddressPtr = (uint16_t *)Address; valuePtr=(uint16_t *)values; size = size / 2; // incoming value is expressed in bytes, not 16 bit words while(size) { // unlock the flash // Key 1 : 0x45670123 // Key 2 : 0xCDEF89AB FLASH->KEYR = 0x45670123; FLASH->KEYR = 0xCDEF89AB; FLASH->CR &= ~BIT1; // ensure PER is low FLASH->CR |= BIT0; // set the PG bit *(AddressPtr) = *(valuePtr); while(FLASH->SR & BIT0); // wait while busy if (FLASH->SR & BIT2) return -1; // flash not erased to begin with if (FLASH->SR & BIT4) return -2; // write protect error AddressPtr++; valuePtr++; size--; } return 0; } void eraseSector(uint32_t SectorStartAddress) { FLASH->KEYR = 0x45670123; FLASH->KEYR = 0xCDEF89AB; FLASH->CR &= ~BIT0; // Ensure PG bit is low FLASH->CR |= BIT1; // set the PER bit FLASH->AR = SectorStartAddress; FLASH->CR |= BIT6; // set the start bit while(FLASH->SR & BIT0); // wait while busy } void readSector(uint32_t SectorStartAddress, void * values, uint16_t size) { uint16_t *AddressPtr; uint16_t *valuePtr; AddressPtr = (uint16_t *)SectorStartAddress; valuePtr=(uint16_t *)values; size = size/2; // incoming value is expressed in bytes, not 16 bit words while(size) { *((uint16_t *)valuePtr)=*((uint16_t *)AddressPtr); valuePtr++; AddressPtr++; size--; } }
Writes and reads must be performed in 16 bit (half word) chunks so a little bit of type casting is necessary. The data type for the values to be read or written is void *. This allows a pointer to any type be passed without generating warnings. The sector start address must be on a 1k boundary. In this chip, Flash memory starts at 0x8000000 so sector start addresses can be 0x8000000, 0x8000400, 0x8000800, 0x8000c00, etc.
The data sheet states that the flash can withstand at least 10000 write cycles (does this include erases?). This seems like a pretty large amount but you would be surprised how quickly it can get used up. If your program requires to write to Flash memory regularly consider some wear leveling scheme.
Full code is available over here on GitHub
Usually it’s not called “write cycle” but P/E cycle (program/erase). And while only whole blocks of flash can be erased (which are rather small for NOR flash and vastly bigger for NAND flash) you can actually write them as you go and piecemeal wise. After erasing all bits of the erased block will be logical 1 and you can individually set them to 0 by writing to them (the other way around will require an erase again). There’re a few typical way how to use flash best without constantly having to erase it, e.g. you can cancel out previous data by overwriting it with 0s to let the system know to look further behind. Or you can subdivide the block and store a used bitmap at the beginning/end of the block cancelling out bits to indicates which is the last written/next free piece. Or you can use a special marker and scan for the latest version/next free space…
LikeLike
Thanks for the clarification and suggestions Daniel
LikeLike
can you give me a wear levelling example?
secondly, i have a 32kb flash(mm32spin05pt). i want to have a read & write protection for first 24KB(first 24 pages only. (each page is size = 1kb)). can you help me in this regard?
LikeLike
Hi, suppose you have to write 16 bytes to flash. Add on a string or value that would not normally appear in the data you want to store. Let’s call this a valid record identifier.
Now starting with a fully erased flash area write your data and the valid record identifier. Next time you need to write data, search for the valid record identifier. This tells you where the old record is. Write zeros over the old record and write your new record just after that in flash. When you get to the end of flash you erase and start over. See storage.cpp and nvm.cpp here https://github.com/fduignan/BreadBoardGames-2018/tree/master/bbg_stm32f103c8t6_st7789Serial
LikeLike
First, thank you for your guidance, it’s really helpful. I have a question, though, can you read the flash memory without unlocking it first?
LikeLike
Yes but make sure your read is word aligned (aligned on an even address)
LikeLike