FLASH Programming in STM32

Description

Every microcontroller have some memory allocated for the user flash. Today We will use this memory to store some data in it. The benefit of using this flash memory is that, even after the power disconnect, the data remains safe in the flash memory.

This tutorial is devided into two halves. The first Half will cover those microcontrollers, whose memory is divided into pages. For eg- Cortex M3, and M0 series. The second Half is for those microcontrollers, whose memory is divided into Sectors

FLASH PAGE TYPE

For the first half, I am using STM32F103 microcontroller, and you can see the memory distribution in the picture below

As you can see above that the main memory (Flash memory) is distributed in 128 pages.
Each page is of 1 KiloByte, thus making the total memory of 128 KiloByte

Now always remember that we should start programming as lower as possible in the flash memory. This is because the start of the flash is already allocated to the program, that is being executed on your controller right now. I have explained it properly in the video, you can check that out in the end.

Each page can hold 1024 Bytes of data, and i don’t have much data to write, so I will choose the last page of the flash memory, i.e. 0x0801FC00 – 0x0801FFFF

Some insight into the CODE

FLASH_PAGE.c Library file contains 2 functions. One is for writing data to the flash, and another is for reading from it

uint32_t Flash_Write_Data (uint32_t StartPageAddress, uint32_t * DATA_32)
{

	static FLASH_EraseInitTypeDef EraseInitStruct;
	uint32_t PAGEError;
	int sofar=0;

	int numberofwords = (strlen(DATA_32)/4) + ((strlen(DATA_32) % 4) != 0);

	  /* Unlock the Flash to enable the flash control register access *************/
	   HAL_FLASH_Unlock();

	   /* Erase the user Flash area*/

	  uint32_t StartPage = GetPage(StartPageAddress);
	  uint32_t EndPageAdress = StartPageAddress + numberofwords*4;
	  uint32_t EndPage = GetPage(EndPageAdress);

	   /* Fill EraseInit structure*/
	   EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES;
	   EraseInitStruct.PageAddress = StartPage;
	   EraseInitStruct.NbPages     = ((EndPage - StartPage)/FLASH_PAGE_SIZE) +1;

	   if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK)
	   {
	     /*Error occurred while page erase.*/
		  return HAL_FLASH_GetError ();
	   }

	   /* Program the user Flash area word by word*/

	   while (sofar<numberofwords)
	   {
	     if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, StartPageAddress, DATA_32[sofar]) == HAL_OK)
	     {
	    	 StartPageAddress += 4;  // use StartPageAddress += 2 for half word and 8 for double word
	    	 sofar++;
	     }
	     else
	     {
	       /* Error occurred while writing data in Flash memory*/
	    	 return HAL_FLASH_GetError ();
	     }
	   }

	   /* Lock the Flash to disable the flash control register access (recommended
	      to protect the FLASH memory against possible unwanted operation) *********/
	   HAL_FLASH_Lock();

	   return 0;
}

Flash_Write_Data takes the following parameters
StartPageAddress is the Start address of the page, or memory in the page, from where you want to start writing the data
DATA_32 is the address of the 32 bit data, that you want to write into the flash

  • It will first count the number of words, that you want to write into the flash
  • Flash will be Unlocked to make modifications
  • Based on the number of words, it will calculate the number of pages required to store that data
  • Next, the required number of pages will be erased, and the new data will be written into them
  • Flash will be Locked again
void Flash_Read_Data (uint32_t StartPageAddress, __IO uint32_t * DATA_32)
{
	while (1)
	{
		*DATA_32 = *(__IO uint32_t *)StartPageAddress;
		if (*DATA_32 == 0xffffffff)
		{
			*DATA_32 = '\0';
			break;
		}
		StartPageAddress += 4;
		DATA_32++;
	}
}

Flash_Read_Data reads the data from the flash. It takes the following parameters

  • StartPageAddress is the Start address of the page, from where you want to start reading the data
  • DATA_32 is the address of the 32 bit variable, where you want to store the data
uint32_t *data = "Hello World";

__IO uint32_t Rx_Data[4];

int main(void)
{
 .......
 .......

 Flash_Write_Data(0x0801FC00 , data);

 Flash_Read_Data(0x0801FC00, Rx_Data);
 
 .......
 .......
}

Above, we are writing the string “Hello World” to the memory location 0x0801FC00, and than we will read data from the same address and store it in the Rx_Data.

Result

data stored in the memory
result flash write and read
Check out the VIDEO Below

100%
100%

FLASH SECTOR TYPE

Some microcontrollers have memory distributed in Sectors.

sector type destribution

In my STM32F446RE, there are 7 sectors and the size is varying between them. Similarly, some microcontrollers can have 11, 15 or 23 sectors. This code will cover all these types, irrespective of the number of sectors the controller have.

IMPORTANT

If the memory for the controller is arranged in dual banks, like Bank1 or Bank2, by defaults these Banks are disabled and the memory uses only single Bank addressing. Check the sector addresses in the single Bank distribution.

Also, if the sector sizes are different than as shown in the picture above, All you need to do is, define new sectors as according to your device datasheet in the function static uint32_t GetSector(uint32_t Address)

Some insight into the CODE

uint32_t Flash_Write_Data (uint32_t StartSectorAddress, uint32_t * DATA_32)
{

	static FLASH_EraseInitTypeDef EraseInitStruct;
	uint32_t SECTORError;
	int sofar=0;

	int numberofwords = (strlen(DATA_32)/4) + ((strlen(DATA_32) % 4) != 0);


	 /* Unlock the Flash to enable the flash control register access *************/
	  HAL_FLASH_Unlock();

	  /* Erase the user Flash area */

	  /* Get the number of sector to erase from 1st sector */

	  uint32_t StartSector = GetSector(StartSectorAddress);
	  uint32_t EndSectorAddress = StartSectorAddress + numberofwords*4;
	  uint32_t EndSector = GetSector(EndSectorAddress);

	  /* Fill EraseInit structure*/
	  EraseInitStruct.TypeErase     = FLASH_TYPEERASE_SECTORS;
	  EraseInitStruct.VoltageRange  = FLASH_VOLTAGE_RANGE_3;
	  EraseInitStruct.Sector        = StartSector;
	  EraseInitStruct.NbSectors     = (EndSector - StartSector) + 1;

	  /* Note: If an erase operation in Flash memory also concerns data in the data or instruction cache,
	     you have to make sure that these data are rewritten before they are accessed during code
	     execution. If this cannot be done safely, it is recommended to flush the caches by setting the
	     DCRST and ICRST bits in the FLASH_CR register. */
	  if (HAL_FLASHEx_Erase(&EraseInitStruct, &SECTORError) != HAL_OK)
	  {
		  return HAL_FLASH_GetError ();
	  }

	  /* Program the user Flash area word by word
	    (area defined by FLASH_USER_START_ADDR and FLASH_USER_END_ADDR) ***********/

	   while (sofar<numberofwords)
	   {
	     if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, StartSectorAddress, DATA_32[sofar]) == HAL_OK)
	     {
	    	 StartSectorAddress += 4;  // use StartPageAddress += 2 for half word and 8 for double word
	    	 sofar++;
	     }
	     else
	     {
	       /* Error occurred while writing data in Flash memory*/
	    	 return HAL_FLASH_GetError ();
	     }
	   }

	  /* Lock the Flash to disable the flash control register access (recommended
	     to protect the FLASH memory against possible unwanted operation) *********/
	  HAL_FLASH_Lock();

	   return 0;
}

Flash_Write_Data will write data to the given memory location.
1.) It will first calculate the number of words, that needs to be written
2.) next, it will calculate the start sector number based on the address entered in the parameter
3.) than, the sector address of the sector where the data is going to end
4.) and finally the sector number of the last sector
5.) It will than erase the required number of sectors and program it with the new data

void Flash_Read_Data (uint32_t StartSectorAddress, __IO uint32_t * DATA_32)
{
	while (1)
	{

		*DATA_32 = *(__IO uint32_t *)StartSectorAddress;
		if (*DATA_32 == 0xffffffff)
		{
			*DATA_32 = '\0';
			break;
		}
		StartSectorAddress += 4;
		DATA_32++;
	}
}

Flash_Read_Data will read the data from the entered memory location, and save it in the DATA_32

oid Convert_To_Str (uint32_t *data, char *str)
{
	int numberofbytes = ((strlen(data)/4) + ((strlen(data) % 4) != 0)) *4;

	for (int i=0; i<numberofbytes; i++)
	{
		str[i] = data[i/4]>>(8*(i%4));
	}
}

Convert_To_Str converts the data to the string

uint32_t *data = "Hello world from Controllerstech";
uint32_t Rx_Data[10];
char string[40];

int main ()
{
  .....
  .....
  Flash_Write_Data(0x0800FFF4 , data);
  Flash_Read_Data(0x0800FFF4 , Rx_Data);
  Convert_To_Str(Rx_Data, string);
  ....
  ....
}

Above, I am writing the data to the memory location, 0x800FFF4, which is somewhere towards the end of SECTOR 3. And this data will partially come in SECTOR 4 also.

Result

sector data result

As you can see above the data is written in both sectors i.e. Sector 3 and Sector 4

Check out the VIDEO Below

100%
100%

DOWNLOAD SECTION

You can buy me a coffee sensor ūüôā

Download the CODE Below

100%
100%

5 3 votes
Article Rating
Subscribe
Notify of
guest
11 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
Menu
11
0
Would love your thoughts, please comment.x
()
x