cancel
Showing results for 
Search instead for 
Did you mean: 

What causes HAL_FLASH_OB_Unlock() to call HardFaultHandler()? It calls WRITE_REG(FLASH->OPTKEYR, FLASH_OPTKEY1); and jumps directly to HardFaultHandler().

KiptonM
Lead

The HAL_FLASH documentation is especially poor.

There in no overview of what you have to do.

You would think (because of lack of anything saying different) you have to

HAL_FLASH_OB_Unlock()

HAL_FLASH_Program(...)

HAL_FLASH_OB_Lock()

The first command HAL_FLASH_OB_Unlock() crashes.

HAL_FLASH_Program(...) says the format is

HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data)

Nowhere in the HAL Documentation's (UM1940) 1441 pages does it say what "TypeProgram" is, what it is used for, or what it should be.

It says TypeProgram indicates the way to program at a specific address. The value can be a value of FLASH Type Program. What is FLASH Type Program?

On page 234 it has

FLASH Type Program

FLASH_TYPEPROGRAM_BYTE

FLASH_TYPEPROGRAM_HALFWORD

FLASH_TYPEPROGRAM_WORD

FLASH_TYPEPROGRAM_DOUBLEWORD

I cannot program that with a byte or a halfword, or a word. it can only be a word.

Looking into the code for

HAL_FLASH_Program() in stm32g0xx_hal_flash.c it says

/**

 * @brief Program double word or fast program of a row at a specified address.

 * @param TypeProgram Indicate the way to program at a specified address.

 *           This parameter can be a value of @ref FLASH_Type_Program

 * @param Address Specifies the address to be programmed.

 * @param Data Specifies the data to be programmed

 *        This parameter is the data for the double word program and the address where

 *        are stored the data for the row fast program depending on the TypeProgram:

 *        TypeProgram = FLASH_TYPEPROGRAM_DOUBLEWORD (64-bit)

 *        TypeProgram = FLASH_TYPEPROGRAM_FAST (32-bit).

 *

 * @retval HAL_StatusTypeDef HAL Status

 */

So one of the options is FLASH_TYPEPROGRAM_FAST. But that is not on the list on page 234! Why?

The macro

#define IS_FLASH_TYPEPROGRAM(__VALUE__)        (((__VALUE__) == FLASH_TYPEPROGRAM_DOUBLEWORD) || \

                            ((__VALUE__) == FLASH_TYPEPROGRAM_FAST))

It does not say any of the other values are valid from Page 234.

I still do not know why HAL_FLASH_OB_Unlock() crashes. It is the first step.

Kip

37 REPLIES 37
Piranha
Chief II

> I cannot find DEV_FLASH_KEY1.

These key constants are indeed defined by me. The reason for this is simple - ST has not defined those in register definition files. Such constants are only defined in HAL files, not even LL. The best would be to define those with the same CMSIS style FLASH_KEYR_KEYR_KEY1 (PERIPHERAL_REGISTER_BITFIELD_VALUE) in register definition files, but...

> cannot find FLASH_OPTR_nSWBOOT0, it calls it FLASH_OPTR_nBOOT0

Those two are not the same! But in this case it doesn't matter because that is a code from my project. Just redefine BSP_FLASH_OPTR with your desired values. I defined it as a hexadecimal default value for my MCU "minus" some specific bits, but that's just how I choose to format it.

stm32g0xx.h (or similar) should have the direct definitions of registers and bitfields, might make it easier to adapt Piranha's code.

Registers and bit fields are there, but not the specific field values. In this case KEY1 and KEY2 are just a two specific values for a KEYR bit field.

KiptonM
Lead

With thanks to Piranha I have it working. Here is my version of his code.

#define CLEAR_BITS        (~(FLASH_OPTR_nBOOT_SEL))
#define SET_BITS          (FLASH_OPTR_nBOOT0 | FLASH_OPTR_nBOOT1)
#define BSP_FLASH_OPTR    ((FLASH->OPTR & CLEAR_BITS) | SET_BITS)
 
void BSP_SyncOptionBytes(void)
{
	char buf[100];
 
	uint32_t x = *((uint32_t *) 0x1FFF7800);
	sprintf(buf,"\r\nStarting BSP_SyncOptionBytes() *0x1FFF7800: 0x%08lX\r\n",x);
	print_debug_str(buf);
 
 
	sprintf(buf,"FLASH_OPTR %08lX, BSP_FLASH_OPTR %08lX\r\n",FLASH->OPTR, BSP_FLASH_OPTR);
	print_debug_str(buf);
	HAL_Delay(5000);  // Give it time to finish printing.
 
 
	if (FLASH->OPTR == BSP_FLASH_OPTR) {
		return;
	}
 
	if (FLASH->CR & FLASH_CR_LOCK) {
		FLASH->KEYR = FLASH_KEY1;
		FLASH->KEYR = FLASH_KEY2;
	}
	if (FLASH->CR & FLASH_CR_OPTLOCK) {
		FLASH->OPTKEYR = FLASH_OPTKEY1;
		FLASH->OPTKEYR = FLASH_OPTKEY2;
	}
 
	FLASH->OPTR = BSP_FLASH_OPTR;
	FLASH->CR = FLASH_CR_OPTSTRT;
	while (FLASH->SR & FLASH_SR_BSY1);
 
	FLASH->CR = FLASH_CR_OBL_LAUNCH;
}

Thanks all who helped. This was a tough one. Of course I did not help that I did not understand at first that you had to write to registers as opposed to Flash.

We know the HAL code does not work for the STM32G031K6T6.

Hey KiptonM,

That looks great! I am hoping you could help me as well...

I am using a Nucleo - L476RG board and would like to enter DFU mode from the software.

I just dont want to copy and paste your code until i understand more about what i am doing.

From the Documentation:

https://www.st.com/resource/en/reference_manual/rm0351-stm32l47xxx-stm32l48xxx-stm32l49xxx-and-stm32l4axxx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf

and

https://www.st.com/resource/en/application_note/cd00167594-stm32-microcontroller-system-memory-boot-mode-stmicroelectronics.pdf

it says i need to use Pattern 7:

Boot0(pin) = 1, nBoot1(bit) = 1 and BFB2(bit) = 0

to enter DFU mode.

How could I achieve that using the code snippet you posted above?

Would simply changing the defines above to:

#define CLEAR_BITS    (~(FLASH_OPTR_nBOOT_SEL))

#define SET_BITS     (FLASH_OPTR_BFB2 | FLASH_OPTR_nBOOT1)

#define BSP_FLASH_OPTR  ((FLASH->OPTR & CLEAR_BITS) | SET_BITS)

also, what is the purpse of:

uint32_t x = *((uint32_t *) 0x1FFF7800); ?

Is it in use in the code?

Thanks for your help

Almog

Sorry I have been out for all of May and just got to checking this today.

Boot0(pin) = 1, nBoot1(bit) = 1 and BFB2(bit) = 0

to enter DFU mode.

 

How could I achieve that using the code snippet you posted above?

 

Would simply changing the defines above to:

 

#define CLEAR_BITS    (~(FLASH_OPTR_nBOOT_SEL))

#define SET_BITS     (FLASH_OPTR_BFB2 | FLASH_OPTR_nBOOT1)

#define BSP_FLASH_OPTR  ((FLASH->OPTR & CLEAR_BITS) | SET_BITS)

I do not think so. In your case you want to clear BFB2(bit) = 0

#define CLEAR_BITS    (~(FLASH_OPTR_BFB2))

And you want to set Boot0(pin) = 1, nBoot1(bit) = 1

#define SET_BITS     (FLASH_OPTR_nBOOT_SEL | FLASH_OPTR_nBOOT1)

You combine the same way

BSP_FLASH_OPTR  ((FLASH->OPTR & CLEAR_BITS) | SET_BITS)

also, what is the purpose of:

uint32_t x = *((uint32_t *) 0x1FFF7800); ?

Is it in use in the code?

Let's step through that code.

0x1FFF7800 is a number;

(uint32_t *) 0x1FFF7800) says that number is an address for 32-bit unsigned data.

*((uint32_t *) 0x1FFF7800) says get the value stored in that 32-bit unsigned address.

uint32_t x = *((uint32_t *) 0x1FFF7800); says get the value stored in that 32-bit unsigned address and put it in x.

I am then printing the value to see what it is. (For debugging purposes)

Why am I interested in the data in that location? Page 81 of RM0444 says that is the Flash memory address of the "User and read protection option bytes"

That is what gets changed by the code. I wanted to see what it was before I started.

Thank you very much for your reply!

Changing my code to what you suggested and adding the following:

  HAL_RCC_DeInit();
  HAL_DeInit();
 
  __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();
//  /* Initialize user application's Stack Pointer */
  __ASM volatile("movs r3, #0\nldr r3, [r3, #0]\nMSR msp, r3\n"
                 :
                 :
                 : "r3", "sp");
 
  ((void (*)(void)) * ((uint32_t*)(0x00000000 + 4)))();
  while (1) {
  }

Allows me to enter DFU mode and upgrade my device remotely.

Cheers!

#define BSP_FLASH_OPTR    ((FLASH->OPTR & CLEAR_BITS) | SET_BITS)
 
if (FLASH->OPTR == BSP_FLASH_OPTR) {
 
FLASH->OPTR = BSP_FLASH_OPTR;

Putting a register read in that macro defeats the original purpose of that macro, which was to compare the FLASH_OPTR register against a compile-time constant. Now there are three FLASH_OPTR register reads in total and the same register is read twice even for a single comparison. Also it compares only some bits, not all, and generally looks at least weird. If one needs to ensure a specific state of only some bits, then a simple variables should be used:

uint32_t rOPTR = FLASH->OPTR;
uint32_t rOPTR_ref = (rOPTR & CLEAR_BITS) | SET_BITS;
 
if (rOPTR == rOPTR_ref) {
 
FLASH->OPTR = rOPTR_ref;