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.
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:
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.
Snapshot of the flash memory