How to Write to Flash Memory (STM32 Microcontroller)

The grand plan is to implement a bootloader. A bootloader should be able to read and write to its flash memory. In my previous post, I wrote about how to read from flash memory. In this post, I will show how to write to flash memory. I am using an ARM Cortex-M4 microcontroller from ST, STM32F411CE.

About the controller:

Microcontroller: STM32F411CE

Flash Memory size: 512KB

RAM size: 128KB

Must erase flash memory first (in page/sector):

A write operation cannot be performed over existing data. Therefore, the page/sector where the address to be written should be erased first. In flash, an erase operation is done in pages/sector, not as a single address.

For example, if I want to write some data to the address 0x080600A0 in STM32F411CE controller, then I have to erase sector 7. Then I should be able to write to that address.

Something to remember, the firmware starts at page/sector 0 and increments sequentially to page/sector 1 and so on. To use the flash memory as a non-volatile memory, it is best to pick the page/sector at the end. This way, the chance of erasing the base firmware is reduced.

Write data size options:

Though the erase operation is done in pages/sectors, with write operations we get granular control. We can write a byte, half world, full word or even double word!

Data Size
Double Word 8 bytes (64 bits)
1 Word 4 Bytes (32 bits)
Half Word 2 Bytes (16 bits)
1 Byte 8 bits

Flash Memory organization (sectors/pages):

I am using STM32F411CE which has a memory capacity of 512KB. The flash memory is organized into pages/sectors as given in the datasheet (image below). Unfortunately, their (ST) terminology flash and sectors are kind of confusing, but they got to be treated the same. They are the same. Just named different.

clip_image002

Flash Erase Function:

In this case we are going to be erasing Sector7 of the memory which spans 128KB from address

0x0806 0000 to 0x0807 FFFF. Also, before an erase and write operation is done, the flash memory needs to be unlocked using the function HAL_FLASH_Unlock() and locked using HAL_FLASH_Lock().

The erase function is as follows:

clip_image004

It is always better to use the last address space/pages/sector to store values since the main application starts from page/sector 0 and we don’t want to overwrite our application accidentally. In this case it would be sector 7 which ranges from 0x0806 0000 to 0x0807 FFFF, so the erase initialization will look like

HAL_FLASH_Unlock(); //unlock the flash

FlashErase.Sector = FLASH_SECTOR_7;
FlashErase.NbSectors = 1;
FlashErase.TypeErase = FLASH_TYPEERASE_SECTORS;
FlashErase.VoltageRange = VOLTAGE_RANGE_3;

HAL_FLASH_Lock(); //lock the flash

That’s all it takes to do the erase operation. Make sure there is . From here onwards, we should be able to write any data into the erased sector. The write to flash memory is done in the following way.

FlashMemAddress = 0x08060000; //Start writing from the 1st address
for(int i = 0; i < rxcount; i++)
{
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE,FlashMemAddress, RxData[i]);
    FlashMemAddress += 1; // 1 byte increment
}

RxData[64] is my UART read buffer where I store the values typed on the console. Once it detects a carriage return, then It will erase the flash and write the data starting from the address 0x08060000. The UART runs on interrupt and here is how the code looks like on receive callback function (interrupt every 1 character)

You can download the whole project : STM32_Project_Snippets/STM32F411_WriteFlash at main · singularengineer/STM32_Project_Snippets (github.com)

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
      if((rxBuf == '\n')|| (rxcount >= 64)) //keep filling the RxData buffer until new line or 64 size
      {
          printf("Deleting flash...\r\n");

          HAL_FLASH_Unlock(); //unlock the flash

          FlashErase.Sector = FLASH_SECTOR_7;
          FlashErase.NbSectors = 1;
          FlashErase.TypeErase = FLASH_TYPEERASE_SECTORS;
          FlashErase.VoltageRange = VOLTAGE_RANGE_3;

          HAL_FLASHEx_Erase(&FlashErase, &SectorError); //The erase function
          printf("Delete status : %08lx \r\n",SectorError);
          printf("writing data: %s\r\n", RxData);


          FlashMemAddress = 0x08060000; //Start writing from the 1st address
          for(int i = 0; i < rxcount; i++)
          {
              HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE,FlashMemAddress, RxData[i]);
              FlashMemAddress += 1; // 1 byte increment
          }

          HAL_FLASH_Lock(); //lock the flash
          memset(RxData,0x00,rxcount);
          rxcount = 0;

      }
      else
      {
          RxData[rxcount++] = rxBuf;
      }
      HAL_UART_Receive_IT(&huart1, &rxBuf, 1);
}

Here is the output written console and then to the memory.

clip_image005

Snapshot of the flash memory

clip_image007