AnsweredAssumed Answered

How to reliably enter DFU mode after using USB in user code on STM32H7?

Question asked by ben mitch on Apr 14, 2018



On STM32H743, using a Linux machine to connect over USB to the DFU bootloader on the USB.

We are generally having success at entering DFU mode by pulsing reset low with BOOT0 held high. This has worked hundreds of times with our toolchain - good. However, if, and seemingly only if, we have called USBD_Init() in the Cube library in the run-time code, the device will sometimes (apparently at random) refuse stubbornly to enter DFU mode using this (hardware) procedure. What could be going on?


- Everything is working well, usually, but maybe one time in three we do not get a DFU device appearing on the USB bus.


- The device has entered some non-run mode, because the usual run-time blinky is absent, and the current draw is consistent with DFU mode (or bootloader in general, probably). But the DFU does not start.


- Once this condition has occurred, nothing /except/ cycling the power has been able to put us in DFU mode. No matter how many times we pull reset, or how vigorously we raise BOOT0 - no DFU.

- Cycling the power /always/ fixes it, and DFU appears immediately (BOOT0 is held high throughout, until we get DFU access, so the power comes back on with BOOT0 asserted).


- This fault has been common across multiple boards/devices, so it must be in the design.

I've been over the bootloader instructions multiple times, and cannot see what we might be doing wrong. Plus, the fact that this condition appears to be dependent on what we do in the run-time software suggests that some part of the USB system is not quite getting reset by pulling the reset pin low. The problem has now been consistent across four revisions of the board, so I thought it was time to ask for help.


Our best guess so far has been that we are entering some other part of the bootloader (e.g. UART, or SPI). However, I've read AN2606 many times, from all sorts of angles, and I can't find any reason why we should be in one of the other bootloader modes. AN2606 is not clear, to my reading - what is an SPI "synchro mechanism"? - but it seems to be the case that all the other modes require an active intervention (send a character to UART, to SPI, an address to I2C) which we of course are not doing, so we should be free to enter the USB bootloader, but it doesn't happen. The USB cable is firmly present, and VBUS asserted on the pin.


The only other normal failure point in Figure 48 of AN2606 would be HSE failure. Yet, the HSE is used at run-time, and has never failed, to our observations. So why it would fail in the bootloader, I can't see. Could we detect a system reset occurring, if that were happening, or would it be all internal to the device? Certainly, I can't see any activity on the NRST line, and it is implied in the RM that we would see this if the device were generating a system reset.


Drilling down into USBD_Init(), it turns out it is HAL_PCD_Init() which seems to be the breaking point. Inside that, it is the statement __HAL_RCC_USB1_OTG_HS_CLK_ENABLE(). Before we call that, everything works fine. Afterwards, DFU fails randomly. __HAL_RCC_USB1_OTG_HS_CLK_ENABLE() resolves to (in stm32h7xx_hal_rcc.h):


#define __HAL_RCC_USB1_OTG_HS_CLK_ENABLE() do { \
__IO uint32_t tmpreg; \
/* Delay after an RCC peripheral clock enabling */ \
UNUSED(tmpreg); \
} while(0)

That doesn't seem helpful - we enable the peripheral clock to this device, and it causes the problem.


If anyone can shed any light on this, we'll be very grateful. Might even send you some biscuits.


Thanks for any help, Ben