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