7
votes

I want to write a variable, for example an integer with the number 5 to the FLASH and then after the power goes away and the device is turned on again read it.

I already know that in order to write something I first need to erase the page and then write.

In the manual it says:

  1. Write OPTKEY1 = 0x0819 2A3B in the Flash option key register (FLASH_OPTKEYR)
  2. Write OPTKEY2 = 0x4C5D 6E7F in the Flash option key register (FLASH_OPTKEYR)

How do I perform this tasks?

Sector 0 has a Block adress from 0x0800 0000 to 0x0800 3FFF, this is where I want to write.

Here the link to the manual, page 71: STM32 Manual

2
It highly dependent to the hardware. Some types of flash memory do not allow non-block operations at all.0andriy
@4386427 That is incorrect. OPTKEY1 and OPTKEY2 are values, not addresses. Adding them to the address of FLASH_OPTKEYR will result in a crash or unexpected behavior.user149341
@duskwuff - I see. I read it as if OPTKEY1 and OPTKEY2 was two registers within a group of registers called FLASH_OPTKEYR. Comment deleted. Thanks.4386427
Reading the manual in-depth is of course an option, but there is usually also an app note for how this is done. Check for "eeprom emulation" or "bootloader" app notes.Lundin
I'd just like to point out, that flash memory has only a limited number of erase-write cycles before it degrades. I strongly recommend to either use battery backed up NVRAM or some high endurance external nonvolatile memory – for example MRAM – to store nonvolatile data that's changed often. Personally I always go with MRAM for those things.datenwolf

2 Answers

12
votes

You can use following code for write data to flash with HAL library.

void Write_Flash(uint8_t data)
{
     HAL_FLASH_Unlock();
     __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGSERR );
     FLASH_Erase_Sector(FLASH_SECTOR_6, VOLTAGE_RANGE_3);
     HAL_FLASH_Program(TYPEPROGRAM_WORD, FlashAddress, data);
     HAL_FLASH_Lock();
}

You should update linker script as follows. Add DATA in MEMORY and add .user_data in SECTIONS.

MEMORY
{
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 320K
CCMRAM (rw)      : ORIGIN = 0x10000000, LENGTH = 64K
FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 2048K
DATA (rwx)      : ORIGIN = 0x08040000, LENGTH = 128k
}

/* Define output sections */
SECTIONS
{
 .user_data :
  {
    . = ALIGN(4);
     KEEP(*(.user_data))
    . = ALIGN(4);
  } > DATA

You should add following attribute on main code for reading data after power on

__attribute__((__section__(".user_data"))) const char userConfig[64];

After all these, you can read your flash data with calling userConfig[0].

3
votes

I'm using STM32F407 and Atollic TrueSTUDIO® for STM32 Version 9.3.0.

When using above suggested code

attribute((section(".user_data"))) const char userConfig[64];

my compiler assumed userConfig to be constant zero. I had to remove the const from the declaration to make it work.

My complete solution consists of two parts (as already said above but with some further modifications):

Step 1 edit linker file:

In 'MEMORY'

FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 896K /* origin size was 1024k, subtracted size of DATA */
DATA (rx)       : ORIGIN = 0x080E0000, LENGTH = 128K

In 'SECTIONS'

/* User data section at the end of the flash to store calibration data etc. */
.user_data (NOLOAD):
{
  . = ALIGN(4);
  _user_data_start = .; /* create a global symbol at user_data start */
   KEEP(*(.user_data))
  . = ALIGN(4);
  _user_data_end = .;  /* create a global symbol at user_data end */
} >DATA

Step 2 write code:

uint8_t userConfig[64] __attribute__ ((section(".user_data")));
extern uint32_t _user_data_start;
extern uint32_t _user_data_end;
uint8_t ConfArray[16];
uint32_t TestArray[2];

// Copy a part from the userConfig to variable in RAM
for (i = 0; i < 16; i++)
{
    ConfArray[i] = userConfig[i];
}

// get the address of start and end of user_data in flash
// the & is importand, else you would get the value at the address _user_data_start and _user_data_end points to
TestArray[0] = (uint32_t)&_user_data_start;
TestArray[1] = (uint32_t)&_user_data_end;