2024-01-30 07:46 AM
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:
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
Solved! Go to Solution.
2024-01-31 05:33 AM
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);
}
2024-01-30 07:47 AM
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.
2024-01-30 09:00 AM
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.
2024-01-31 05:33 AM
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);
}