We flash the program not only to the beginning of the STM32 FLASH memory

Do you know where your program goes after you press the RUN or DEBUG button in the IDE? If so, how to change this address or even go beyond the constant memory and burn directly into RAM.

I work in CubeIDE and my entire interface will be from this development environment.

Let's start with the answer to the first question, if you use the standard IDE settings and take a standard ST-LINK (it doesn't matter if it's the original or a whistle), then when burning the microcontroller, the required number of sectors in the FLASH memory will be cleared and the program will be written in their place, and at the very beginning of the FLASH memory at the address 0x08000000.

Let's stop here for a bit. For most projects this is enough, the program is in FLASH memory, does not disappear when the microcontroller is rebooted and starts up properly. Problems start when the task comes to embed a bootloader that will independently accept the firmware via one of the interfaces, be it UART/USART, I2C or CAN and save it in the required place in FLASH memory, and after rebooting should independently prepare and transfer control to the required program.

Here you can’t do without a little adjustment of the linker script and the code of the program itself.

In CubeIDE, in the root of your project, you will definitely find a file with the extension .ld, this is the linker script in which you can find a description of the initial addresses and sizes of different memory sectors (example STM32F429ZI):

MEMORY
{
  CCRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
  RAM   (xrw) : ORIGIN = 0x20000000, LENGTH = 192K
  FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 2048K
}

Here we see that our chip has two areas of volatile memory: CCRAM and RAM of 64 and 192 KB respectively, and one area of ​​non-volatile memory – FLASH of 2 MB. CCRAM memory starts at address 0x10000000 and, taking into account the size, ends at address 0x10010000, and RAM memory occupies the address space from 0x20000000 to 0x20030000.

I will dwell on FLASH memory in a little more detail. Unlike RAM, it does not allow rewriting any bits thousands of times per second. Firstly, the rewriting resource will run out quite quickly, secondly, in order to change just one bit from 0 to 1 in FLASH memory, you will have to “erase” the entire page, the size of which (in my experience) can reach 128 KB. Therefore, when working with FLASH memory, you should always keep in mind which sector you are currently working in and whether there is anything else important in it. You can find a map of your FLASH memory with all sectors and their sizes in the Reference manual section dedicated to embedded memory (for example, “Embedded flash memory interface”).

The FLASH section in the linker script determines where the program will be loaded, so if you want to change the address where the program will be loaded and, what is no less important, for what address the interrupt table will be compiled, you need to change the address of the beginning of the FLASH memory in the linker script. For example, like this:

Now the program will work from the middle of FLASH memory. IMPORTANT: do not forget to change the size of the area, so as not to go beyond the address space.

MEMORY
{
  CCRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
  RAM   (xrw) : ORIGIN = 0x20000000, LENGTH = 192K
  FLASH (rx)  : ORIGIN = 0x08100000, LENGTH = 1024K
}

You can also run the program from RAM memory, the main thing is not to allow the FLASH and RAM areas to overlap:

MEMORY
{
  CCRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
  RAM   (xrw) : ORIGIN = 0x20025800, LENGTH = 42K
  FLASH (rx)  : ORIGIN = 0x20000000, LENGTH = 150K
}

When loading into RAM, do not forget that after any reset your program will be erased and you will have to burn it again.

BUT, in order for your code to work from the new location in memory that you have defined, you need to make the final touch – write the new address of the beginning of the program into the SCB structure as the very first step in the main function.

#define START_ADDR 0x20000000

int main(void)
{
    /* Пока не будет передан новый адрес таблицы векторов, 
       другие функции не должны вызываться */
    __disable_irq();
    SCB->VTOR = START_ADDR;
    __enable_irq();

    /* Остальная программа начинается тут */
  
    while(1)
    {
    
    }
}

Congratulations, you have taken the first step to working from the bootloader and can now control where your program is burned and stored in the microcontroller's memory.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *