Skip to main content
Visitor II
October 21, 2026
Question

NUCLEO-WL55JC Change FLASH_OPTR and Enter SPI Bootloader

Hello,

Full application code is based off of LoRaWAN_End_Node_LBM.  I am using the below code to attempt to change the OTPR bits to boot from bootloader (system) for SPI connection.  This code is executed after receiving a command via SPI communications by another microcontroller.  The idea is for the other microcontroller to receive the update file and program the STM32WL55 via SPI.

Pattern 13: nBoot0(bit) = 0, nBoot1(bit) = 1 and nSWBoot0(bit) = 0  

FLASH->CR is 0x00000000

I monitor the FLASH->OPTR register in debug and see that the OPTR does not change from 0x3FFFF0AA before reset.

After reset, I lose connection with debug. I know it is still booting into Flash because it reconnects with our LoRaWAN Gateway.

  1. Am I missing something?  Trying to piece things together with three different documents and trying to determine if BFU_1_Slot is relevant and how it can be modified for SPI. 
  2. Are there any clear example code specifically for my device that already demonstrates what I am attempting to accomplish?
  3. Should I be able to observe the changes in the FLASH->OPTR bits before reset?  Why can’t I change the OPTR register?
  4. Am I understanding this right that the flow chart from AN2606 Figure 118 is the embedded bootloader?  No extra programming needed?
  5. Does the bootloader loop indefinitely until the synchronization byte (0x5A from AN4286) and frame is received?
  6. Do the OPTR register return to its default 0x3FFFF0AA after exiting the bootloader (Go Command)?
HAL_StatusTypeDef ClearnBootSel()
{
uint32_t UserType,UserConfig,RDPLevel;
FLASH_OBProgramInitTypeDef OB;
HAL_FLASHEx_OBGetConfig(&OB);

HAL_FLASH_Unlock();
HAL_FLASH_OB_Unlock();
//UserType = OB_USER_BOOT_LOCK|OB_USER_nSWBOOT0|OB_USER_nBOOT0|OB_USER_nBOOT1;
//UserConfig = OB_BOOT_LOCK_DISABLE|OB_BOOT0_FROM_OB|OB_BOOT0_RESET|OB_BOOT1_SET;
//RDPLevel = OB_RDP_LEVEL_0;
//FLASH_OB_OptrConfig(UserType,UserConfig,RDPLevel);//(uint32_t UserType, uint32_t UserConfig, uint32_t RDPLevel)

OB.OptionType = OPTIONBYTE_USER;
OB.UserType = OB_USER_BOOT_LOCK;
OB.UserConfig = OB_BOOT_LOCK_DISABLE;
if ( HAL_FLASHEx_OBProgram(&OB) != HAL_OK )
{
HAL_FLASH_OB_Lock();
HAL_FLASH_Lock();
return HAL_ERROR;
}

OB.OptionType = OPTIONBYTE_USER;
OB.UserType = OB_USER_nSWBOOT0;
OB.UserConfig = OB_BOOT0_FROM_OB;//OB_BOOT0_RESET;
if ( HAL_FLASHEx_OBProgram(&OB) != HAL_OK )
{
HAL_FLASH_OB_Lock();
HAL_FLASH_Lock();
return HAL_ERROR;
}

OB.OptionType = OPTIONBYTE_USER;
OB.UserType = OB_USER_nBOOT0;
OB.UserConfig = OB_BOOT0_RESET;
if ( HAL_FLASHEx_OBProgram(&OB) != HAL_OK )
{
HAL_FLASH_OB_Lock();
HAL_FLASH_Lock();
return HAL_ERROR;
}

OB.OptionType = OPTIONBYTE_USER;
OB.UserType = OB_USER_nBOOT1;
OB.UserConfig = OB_BOOT1_SET;
if ( HAL_FLASHEx_OBProgram(&OB) != HAL_OK )
{
HAL_FLASH_OB_Lock();
HAL_FLASH_Lock();
return HAL_ERROR;
}

HAL_FLASH_OB_Launch();

// We should not make it past the Launch, so lock flash memory and return an error from function

HAL_FLASH_OB_Lock();
HAL_FLASH_Lock();
return HAL_ERROR;
}