cancel
Showing results for 
Search instead for 
Did you mean: 

how to use flash memory "the bare metal way".

HellasT
Associate III

Hello, everybody! I'm relatively new to STM32 microcontrollers and have a moderate knowledge of C programming and a fair understanding of electronics.

Over the past few months, I've been learning to properly set up STM32F4 series MCUs, specifically the F401RET6U (Nucleo board) and the F411CEU6 (Black Pill board), using bare-metal approach. I've spent considerable time watching tutorial videos and reading online guides to help me get started.

I'm now at a point where I can set up the clock and read from and write to GPIOs.

From what I understand, setting up peripherals involves configuring the correct values in the appropriate registers in the right order.

I would like to learn how to use embedded flash memory to store information that needs to be retained after a power loss. So far, I've struggled to find a guide or tutorial that I can fully understand. Could someone please assist me with this or point me in the right direction?

Thank you very much, and have a blessed week, everyone!

Wise is not he who knows many. Wise is he who knows useful. Aeschylus.
15 REPLIES 15

It's not the Compiler which allocates storage; that's the Linker's job.

Therefore you'd be looking to adjust the Linker script ...

I hear ya.

Ive read about adjusting the Linker script to create a custom section and assign it to a sector.

Wise is not he who knows many. Wise is he who knows useful. Aeschylus.
HellasT
Associate III

I have read alot on the reference manual and i think i am on the right track. I have one question. When uploading a new code on the MCU using the cubeide, does the whole mcu's flash get erased before the new code is uploaded ?

Based on my experiments i would say that ony the sectors occupied by the code are being erased and then re programmed.

Here are parts of my code concerning the use of the mcu's flash : 

 

 

#include <stdint.h>
#define STM32F401xE
#include "stm32f4xx.h"
#include <MyClock.h>
#include <MyFlash.h>
// 16KB, Word length is 4 bytes (32 bits).
uint32_t MyFlashData[4096] __attribute__((section("MyFlashSection"))) = {0xFF};

int main(void)
{	
        Clk_speed (SCALE_2_MODE,2,1,1,1,HPRE_0,PPRE1_2,PPRE2_0,4,84,PLLP_2,1,HSE);
        MyFlashWriteWord(0x0800C000,0xF0F0F0F0);
	while(1)
	{
		
	}
}

 

 

and here is the linkerscript :

 

 

/*
******************************************************************************
**
** @file        : LinkerScript.ld (debug in RAM dedicated)
**
** @author      : Auto-generated by STM32CubeIDE
**
**  Abstract    : Linker script for NUCLEO-F401RE Board embedding STM32F401RETx Device from stm32f4 series
**                      512KBytes FLASH
**                      96KBytes RAM
**
**                Set heap size, stack size and stack location according
**                to application requirements.
**
**                Set memory bank area and size if external memory is used
**
**  Target      : STMicroelectronics STM32
**
**  Distribution: The file is distributed as is, without any warranty
**                of any kind.
**
******************************************************************************
** @attention
**
** Copyright (c) 2024 STMicroelectronics.
** All rights reserved.
**
** This software is licensed under terms that can be found in the LICENSE file
** in the root directory of this software component.
** If no LICENSE file comes with this software, it is provided AS-IS.
**
******************************************************************************
*/

/* Entry Point */
ENTRY(Reset_Handler)

/* Highest address of the user mode stack */
_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */

_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */

/* Memories definition */
MEMORY
{
  RAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 96K
  FLASH    (rx)    : ORIGIN = 0x8000000,   LENGTH = 512K
  MY_FLASH_SECTION (rw) : ORIGIN = 0x0800C000, LENGTH = 16K /* My custom section at Sector 3*/   
}

/* Sections */
SECTIONS
{
  /* The startup code into "RAM" Ram type memory */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >RAM

  /* The program code and other data into "RAM" Ram type memory */
  .text :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)
    *(.RamFunc)        /* .RamFunc sections */
    *(.RamFunc*)       /* .RamFunc* sections */

    KEEP (*(.init))
    KEEP (*(.fini))

    . = ALIGN(4);
    _etext = .;        /* define a global symbols at end of code */
  } >RAM

  /* Constant data into "RAM" Ram type memory */
  .rodata :
  {
    . = ALIGN(4);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
  } >RAM

  .ARM.extab (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
  {
    . = ALIGN(4);
    *(.ARM.extab* .gnu.linkonce.armextab.*)
    . = ALIGN(4);
  } >RAM

  .ARM (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
  {
    . = ALIGN(4);
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
    . = ALIGN(4);
  } >RAM

  .preinit_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
    . = ALIGN(4);
  } >RAM

  .init_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
    . = ALIGN(4);
  } >RAM

  .fini_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
    . = ALIGN(4);
  } >RAM

  /* Used by the startup to initialize data */
  _sidata = LOADADDR(.data);

  /* Initialized data sections into "RAM" Ram type memory */
  .data :
  {
    . = ALIGN(4);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */

    . = ALIGN(4);
    _edata = .;        /* define a global symbol at data end */

  } >RAM

  /* Uninitialized data section into "RAM" Ram type memory */
  . = ALIGN(4);
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss section */
    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)

    . = ALIGN(4);
    _ebss = .;         /* define a global symbol at bss end */
    __bss_end__ = _ebss;
  } >RAM

  /* User_heap_stack section, used to check that there is enough "RAM" Ram  type memory left */
  ._user_heap_stack :
  {
    . = ALIGN(8);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(8);
  } >RAM
  
  .MyFlashSection :
  {
    *(.MyFlashSection)
  } > MY_FLASH_SECTION

  /* Remove information from the compiler libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }

  .ARM.attributes 0 : { *(.ARM.attributes) }
}

 

 

Wise is not he who knows many. Wise is he who knows useful. Aeschylus.
HellasT
Associate III

After uploading this code i opened Cubeprogrammer and verified that address 800c000 indeed contained F0F0F0F0 while the rest of the 3rd sector was clear (FFFF). now i opened cubeide again and modified the code so that i write the same value to 800c004 address (that would be the next 4 bytes of sector3) and again opened Cubeprogrammer. So then both 800C000 and 800C004 had F0F0F0F0. That is how i came to the assumption that upon each new code upload, the flash memory is not fully erased.

 

#include <stdint.h>
#define STM32F401xE
#include "stm32f4xx.h"
#include <MyClock.h>
#include <MyFlash.h>

uint32_t MyFlashData[4096] __attribute__((section("MyFlashSection"))) = {0xFF}; // 16KB, Word length is 4 bytes (32 bits).

int main(void)
{	
	
	Clk_speed (SCALE_2_MODE,2,1,1,1,HPRE_0,PPRE1_2,PPRE2_0,4,84,PLLP_2,1,HSE);
	MyFlashWriteWord(0x0800C004, 0xF0F0F0F0);
	while(1)
	{		
	}
}

 

I have to admit though that i had the feeling that this part of the code would somehow clean sector 3 upon code upload.

 

uint32_t MyFlashData[4096] __attribute__((section("MyFlashSection"))) = {0xFF};

 

specially due to the = {0xFF}; part

Wise is not he who knows many. Wise is he who knows useful. Aeschylus.

@HellasT wrote:

 That is how i came to the assumption that upon each new code upload, the flash memory is not fully erased.


Yes, that's what CubeIDE does - other IDEs vary:

https://community.st.com/t5/stm32cubeide-mcus/partial-or-full-chip-erase-before-programming/m-p/734869/highlight/true#M31829

 


@HellasT wrote:

I have to admit though that i had the feeling that this part of the code would somehow clean sector 3 upon code upload.

 

uint32_t MyFlashData[4096] __attribute__((section("MyFlashSection"))) = {0xFF};

 

specially due to the = {0xFF}; part


No.

You can't erase flash by writing 0xFF- that's not how Flash works.

The erase is a separate operation - distinct from writing - which is why we have to go through all this faff when trying to write to Flash!

Follow the links from here:

https://community.st.com/t5/stm32-mcus-products/store-values-in-internal-flash/m-p/667055/highlight/true#M242290

HellasT
Associate III

That makes sense. Well now i just perform sector erase when needed. Ofcourse i also plan to implement some wear leveling technique etc. Thanks !

Wise is not he who knows many. Wise is he who knows useful. Aeschylus.