cancel
Showing results for 
Search instead for 
Did you mean: 

Trouble entering DFU on STM32F401 (B)

rsturmer-410
Associate II

I am attempting to enter the system memory bootloader and reload my microcontroller via DFU on USB.  I am having great difficulty getting it to work - I will detail the things I've tried below:

I have tried entering the bootloader via software and also by holding the BOOT0 pin low and resetting the part.  I can enter the bootloader by both methods, but in neither case does the HSE start, nor the USB enumerate properly.  When connected to a windows PC, I get "Device Descriptor Request Failed" if I check in the device manager.

When attempting to enter the bootloader via software, I use the following method:  In the program below, I set _BOOTLOADER_TOKEN to BOOTLOADER_MAGIC_CODE and perform an NVIC_Reset - BOOTLOADER_TOKEN is reserved in my linker definition file, so it is not cleared by startup code, and I check for it at startup.  This code executes in main() after HAL_Init() but before I have initialized any peripherals or clocks or anything.  The behavior is the same even if I call this code before HAL_Init():

 

  // Check the magic address
  if (_BOOTLOADER_TOKEN == BOOTLOADER_MAGIC_CODE)
  {
      // Clear the magic code to prevent re-entering the bootloader after reset
      _BOOTLOADER_TOKEN = 0;

      // Jump to the system bootloader
      // First, disable all global interrupts
      __disable_irq();

      // The system bootloader address for STM32F407
      void (*SysMemBootJump)(void) = (void (*)(void)) (*((uint32_t *) 0x1FFF0004));

      SYSCFG->MEMRMP = 0x01;
      // Set the main stack pointer to the bootloader's stack pointer
      __set_MSP(*(uint32_t *) 0x1FFF0000);

      // Jump to bootloader
      SysMemBootJump();

      // Should never reach here
      while (1);
  }

 

 

When I use either method to enter the bootloader, I can attach and see that the program is indeed running, and it appears to be running "real" code in the bootloader section of memory.  I get addresses such as (for example) 0x1fff07b2, 0x1fff05e2, 0x1fff0672, 0x1fff07e2 - this feels like it's running "real" program and not just spinning in a hardfault handler or something.  I have no debug information for this area of code, so without decompiling it, or stepping through assembly instructions, I don't really know what it's doing.

Per AN2606, once it's done the detection phase of determining whether it should be listening on other peripherals, the bootloader is supposed to check for a USB cable (not clear what method it uses) and then start the HSE and enter DFU mode if a cable is present. I don't think it is doing this, because I don't see so much as a blip on the HSE output when I plug in the USB cable, or indeed any time after booting into the reloader.  If the HSE was "trying" to start but didn't have the startup time necessary per the RCC configuration, I feel like I would at least see an attempted start.  I have my scope setup to trigger at a fairly low voltage, and have disabled glitch filtering, etc.

There's always the chance that the bootloader is mistakenly deciding to listen on one of the other interfaces for reload.  I don't know how to definitively determine if it is doing this, but I have reduced the chance of this, by strapping all of the pins indicated in the application note that it is listening on.  Namely:

  • PA7 (SPI1 MOSI) is actually tied to a SPI peripheral, but I have added a pull-down resistor and confirmed on the scope that there is no chatter or traffic at boot.
  • PB15 (SPI2 MOSI) is a gpio in my application, with a pullup
  • PC12 (SPI3 MOSI) is also a gpio in my application, with a pulldown
  • PD6 (USART2 RX) is not a pin on my micro variant
  • PA10 (USART1 RX) is a LCD select signal in my application that floats, but I have added a 10k pulldown to keep it in a known state when reset
  • PB6/PB7 (I2C1 SCL/SDA) are connected to the UART of another micro in my application.  I have put this micro into a state of the UART transceiver being initialized but it not sending or reciving anything
  • PB3/PB10 (I2C2 SCL/SDA) - PB10 is a GPIO which I have strapped to ground.  PB3 is UNCONNECTED (is this possibly my issue?) - I have yet to confirm its state at startup
  • PA8/PB4 (I2C3 SCL/SDA) - These are actually connected to an I2C accelerometer, but the micro is the master in this relationship - (is this possibly my issue?)

 

Finally, the application note indicates that it is important that the USB be unconnected during the initial detection phase for the other peripherals.  I have ensured this (tried it both ways) and it doesn't seem to make a difference.

I've spent a day on this - almost at wits end!  What could it be?! Is there any way I can more thoroughly determine what the bootloader is actually doing internally (what peripheral it thinks it is listening on) or anything else I can easily try to get it to behave properly?

 

Thank you for any help

1 ACCEPTED SOLUTION

Accepted Solutions
rsturmer-410
Associate II

For posterity, I got this resolved!

I think the magic trick is the RCC_Deinit() - It looks like maybe the clock configuration survives the NVIC_Reset() and is mucking things up.  I'm not certain of this, but at least on my setup, the code below works every time:

 

  // Check the magic address
  if (_BOOTLOADER_TOKEN == BOOTLOADER_MAGIC_CODE)
  {
      // Clear the magic code to prevent re-entering the bootloader after reset
      _BOOTLOADER_TOKEN = 0;

      // Reset the other micros so they don't cause a ruckus
      __HAL_RCC_GPIOB_CLK_ENABLE();
      HAL_GPIO_WritePin(GPIOB, MC_RESET_Pin|HEATER_RESET_Pin, GPIO_PIN_RESET);

      // Jump to the system bootloader
      // First, disable all global interrupts
		/* Disable all interrupts */
		__disable_irq();

		/* Disable Systick timer */
		SysTick->CTRL = 0;

		/* Set the clock to the default state */
		HAL_RCC_DeInit();

		/* Clear Interrupt Enable Register & Interrupt Pending Register */
		for (int i=0;i<5;i++)
		{
			NVIC->ICER[i]=0xFFFFFFFF;
			NVIC->ICPR[i]=0xFFFFFFFF;
		}

		/* Re-enable all interrupts */
		__enable_irq();

      // The system bootloader address for STM32F407
      void (*SysMemBootJump)(void) = (void (*)(void)) (*((uint32_t *) 0x1FFF0004));

      // Set the main stack pointer to the bootloader's stack pointer
      __set_MSP(*(uint32_t *) 0x1FFF0000);

      // Jump to bootloader
      SysMemBootJump();

      // Should never reach here
      while (1);
  }

View solution in original post

3 REPLIES 3
rsturmer-410
Associate II

Oh, 2 more things:

The USB is fully functional in my application.  Works fine as a CDC device.  There is no pullup on DP

The HSE is fully functional in my application.  No problems starting up, and runs neatly at 8MHz.

rsturmer-410
Associate II

One more update:

It seems I was mistaken, or perhaps the behavior is intermittent - if I use the BOOT0 pin to jump to the bootloader, I can actually enter it and the USB enumerates properly - so there seems to be something different between entering via software vs via "hardware" - this feels like progress at least but I'm still a bit stuck.

rsturmer-410
Associate II

For posterity, I got this resolved!

I think the magic trick is the RCC_Deinit() - It looks like maybe the clock configuration survives the NVIC_Reset() and is mucking things up.  I'm not certain of this, but at least on my setup, the code below works every time:

 

  // Check the magic address
  if (_BOOTLOADER_TOKEN == BOOTLOADER_MAGIC_CODE)
  {
      // Clear the magic code to prevent re-entering the bootloader after reset
      _BOOTLOADER_TOKEN = 0;

      // Reset the other micros so they don't cause a ruckus
      __HAL_RCC_GPIOB_CLK_ENABLE();
      HAL_GPIO_WritePin(GPIOB, MC_RESET_Pin|HEATER_RESET_Pin, GPIO_PIN_RESET);

      // Jump to the system bootloader
      // First, disable all global interrupts
		/* Disable all interrupts */
		__disable_irq();

		/* Disable Systick timer */
		SysTick->CTRL = 0;

		/* Set the clock to the default state */
		HAL_RCC_DeInit();

		/* Clear Interrupt Enable Register & Interrupt Pending Register */
		for (int i=0;i<5;i++)
		{
			NVIC->ICER[i]=0xFFFFFFFF;
			NVIC->ICPR[i]=0xFFFFFFFF;
		}

		/* Re-enable all interrupts */
		__enable_irq();

      // The system bootloader address for STM32F407
      void (*SysMemBootJump)(void) = (void (*)(void)) (*((uint32_t *) 0x1FFF0004));

      // Set the main stack pointer to the bootloader's stack pointer
      __set_MSP(*(uint32_t *) 0x1FFF0000);

      // Jump to bootloader
      SysMemBootJump();

      // Should never reach here
      while (1);
  }