cancel
Showing results for 
Search instead for 
Did you mean: 

Write to STM32 internal flash fails

AR_26
Associate II

On my stm32g0 mcu there is no eeprom. So, I am using internal flash to save one byte user data to retain it between power cycles.I am doing it the following way,

  1. Add Data section in memory in the linker script
MEMORY { 
...... 
   DATA (xrw) : ORIGIN = 0x800F800, LENGTH = 2K //Allocated one full flash page 
 
}

  1. Create user data section

 .user_data : 
 
     { . = ALIGN(4); 
 
       *(.user_data)
 
       . = ALIGN(4);
 
     } >DATA

  1. Create a variable to store in flash

attribute((section(".user_data"))) const uint8_t userConfig[64]

  1. Write data using following functions,

 HAL_FLASH_Unlock();
 
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGSERR );
 
 FLASH_PageErase(flash_page_number);
 
HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, (uint32_t)&userConfig[index], someData);
 
HAL_FLASH_Lock();

When I try to write to the flash it fails with PGSERR flag set.

9 REPLIES 9

A Double Word would need to be a 64-bit aligned address, ie divisible by 8

You can't put in random misaligned addresses, and you can't write the same 8-byte cell twice

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

@Community member​ Thanks for the response! Any suggestions how I can do this correctly?

AR_26
Associate II

@Community member​ I tried doing the following way,

HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, startAddressOfFlashPage, u64_someData);

I still get the same error. After debugging I can see it fails inside FLASH_Program_DoubleWord

static void FLASH_Program_DoubleWord(uint32_t Address, uint64_t Data)
{
  /* Set PG bit */
  SET_BIT(FLASH->CR, FLASH_CR_PG);
 
  /* Program first word */
  *(uint32_t *)Address = (uint32_t)Data;
 
  /* Barrier to ensure programming is performed in 2 steps, in right order
    (independently of compiler optimization behavior) */
  __ISB();
 
  /* Program second word */
  *(uint32_t *)(Address + 4U) = (uint32_t)(Data >> 32U);
}

After writing the first 32 bits. the CFGBSY flag in the FLASH_SR register is set. Then after writing the nest 32 bits, it sets the PGSERR flag in the status register

AR_26
Associate II

Update: Was able to get it working on blocking mode using below code,

	HAL_FLASH_Unlock();
 
	FirstPage = GetPage(FLASH_USER_START_ADDR);
	NbOfPages = GetPage(FLASH_USER_END_ADDR) - FirstPage + 1;
 
	EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
	EraseInitStruct.Page      = FirstPage;
	EraseInitStruct.NbPages   = NbOfPages;
 
	if(HAL_FLASHEx_Erase(&EraseInitStruct, &PageError) != HAL_OK)
	{
		Error_Handler();
	}
 
	Address = FLASH_USER_START_ADDR;
 
	if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, Address, data) != HAL_OK)
	{
		Error_Handler();
	}
	HAL_FLASH_Lock();

But when I try to use interrupt mode APIs, it doesn't works. Below are the APIs I use for interrupt mode

HAL_FLASHEx_Erase_IT(FLASH_EraseInitTypeDef *pEraseInit)
 
 
 
HAL_FLASH_Program_IT(uint32_t TypeProgram, uint32_t Address, uint64_t Data)
 
/**
  * @brief  FLASH end of operation interrupt callback.
  * @param  ReturnValue The value saved in this parameter depends on the ongoing procedure
  *                  Mass Erase: 0
  *                  Page Erase: Page which has been erased
  *                  Program: Address which was selected for data program
  * @retval None
  */
void HAL_FLASH_EndOfOperationCallback(uint32_t ReturnValue)

Piranha
Chief II

So.. have you implemented the ISR function?

As for PGSERR.. Maybe you are calling those functions from interrupts and there are race issues?

AR_26
Associate II

@Piranha​ I do not have the PGSERR error anymore. I got it working as per my answer above.

I am not able to get it working in interrupt mode,

ISR is already in the HAL code generated by CubeIDE,

/**
  * @brief Handle FLASH interrupt request.
  * @retval None
  */
void HAL_FLASH_IRQHandler(void)
{
  uint32_t param = 0xFFFFFFFFU;
  uint32_t error;
 
  /* Save flash errors. Only ECC detection can be checked here as ECCC
     generates NMI */
  error = (FLASH->SR & FLASH_FLAG_SR_ERROR);
  error |= (FLASH->ECCR & FLASH_FLAG_ECCC);
 
  CLEAR_BIT(FLASH->CR, pFlash.ProcedureOnGoing);
 
  /* A] Set parameter for user or error callbacks */
  /* check operation was a program or erase */
  if ((pFlash.ProcedureOnGoing & (FLASH_TYPEPROGRAM_DOUBLEWORD | FLASH_TYPEPROGRAM_FAST)) != 0x00U)
  {
    /* return adress being programmed */
    param = pFlash.Address;
  }
  else if ((pFlash.ProcedureOnGoing & (FLASH_TYPEERASE_MASS | FLASH_TYPEERASE_PAGES)) != 0x00U)
  {
    /* return page number being erased (0 for mass erase) */
    param = pFlash.Page;
  }
  else
  {
    /* Nothing to do */
  }
 
  /* B] Check errors */
  if (error != 0x00U)
  {
    /*Save the error code*/
    pFlash.ErrorCode |= error;
 
    /* clear error flags */
    __HAL_FLASH_CLEAR_FLAG(error);
 
    /*Stop the procedure ongoing*/
    pFlash.ProcedureOnGoing = FLASH_TYPENONE;
 
    /* Error callback */
    HAL_FLASH_OperationErrorCallback(param);
  }
 
  /* C] Check FLASH End of Operation flag */
  if (__HAL_FLASH_GET_FLAG(FLASH_FLAG_EOP) != 0x00U)
  {
    /* Clear FLASH End of Operation pending bit */
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP);
 
    if (pFlash.ProcedureOnGoing == FLASH_TYPEERASE_PAGES)
    {
      /* Nb of pages to erased can be decreased */
      pFlash.NbPagesToErase--;
 
      /* Check if there are still pages to erase*/
      if (pFlash.NbPagesToErase != 0x00U)
      {
        /* Increment page number */
        pFlash.Page++;
        FLASH_PageErase(pFlash.Page);
      }
      else
      {
        /* No more pages to erase: stop erase pages procedure */
        pFlash.ProcedureOnGoing = FLASH_TYPENONE;
      }
    }
    else
    {
      /*Stop the ongoing procedure */
      pFlash.ProcedureOnGoing = FLASH_TYPENONE;
    }
 
    /* User callback */
    HAL_FLASH_EndOfOperationCallback(param);
  }
 
  if (pFlash.ProcedureOnGoing == FLASH_TYPENONE)
  {
    /* Disable End of Operation and Error interrupts */
    __HAL_FLASH_DISABLE_IT(FLASH_IT_EOP | FLASH_IT_OPERR | FLASH_IT_ECCC);
 
    /* Process Unlocked */
    __HAL_UNLOCK(&pFlash);
  }
}

I used the End of Operation callback provided by the HAL to complete the flash write.

Below are steps I follow,

  1. Unlock flash and call erase with interrupt API,
	HAL_FLASH_Unlock();
 
	FirstPage = GetPage(FLASH_USER_START_ADDR);
	NbOfPages = GetPage(FLASH_USER_END_ADDR) - FirstPage + 1;
 
	EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
	EraseInitStruct.Page      = FirstPage;
	EraseInitStruct.NbPages   = NbOfPages;
 
	if(HAL_FLASHEx_Erase_IT(&EraseInitStruct) != HAL_OK)
	{
		Error_Handler();
	}

2.In the EOP callback check for page number which was erased because this call is end of erase operation and call flash program API. And if the return value is the address to be written then lock the flash

/**
  * @brief  FLASH end of operation interrupt callback.
  * @param  ReturnValue The value saved in this parameter depends on the ongoing procedure
  *                  Mass Erase: 0
  *                  Page Erase: Page which has been erased
  *                  Program: Address which was selected for data program
  * @retval None
  */
void HAL_FLASH_EndOfOperationCallback(uint32_t ReturnValue)
{
    if(ReturnValue == flash_page)
    {
        if(HAL_FLASH_Program_IT(FLASH_TYPEPROGRAM_DOUBLEWORD, Address, data) != HAL_OK)
        {
             Error_Handler();
        }
    }
 
    if(ReturnValue == Address)
    {
        HAL_FLASH_Lock();
    }
}

Above implementation works fine until erase, but when it comes into the End of operation callback and flash program is called then it fails and goes to error handler

Piranha
Chief II

HAL is a separate library an is not generated by CubeMX. And HAL_FLASH_IRQHandler() is not an ISR itself - it's a handler that has to be called from the ISR. CubeMX generated code puts ISRs in stm32g0xx_it.c - check that. And someone has to enable FLASH_IRQn in NVIC also!

P.S. That HAL_FLASH_IRQHandler() code... Even looking at it is dangerous - one can die from a heart attack!

AR_26
Associate II

@Piranha​ Sorry my bad, I didn't explain it correct. I already enabled Flash global interrupts. CubeMx generates a ISR in the stm32g0xx_it.c file which calls the HAL_FLASH_IRQHandler()  as below,

/**
  * @brief This function handles Flash global interrupt.
  */
void FLASH_IRQHandler(void)
{
  HAL_FLASH_IRQHandler();
}

Piranha
Chief II

Well, put a breakpoint in that code and step it through. But here is what you'll find out:

  1. HAL_FLASHEx_Erase_IT() calls __HAL_LOCK().
  2. HAL_FLASH_EndOfOperationCallback() is called from here. Notice that __HAL_UNLOCK() is called only after the callback.
  3. HAL_FLASH_Program_IT() calls __HAL_LOCK() and returns HAL_BUSY.

Why? Because HAL bloatware is written by incompetent fools. It's so "not perfect", that it's useless. If you would write your own code, you would get this simple thing working in a time shorter than our conversation here and it would take less lines of code than you've written now just trying to use FLASH with the "helpful" HAL... ;)