cancel
Showing results for 
Search instead for 
Did you mean: 

Need help entering bootloader mode and USB DFU via user code

ESpra.1
Senior

I have code that's meant to put an STM32L552xx microcontroller into bootloader mode after reset, so I can connect it to CubeProgrammer afterwards. I tested the code and while it does appear to be doing something, it isn't showing up in the device manager in any sort of update mode, nor can CubeProgrammer find it under the USB tab.

I'm still looking into causes, but I wanted to check to make sure that this wasn't a simple case of me misunderstanding the instructions from AN2606, table 2 (I'm using Bootloader Pattern 12, trustzone disabled and boot0 pin pulled low).

0693W00000VOX0fQAH.pngCode for starting the bootloader from user code is below:

void prepareForUpdate()
{
	uint32_t boot_addr = 0x017F200;
	FLASH_OBProgramInitTypeDef ob_cfg;
	HAL_FLASHEx_OBGetConfig(&ob_cfg);
 
	ob_cfg.OptionType = OPTIONBYTE_USER;
	ob_cfg.USERType = OB_USER_nSWBOOT0;
	ob_cfg.USERConfig = OB_nBOOT0_SET;
	ob_cfg.BootAddrConfig = OB_BOOTADDR_NS0;
	ob_cfg.BootAddr = boot_addr;
 
	HAL_FLASH_Unlock();
	HAL_FLASH_OB_Unlock();
	HAL_FLASHEx_OBProgram(&ob_cfg);
	HAL_FLASH_OB_Launch();
}

When I execute the code, the microcontroller resets but does not appear to enter bootloader mode. I can execute all other code in the project before and after reset, so it isn't outright breaking anything that I can see. I can confirm it can detect the usb, since that's how I tell it to execute other parts of the code. I should also note that I haven't the USB_DEVICE in CUBEMX configured as a Communication Device Class and haven't configured it for DFU or as a composite device. I don't believe that this is the problem, but I'm not writing it off entirely.

I'm going to keep testing and researching for more information to narrow the problem down, but I figured I'd check in to make sure this isn't a simple case of bad code from misunderstanding.

Thanks in advance!

11 REPLIES 11
MM..1
Chief II

Try describe  I can execute all other code in the project before and after reset,

And some changes to OB need POR (power reset) , too some errata and eception exist for bootloaders versions...

There's other code in the project for controlling stepper, DC and servo motors, and communicating with sensors via I2C, which I control by sending characters over USB Virtual Com Port. Before and after running prepareForUpdate(), that side of the code still works the same.

Also, yes, I have tried turning it off and on again (I'm not griping, just making a bad joke).

I'll see if I can dig up more helpful information. Anything else from me that might be helpful?

Maybe i dont ask right. describe how you execute code not what code.

Your prepare func reconfigure MCU to start system bootloader always.

If this is writed into OB your MCU never can start app code self.

Okay, I'm not sure if this is what you're talking about but here's a description of operations. I plug the microcontroller into power, and into a usb port on a PC. I open Tera Term and I, for instance, type 'x'. This command is picked up by the USB CDC and passed into a switch case statement and runs the function inside (operate GPIO, collect data from I2C sensor, etc).

So, I want to put the microcontroller into DFU mode for a firmware update. As part of that, I need to run prepareForUpdate(). I send 'u' via USB CDC, it goes into the switch case statement, finds case 'u' and runs the contents, including prepareForUpdate(). The microcontroller resets (or appears to, based on the brief disconnection and reconnection to Tera Term) and, if things go as I hoped, the microcontroller should now at least be in bootloader mode, and hopefully in DFU mode so I can connect to it via CubeProgrammer. Instead, it seems to go right back into Application Mode.

Compare your code with STM32Cube_FW_F4/main.c at master · fboris/STM32Cube_FW_F4 · GitHub

and check return values from HAL.

Too with SWD read OB and check is realy writed ...

Alright, still not working, but I collected some more information from some debug code.

void prepareForUpdate()
{
	uint32_t boot_addr = 0x017F200;
	FLASH_OBProgramInitTypeDef ob_cfg;
        FLASH_OBProgramInitTypeDef checkConfig;
	HAL_FLASHEx_OBGetConfig(&ob_cfg);
 
	HAL_FLASH_Unlock();
	HAL_FLASH_OB_Unlock();
 
#ifdef TR_DEBUG
	sprintf(debug_buf, "Option Type: %lu\r\nUser Type: %lu\r\nUser Config: %lu\r\nBoot Address Config: %lu\r\nBoot Address: %lu\r\n\r\n", ob_cfg.OptionType, ob_cfg.USERType, ob_cfg.USERConfig, ob_cfg.BootAddrConfig, ob_cfg.BootAddr);
	HAL_Delay(500);
	CDC_Transmit_FS((uint8_t*) debug_buf, sizeof(debug_buf));
	HAL_Delay(500);
	debug_buf[0] = '\0';
#endif
 
	ob_cfg.OptionType = OPTIONBYTE_USER;
	ob_cfg.USERType = OB_USER_nSWBOOT0;
	ob_cfg.USERConfig = OB_nBOOT0_SET;
	ob_cfg.BootAddrConfig = OB_BOOTADDR_NS0;
	ob_cfg.BootAddr = boot_addr;
	HAL_FLASHEx_OBProgram(&ob_cfg);
 
#ifdef TR_DEBUG
	HAL_Delay(500);
	HAL_FLASHEx_OBGetConfig(&checkConfig);
	HAL_Delay(500);
	sprintf(debug_buf, "Option Type: %lu\r\nUser Type: %lu\r\nUser Config: %lu\r\nBoot Address Config: %lu\r\nBoot Address: %lu\r\n\r\n", checkConfig.OptionType, checkConfig.USERType, checkConfig.USERConfig, checkConfig.BootAddrConfig, checkConfig.BootAddr);
	HAL_Delay(500);
	CDC_Transmit_FS((uint8_t*) debug_buf, sizeof(debug_buf));
	HAL_Delay(500);
	debug_buf[0] = '\0';
#endif
 
	if(HAL_FLASH_OB_Launch() != HAL_OK)
	{
 
		sprintf(error_buf, "Error while programming option bytes!");
		HAL_Delay(500);
		CDC_Transmit_FS((uint8_t*) error_buf, sizeof(error_buf));
		HAL_Delay(500);
		while(1)
		{
			HAL_Delay(0);
		}
	}
}

0693W00000VOcfJQAT.pngThis is what's being output on Tera Term. The above screenshot is from two consecutive runs of prepareForUpdate().

I can't really use SWV all that well in this instance. Well, I mean I can, but a successful execution of HAL_FLASH_OB_Launch() improperly terminates the debug session by resetting the microcontroller.

You maybe dont read precise, before get config is

/* Prevent Access to option bytes sector */ 
      HAL_FLASH_OB_Lock();
    
      /* Disable the Flash option control register access (recommended to protect 
      the option Bytes against possible unwanted operations) */
      HAL_FLASH_Lock();
      
      /* Get FLASH_WRP_SECTORS write protection status */
      HAL_FLASHEx_OBGetConfig(&OBInit);

and maybe order is too critical. First OB then flash ... and real programmin is started with Launch , not with OBProg...

And SWD is not only for debug, i dont ask SWV... Try set OB in STMCubeProgrammer GUI

I actually did mess around with that yesterday after I had a little mixup. Almost bricked the USB C side of my NUCLEO board, but was able to fix it with CubeProgrammer.

As for unlocking Flash and OB Flash, it has to be done in the order of Flash, then OB Flash, otherwise it hardfaults. I tested that already.

Circling back to the "almost ruining the USB C" on the NUCLEO board, I realized that when I ran HAL_FLASHEx_OBProgram(), it was only programming the user type and user config option bits, not the boot address or boot config address bits. I split ob_cfg into two (one for user options and one for boot options) then ran HAL_FLASHEx_OBProgram() for both of them, leading to my PC no longer being able to connect to USB C. Additionally, it still wasn't detectable as DFU, though I will note that I currently have USB C configured as a VCP, and not as a composite VCP and DFU device. My research suggested it was unnecessary to do so, but I'm beginning to reconsider.

Edit/Update: I got something! When I went into CubeProgrammer, I changed NSBOOTADD0's value to 0x17f200. The way I had coded it in the version that broke the USB C's connectivity changed NSBOOTADD0's address to 0x17f200. The address should be 0x0bf90000.

That said, you are right; it appears to be locked in bootloader mode until I turn off the option bits. Still, this is good progress I feel

Maybe you waste time with OBs idea. Try jump directly example code for F series, change addr and some

//-----------------------------------------------------------------------------
/**
 * Function to perform jump to system memory boot from user application
 *
 * Call function when you want to jump to system memory
 */
void JumpToBootloader(void) {
	void (*SysMemBootJump)(void);
 
	/**
	 * Step: Set system memory address. 
	 *       
	 *       For other families, check AN2606 document table 110 with descriptions of memory addresses 
	 */
	volatile uint32_t addr = 0x1FFFEC00; //0x1FFF0000;
			
	/**
	 * Step: Disable RCC, set it to default (after reset) settings
	 *       Internal clock, no PLL, etc.
	 */
#if defined(USE_HAL_DRIVER)
	HAL_RCC_DeInit();
#endif /* defined(USE_HAL_DRIVER) */
#if defined(USE_STDPERIPH_DRIVER)
	RCC_DeInit();
#endif /* defined(USE_STDPERIPH_DRIVER) */
	
	/**
	 * Step: Disable systick timer and reset it to default values
	 */
	SysTick->CTRL = 0;
	SysTick->LOAD = 0;
	SysTick->VAL = 0;
 
	/**
	 * Step: Disable all interrupts
	 */
	__disable_irq();
	
	/**
	 * Step: Remap system memory to address 0x0000 0000 in address space
	 *       For each family registers may be different. 
	 *       Check reference manual for each family.
	 *
	 *       For STM32F4xx, MEMRMP register in SYSCFG is used (bits[1:0])
	 *       For STM32F0xx, CFGR1 register in SYSCFG is used (bits[1:0])
	 *       For others, check family reference manual
	 */
	//Remap by hand... {
#if defined(STM32F4)
	SYSCFG->MEMRMP = 0x01;
#endif
//#if defined(STM32F0)
	SYSCFG->CFGR1 = 0x01;
//#endif
	//} ...or if you use HAL drivers
	//__HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();	//Call HAL macro to do this for you
	
	/**
	 * Step: Set jump memory location for system memory
	 *       Use address with 4 bytes offset which specifies jump location where program starts
	 */
	SysMemBootJump = (void (*)(void)) (*((uint32_t *)(addr + 4)));
 
/**
	 * Step: Set main stack pointer.
	 *       This step must be done last otherwise local variables in this function
	 *       don't have proper value since stack pointer is located on different position
	 *
	 *       Set direct address location which specifies stack pointer in SRAM location
	 */
	__set_MSP(*(uint32_t *)addr);
	/**
	 * Step: Actually call our function to jump to set location
	 *       This will start system memory execution
	 */
	SysMemBootJump();
	
	/**
	 * Step: Connect USB<->UART converter to dedicated USART pins and test
	 *       and test with bootloader works with STM32 Flash Loader Demonstrator software
	 */
}