cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 Enter DFU from VCP

McNickel
Associate II

Hello,

I have an application where I would like to switch the STM32G431KB into the built-in bootloader (using USB DFU) by sending a specific byte through the CDC Virtual COM port. I was able to get the unit into DFU mode by running the "USB_BootloaderInit" function below when a button is pressed, however when I try running the function upon reception of data from the CDC port, Windows either does not see the device or gives me a "Device Descriptor Request Failed" notification.

I get the feeling the USB port is still doing some work or there are interrupts still running, but I am not sure how to go about checking what is going on or properly clearing any pending interrupts, or what exactly is going on and I wanted to see if anyone could help point me in the right direction.

This is the function that allows me to enter into the bootloader:

void USB_BootloaderInit()
{
	volatile uint32_t addr = 0x1FFF0000;
	SysMemBootJump = (void (*)(void)) (*((uint32_t *)(addr + 4)));	//Point the PC to the System Memory reset vector
 
	HAL_RCC_DeInit();		//Reset the system clock
	SysTick->CTRL = 0;		//Reset the  SysTick Timer
	SysTick->LOAD = 0;
	SysTick->VAL  = 0;
 
	__set_MSP(*(uint32_t *)addr);
 
	SysMemBootJump();
 
	while(1);
}

It is being called when the correct character is received through the CDC_Receive_FS function as part of the CubeIDE USB Device Middleware.

Currently, I have tried running the USBD_DeInit and USBD_Stop functions before the USB_BootloaderInit function, along with putting some delay (5ms - 1Second using timers) between the DeInit/Stop and BootloaderInit functions, with no avail.

Any help is appreciated

1 ACCEPTED SOLUTION

Accepted Solutions
Uwe Bonnes
Principal II

You need to set VTOR an SYSCFG MEMMODE. But better is to have a switch in early startup code to check for some magicnumber in RAM that you set just before reset when DFU is requested.

View solution in original post

11 REPLIES 11
Uwe Bonnes
Principal II

You need to set VTOR an SYSCFG MEMMODE. But better is to have a switch in early startup code to check for some magicnumber in RAM that you set just before reset when DFU is requested.

TDK
Guru

You can reset the USB peripheral using code similar to:

  // disable USB
  __HAL_RCC_USB_OTG_FS_CLK_ENABLE();
  __HAL_RCC_OTGFS_FORCE_RESET();
  __HAL_RCC_OTGFS_RELEASE_RESET();

If you feel a post has answered your question, please click "Accept as Solution".

Thank you Uwe Bonnes,

I had been thinking about putting a statement in early startup, but I wasn't sure how to go about it so I was avoiding it, but it worked flawlessly.

For anyone who is reading this in the future, here is what I did:

When the correct character is received on the serial port, I run the following function:

void USB_TriggerBootloader()
{
	SYSCFG->MEMRMP &= 0xFFFFFFF9;		//Remap the memory (may not be necessary)
	SYSCFG->MEMRMP |= 1;
	switchToBootloader = 0x11;			//Set the noinit variable to be read by startup code
	NVIC_SystemReset();					//Reset the system
}

the switchToBootloader variable is defined as:

uint8_t switchToBootloader __attribute__ ((section (".noinit")));

The ".noinit" prevents the variable from being re-initialized upon a reset.

After a reset, the first lines of code in the main function are an if statement which checks switchToBootloader and, if it is equal to 0x11, runs the following function:

void (*SysMemBootJump) (void);
 
void USB_BootloaderInit()
{
	switchToBootloader = 0x00;	//Reset the variable to prevent being stuck in the bootloader (since a device reset wont change it)
	volatile uint32_t addr = 0x1FFF0000;	//The STM32G431KB system memory start address
	SysMemBootJump = (void (*)(void)) (*((uint32_t *)(addr + 4)));	//Point the PC to the System Memory reset vector
 
	HAL_RCC_DeInit();		//Reset the system clock
	SysTick->CTRL = 0;		//Reset the  SysTick Timer
	SysTick->LOAD = 0;
	SysTick->VAL  = 0;
 
	__set_MSP(*(uint32_t *)addr);	//Set the Main Stack Pointer
 
	SysMemBootJump();				//Run our virtual function defined above that sets the PC
 
	while(1);
}

And it works reliably!

EDIT: I added the declaration for SysMemBootJump.

How did you define SysMemBootJump?

Otherwise gives me:

error: called object 'SysMemBootJump' is not a function or function pointer

 Update: Never mind...fund it (o;

Still no luck with your code...

It does reset and the switchToBootloader is still set to 0x11....but after USB_BootloaderInit() the device is gone.

Guess something to do with the SYSCFG remapping....

SYSCFG->MEMRMP doesn't exist for the STM32F0 family...

Guess it must be SYSCFG_CFGR1_MEM_MODE then...

Check out AN2606, read the section for the device you are using and also read section 64 to get the system memory address for that device (set “addr�? to the first address listed in the system memory column for the device)

AN2606:

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

The appnote only tells that BOOT0 pin has to be set high.....and I already had the sys mem address correct with 0x1FFFC800.

Now hooked up my Nucleo-G431 board and used your code here....and connected USB via PA11/PA12...

When it hits USB_BootloaderInit() after reset....I don't even see a message then trying to enumerate the bus...

int main(void)
{
......
......
  if(switchToBootloader == 0x11)
  {
	  HAL_Delay(2000);
	  char text[20] = "Switching to DFU\n\r";
	  CDC_Transmit_FS((uint8_t *)text, 18);
	  USB_BootloaderInit();
  }
 
  while (1)
  {
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
	  if(send_usb)
	  {
		  HAL_RTC_GetTime(&hrtc, &currTime, RTC_FORMAT_BIN);
		  HAL_RTC_GetDate(&hrtc, &currDate, RTC_FORMAT_BIN);
 
		  sprintf(buffer,"%02d.%02d.20%02d %02d:%02d:%02d\n\r", currDate.Date, currDate.Month, currDate.Year, currTime.Hours, currTime.Minutes, currTime.Seconds);
 
		  CDC_Transmit_FS((uint8_t *)buffer, 21);
	  	  send_usb = 0;
	  	  counter++;
	  	  if(counter > 10)
	  		USB_TriggerBootloader();
	  }
  }
}
 
/* USER CODE BEGIN 4 */
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
//	HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
	send_usb = 1;
}
 
void USB_TriggerBootloader()
{
	SYSCFG->MEMRMP &= 0xFFFFFFF9;		//Remap the memory (may not be necessary)
	SYSCFG->MEMRMP |= 1;
	switchToBootloader = 0x11;			//Set the noinit variable to be read by startup code
	NVIC_SystemReset();					//Reset the system
}
 
void (*SysMemBootJump) (void);
 
void USB_BootloaderInit()
{
	switchToBootloader = 0x00;	//Reset the variable to prevent being stuck in the bootloader (since a device reset wont change it)
	volatile uint32_t addr = 0x1FFF0000;	//The STM32G431KB system memory start address
	SysMemBootJump = (void (*)(void)) (*((uint32_t *)(addr + 4)));	//Point the PC to the System Memory reset vector
 
	HAL_RCC_DeInit();		//Reset the system clock
	SysTick->CTRL = 0;		//Reset the  SysTick Timer
	SysTick->LOAD = 0;
	SysTick->VAL  = 0;
 
	__set_MSP(*(uint32_t *)addr);	//Set the Main Stack Pointer
 
	SysMemBootJump();				//Run our virtual function defined above that sets the PC
 
	while(1);
}
/* USER CODE END 4 */

I will take a deeper look at your code in a bit, but the first thing i notice: try removing lines 7 - 9 ( The delay and CDC transmit). I had to add the whole reset part because I was only seeing an error when it was triggered by USB, so i presume there is maybe an interrupt or a task that hasn't been cleared that is causing the issue.

Do the CDC functions work correctly? Are you getting data to/from the computer?

EDIT: also make sure the if switchToBootloader statement, line 5, is the absolute first piece of code in your main function, prior to all of the initializations.

Okay...will have a deeper look tomorrow morning (o;

Well for the CDC functions...it sends every second the RTC date/time out without problems....though there is no code yet for receiving...

You mean that switchToBootloader part has to come even before the pre-defined HAL initializations?

Ah that might cause the problem...

Well would be great if that was it so I can soon publish my programmable thermocouple USB logger hard/software project on github for everyone (o;

Will keep you updated...

many thanks

richard