STM32H5 Daul Bank Implementation

Preface

I previously implemented dual-bank on the L4, and now I’m doing it again on the H5. However, the H5’s dual-bank architecture works differently—after running into some issues, I discovered it maps directly to another memory space. This article provides a deep dive into the H5 series’ dual-bank to help everyone avoid the pitfalls and complete their projects smoothly.

Architecture

In the H5 series, dual-bank is enabled by default, making it different from the G4 or L4. The H5 starts with the memory already split into two independent regions. In older L4 or G4 devices, bank switching relied on the BFB mechanism as shown in the diagram below.

Implementation

To determine the current bank location, it differs from G4 or L4 as it doesn’t rely on BFB, so some adjustments are needed.

uint8_t getActiveBank() {

	
	    /* Get the boot configuration status */
	     HAL_FLASHEx_OBGetConfig(&OBInit);

	     /* Check Swap Flash banks  status */
	     if ((OBInit.USERConfig & OB_SWAP_BANK_ENABLE) == OB_SWAP_BANK_DISABLE)
	     {
	    	 /*Active Bank is bank 1 */
	    	 printf("Active Bank is bank 1\n");
	    	 return BANK1_ACTIVE;

	     }
	     else
	     {
	    	 /*Active Bank is bank 2 */
	    	 printf("Active Bank is bank 2\n");
	    	 return BANK2_ACTIVE;

	     }
}

After identifying the bank, switching is typically done via a system reset, which is faster.
Using direct address jumps requires both banks to have minimal code differences and fixed data addresses to avoid MCU logic errors.
(This behavior is already demonstrated in the provided example code.)

void BT_SWAP_thread_entry(FLASH_OBProgramInitTypeDef OBInit)
{
		      /* Get the boot configuration status */
//		      __disable_irq();
             HAL_FLASH_OB_Unlock();
		      HAL_FLASHEx_OBGetConfig(&OBInit);

		      /* Check Swap Flash banks  status */
		      if ((OBInit.USERConfig & OB_SWAP_BANK_ENABLE) == OB_SWAP_BANK_DISABLE)
		      {

		        /*Swap to bank2 */
		        /*Set OB SWAP_BANK_OPT to swap Bank2*/
		        OBInit.OptionType = OPTIONBYTE_USER;
		        OBInit.USERType = OB_USER_SWAP_BANK;;
		        OBInit.USERConfig = OB_SWAP_BANK_ENABLE;

		        if (HAL_FLASHEx_OBProgram(&OBInit) != HAL_OK) {
		                printf("OBINT Fail\n\r ");
		                while (1) {

		                }
		            }
		        /* Launch Option bytes loading */
		        if (HAL_FLASH_OB_Launch() != HAL_OK) {
		            	printf("OBLaunch Fail\n\r ");
		                while (1) {

		                }
		            }
		        HAL_FLASH_OB_Lock();

		        /* Reset the MCU */
		//        __enable_irq();
		//        __disable_irq();
		        HAL_NVIC_SystemReset();

		      }
		      else
		      {

		        /* Swap to bank1 */
		        /*Set OB SWAP_BANK_OPT to swap Bank1*/
		        OBInit.OptionType = OPTIONBYTE_USER;
		        OBInit.USERType = OB_USER_SWAP_BANK;
		        OBInit.USERConfig = OB_SWAP_BANK_DISABLE;
		        if (HAL_FLASHEx_OBProgram(&OBInit) != HAL_OK) {
		                printf("OBINT Fail\n\r ");
		                while (1) {

		                }
		            }
		        /* Launch Option bytes loading */
		        if (HAL_FLASH_OB_Launch() != HAL_OK) {
		            	printf("OBLaunch Fail\n\r ");
		                while (1) {

		                }
		            }
		        HAL_FLASH_OB_Lock();
		        /* Reset the MCU */
		        HAL_NVIC_SystemReset();

		      }

}

Key Considerations

Since H5 has two completely independent banks, switching between banks also changes the starting address. As shown in the diagram, this includes the accessed flash data. Therefore, when interpreting data, if both banks store different data, the pointer must be redirected accordingly. Priority of data access is based on which bank is currently active.

You can refer to the code snippet below. First, there’s no need to detect the current bank. Instead, simply read different data from each bank for analysis.

	uint32_t DateBank1=0;
    uint8_t Data1_m=0,Data1_h=0,Data2_m=0,Data2_h=0;
    uint32_t DateBank2=0;
    uint8_t bank_inform=0;
    uint32_t QuadWord[4] = {0,0,0,0};//{bank_inform,bank_flag,0,0}
		bank_inform=getActiveBank();
	printf("Bank=%d\n\r",bank_inform);
	if(bank_inform==1)
	{
	  DateBank1 = *(__IO uint32_t *)0x0807C004;//bank 1 VERSION data
	  DateBank2 = *(__IO uint32_t *)0x0817C004;//bank 2 VERSION data need modify 0x080FC004 on 1M flash size
	}
	else
	{
	  DateBank2 = *(__IO uint32_t *)0x0807C004;//bank 1 VERSION data
	  DateBank1 = *(__IO uint32_t *)0x0817C004;//bank 2 VERSION data need modify 0x080FC004 on 1M flash size
	}

Another important point: although the address changes, the flash erase operation still follows the original rules.
For example, if you’re currently in Bank1 and want to erase the 63rd sector in Bank1, the code will use the following declaration.

		  EraseInitStruct.TypeErase     = FLASH_TYPEERASE_SECTORS;
		  EraseInitStruct.Banks         = 1;
		  EraseInitStruct.Sector        = 63;
		  EraseInitStruct.NbSectors     = 1;

		  if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK)
		printf("Erase fail\n\r");

If you’ve switched to Bank2 and still want to erase the 63rd sector in Bank1, the declaration will be the same as if you’re in Bank1.
It does not change depending on the active bank.

		  EraseInitStruct.TypeErase     = FLASH_TYPEERASE_SECTORS;
		  EraseInitStruct.Banks         = 1;
		  EraseInitStruct.Sector        = 63;
		  EraseInitStruct.NbSectors     = 1;

		  if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK)
		printf("Erase fail\n\r");

Leave a Comment

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

Shopping Cart