cancel
Showing results for 
Search instead for 
Did you mean: 

What is the correct way to jump from application FW to the DFU bootloader on the STM32F078VB, using a project generated with the latest STM32CubeMX?

ACaru.1
Associate II

I have an existing STM32F078VB firmware application. The following code used to work perfectly fine to jump to the DFU bootloader shortly after power up, based on the application firmware detecting 2 buttons being pressed on the board:

#define BOOT_STACK_ADDR		0x200014A8	/**< Bootloader stack address */
#define SYS_MEM_START_ADDR	0x1FFFC800	/**< System memory start address */
 
void DfuStart(void)
{
	void (*boot_jump)(void);
 
	/**
	 * Disable peripheral clocks
	 */
	__HAL_RCC_GPIOE_CLK_DISABLE();
	__HAL_RCC_GPIOD_CLK_DISABLE();
	__HAL_RCC_GPIOA_CLK_DISABLE();
	__HAL_RCC_GPIOB_CLK_DISABLE();
	__HAL_RCC_GPIOC_CLK_DISABLE();
	__HAL_RCC_GPIOF_CLK_DISABLE();
 
	HAL_DeInit();
	HAL_RCC_DeInit();
 
	/**
	 * Disable systick
	 */
	SysTick->CTRL = 0;
	SysTick->LOAD = 0;
	SysTick->VAL = 0;
 
	/* Disable Timer 6 which is used as HAL tick source. */
	__HAL_RCC_TIM6_CLK_DISABLE();
 
	/* NOTE WELL: Do NOT disable interrupts here! */
 
	/**
	 * Run to stack pointer and execute code from system memory
	 */
	__set_MSP(BOOT_STACK_ADDR);
	boot_jump = (void (*)(void))(*((uint32_t *)(SYS_MEM_START_ADDR + 4)));
	boot_jump();
	while (1);
}

Recently, I upgraded from STM32CubeMX 5.2.1 to version 5.6.0, and regenerated the project files (with no other changes to my application). Now, the above code no longer works, it just resets and restarts the application firmware, rather then entering the DFU bootloader.

Any ideas what changed in the CubeMX generated startup code that would cause this?

1 ACCEPTED SOLUTION

Accepted Solutions
ACaru.1
Associate II

I managed to get this working by moving the relevant code to an earlier point in main(), prior to the various CubeMX-generated calls to initialize peripheral configured in the CubeMX project. Here is the beginning of the new version of main() that works. The 2 GPIOs being checked are push buttons on the board. If they are both pressed at power on the bootloader is entered to allow a DFU update of the firmware.

int main(void)
{
  /* USER CODE BEGIN 1 */
 
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  void (*boot_jump)(void);
 
  /* USER CODE END 1 */
 
  /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* USER CODE BEGIN Init */
 
  __HAL_RCC_GPIOE_CLK_ENABLE();
  GPIO_InitStruct.Pin = SELECT_BUTTON_Pin|RESET_BUTTON_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
  /* If both Select and Reset button held down at boot time, then immediately
   * jump to DFU bootloader, rather than start the Snickerdoodle application.
   */
  if ((HAL_GPIO_ReadPin(RESET_BUTTON_GPIO_Port, RESET_BUTTON_Pin) == GPIO_PIN_RESET) &&
	  (HAL_GPIO_ReadPin(SELECT_BUTTON_GPIO_Port, SELECT_BUTTON_Pin) == GPIO_PIN_RESET))
  {
	  HAL_DeInit();
	  boot_jump = (void (*)(void))(*((uint32_t *)(SYS_MEM_START_ADDR + 4)));
	  __set_MSP(*(__IO uint32_t*)SYS_MEM_START_ADDR);
	  
	  /* NOTE WELL: This call never returns: */
	  boot_jump();
  }
 
  /* USER CODE END Init */
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_ADC_Init();
  MX_SPI2_Init();
  MX_TIM1_Init();
  MX_TIM3_Init();
  MX_USART1_UART_Init();
  MX_USART2_UART_Init();
  MX_CRC_Init();
  MX_RTC_Init();
  /* USER CODE BEGIN 2 */

Following is the original version of main(). You can see that it was not attempting to jump to the bootloader until after the various CubeMX-generated peripheral initialization functions had already been called. Thus, I suspect that the CubeMX-generated HAL de-init code from the latest version of CubeMX does not fully de-initialize something, such that the bootloader cannot execute properly. Unfortunately, I really don't have time to investigate it further now that it is working.

int main(void)
{
  /* USER CODE BEGIN 1 */
 
  /* USER CODE END 1 */
 
  /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* USER CODE BEGIN Init */
 
  /* USER CODE END Init */
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_ADC_Init();
  MX_SPI2_Init();
  MX_TIM1_Init();
  MX_TIM3_Init();
  MX_USART1_UART_Init();
  MX_USART2_UART_Init();
  MX_CRC_Init();
  MX_RTC_Init();
  /* USER CODE BEGIN 2 */
 
  /* If both Select and Reset button held down at boot time, then immediately
   * jump to DFU bootloader, rather than start the Snickerdoodle application.
   */
  if ((HAL_GPIO_ReadPin(RESET_BUTTON_GPIO_Port, RESET_BUTTON_Pin) == GPIO_PIN_RESET) &&
      (HAL_GPIO_ReadPin(SELECT_BUTTON_GPIO_Port, SELECT_BUTTON_Pin) == GPIO_PIN_RESET))
  {
    DfuStart();
  }
 

View solution in original post

3 REPLIES 3
TDK
Guru

What are you doing with BOOT_STACK_ADDR? Do this instead:

#define ApplicationAddress 0x1FFF0000
typedef void (*pFunction)(void);
uint32_t JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
pFunction Jump_To_Boot = (pFunction) JumpAddress;
__set_MSP(*(__IO uint32_t*) ApplicationAddress);
Jump_To_Boot();

I don't think CubeMX is the issue here.

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

Thank you for the suggestion. To answer your question about BOOT_STACK_ADDR, that is the stack pointer value found in the system flash at 0x1FFFC800 (0x1FFF000 is NOT the correct address for the STM32F078). Your suggested code is more elegant, but in fact identical to what I posted, with the exception of the ApplicationAddress value. It does NOT work ever since I regenerated the project files using STM32CubeMX version 5.6.0. It worked perfectly before I upgraded from version 5.2.1. So, I believe the issue is indeed something that changed in the output of CubeMX. It might be possible to deduce what changed if there was a definitive guide or app note on the proper way to jump to the DFU bootloader from application Firmware, but I have not found anything other than AN2606 which clearly does not provide enough information.

ACaru.1
Associate II

I managed to get this working by moving the relevant code to an earlier point in main(), prior to the various CubeMX-generated calls to initialize peripheral configured in the CubeMX project. Here is the beginning of the new version of main() that works. The 2 GPIOs being checked are push buttons on the board. If they are both pressed at power on the bootloader is entered to allow a DFU update of the firmware.

int main(void)
{
  /* USER CODE BEGIN 1 */
 
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  void (*boot_jump)(void);
 
  /* USER CODE END 1 */
 
  /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* USER CODE BEGIN Init */
 
  __HAL_RCC_GPIOE_CLK_ENABLE();
  GPIO_InitStruct.Pin = SELECT_BUTTON_Pin|RESET_BUTTON_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
  /* If both Select and Reset button held down at boot time, then immediately
   * jump to DFU bootloader, rather than start the Snickerdoodle application.
   */
  if ((HAL_GPIO_ReadPin(RESET_BUTTON_GPIO_Port, RESET_BUTTON_Pin) == GPIO_PIN_RESET) &&
	  (HAL_GPIO_ReadPin(SELECT_BUTTON_GPIO_Port, SELECT_BUTTON_Pin) == GPIO_PIN_RESET))
  {
	  HAL_DeInit();
	  boot_jump = (void (*)(void))(*((uint32_t *)(SYS_MEM_START_ADDR + 4)));
	  __set_MSP(*(__IO uint32_t*)SYS_MEM_START_ADDR);
	  
	  /* NOTE WELL: This call never returns: */
	  boot_jump();
  }
 
  /* USER CODE END Init */
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_ADC_Init();
  MX_SPI2_Init();
  MX_TIM1_Init();
  MX_TIM3_Init();
  MX_USART1_UART_Init();
  MX_USART2_UART_Init();
  MX_CRC_Init();
  MX_RTC_Init();
  /* USER CODE BEGIN 2 */

Following is the original version of main(). You can see that it was not attempting to jump to the bootloader until after the various CubeMX-generated peripheral initialization functions had already been called. Thus, I suspect that the CubeMX-generated HAL de-init code from the latest version of CubeMX does not fully de-initialize something, such that the bootloader cannot execute properly. Unfortunately, I really don't have time to investigate it further now that it is working.

int main(void)
{
  /* USER CODE BEGIN 1 */
 
  /* USER CODE END 1 */
 
  /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* USER CODE BEGIN Init */
 
  /* USER CODE END Init */
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_ADC_Init();
  MX_SPI2_Init();
  MX_TIM1_Init();
  MX_TIM3_Init();
  MX_USART1_UART_Init();
  MX_USART2_UART_Init();
  MX_CRC_Init();
  MX_RTC_Init();
  /* USER CODE BEGIN 2 */
 
  /* If both Select and Reset button held down at boot time, then immediately
   * jump to DFU bootloader, rather than start the Snickerdoodle application.
   */
  if ((HAL_GPIO_ReadPin(RESET_BUTTON_GPIO_Port, RESET_BUTTON_Pin) == GPIO_PIN_RESET) &&
      (HAL_GPIO_ReadPin(SELECT_BUTTON_GPIO_Port, SELECT_BUTTON_Pin) == GPIO_PIN_RESET))
  {
    DfuStart();
  }