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;