通过应用程序跳转到STM32中的Bootloader,即在用户闪存的引导模式下使用Boot 0和Boot 1 Pins

时间:2014-11-12 16:08:51

标签: c usb stm32 dfu

我需要固件升级。我打算使用USB DFU类。但在我的情况下,固件升级命令将来自PC应用程序。所以我需要切换到系统内存中的bootloader。最初我正在运行应用程序,因此它从用户闪存启动,即我为用户闪存配置了Boot0和Boot 1引脚。由于DFU引导加载程序存在于系统闪存中,现在需要更改Boot0和Boot1引脚设置。有没有像Boot 0和Boot 1设置保持用户闪存一样的方法,在应用程序中我们跳转到系统内存?

5 个答案:

答案 0 :(得分:22)

仅在处理器启动时对Boot0 / 1引脚进行采样,以检查是否应从内存加载用户代码或是否应加载引导加载程序。 之后这些引脚的状态对引导加载程序没有影响。

我遇到了类似的请求,并找到了2种按需加载引导加载程序的方法。

首先,你可以" JUMP"从用户代码到引导加载程序。例如,您可以在按下按钮时跳转到引导加载程序。

但是......这比简单的JUMP指令复杂得多:一些寄存器和设备必须正确配置才能与引导加载程序配合使用,你必须确保在JUMP期间不会触发IRQ,...事实上,您必须重新配置处理器,就像它在重置后刚刚启动一样。 您可以找到有关此技术的一些信息:on this video from ST

我设法在STM32F1xx项目上做了这样的事情。 然而,在基于STM32F4的更复杂的项目中,这将变得非常困难......我将不得不停止所有设备(定时器,通信接口,ADC,DAC ......),确保不会触发IRQ,重新配置所有时钟,......

相反,我决定实现第二个解决方案:当我想跳转到引导加载程序时,我在其中一个备份寄存器中写入一个字节,然后发出软复位。然后,当处理器重新启动时,在程序的最开始,它将读取该寄存器。该寄存器包含指示应在引导加载程序模式下重新引导的值。然后,跳转到引导加载程序要容易得多,如the youtube video中所示。

答案 1 :(得分:4)

In MicroPython there is a pyb.bootloader() function which is used to enter into DFU mode.

The C code which implements that can be found in their source repository.

I've used the STM32F4 version extensively (the #else block), and the F7 variant a few times (although its been a while).

I'll put the body of the function here since the above links could become stale if that file changes:

// Activate the bootloader without BOOT* pins.
STATIC NORETURN mp_obj_t machine_bootloader(void) {
    pyb_usb_dev_deinit();
    storage_flush();

    HAL_RCC_DeInit();
    HAL_DeInit();

#if defined(MCU_SERIES_F7)
    // arm-none-eabi-gcc 4.9.0 does not correctly inline this
    // MSP function, so we write it out explicitly here.
    //__set_MSP(*((uint32_t*) 0x1FF00000));
    __ASM volatile ("movw r3, #0x0000\nmovt r3, #0x1FF0\nldr r3, [r3, #0]\nMSR msp, r3\n" : : : "r3", "sp");

    ((void (*)(void)) *((uint32_t*) 0x1FF00004))();
#else
    __HAL_REMAPMEMORY_SYSTEMFLASH();

    // arm-none-eabi-gcc 4.9.0 does not correctly inline this
    // MSP function, so we write it out explicitly here.
    //__set_MSP(*((uint32_t*) 0x00000000));
    __ASM volatile ("movs r3, #0\nldr r3, [r3, #0]\nMSR msp, r3\n" : : : "r3", "sp");

    ((void (*)(void)) *((uint32_t*) 0x00000004))();
#endif

    while (1);
}

The pyb_usb_dev_deinit() function shuts down USB, and storage_flush writes out any cached filesystem data. The HAL functions come from the STM32Cube HAL files.

If you use a newer version of dfu-util (IIRC 0.8 or newer) then you can specify the -s :leave command line option to have your newly flashed program executed at the end of flashing. Combining with the above I go through flash/test cycles without having to touch the board, and only use BOOT0/RESET when the firmware hard-crashes.

There is also a python DFU flasher called pydfu.py: https://github.com/micropython/micropython/blob/master/tools/pydfu.py which is a little faster than dfu-util.

答案 2 :(得分:3)

要跳转到新图像很难。作为自我测试的一部分,我已经成功地完成了它。

  1. 链接第二个图像时,必须将第二个图像(本例中的引导加载程序)驻留在flash中的地址传递给链接器。您可以使用与位置无关的代码,但这有其他问题。
  2. 显然,您必须从与链接器相同的地址开始编程第二个图像。
  3. 设置跳转功能:void (* const jumpFunction)(void) = (void (*)(void))(APPLICATION_ADDRESS + 4ul + 1ul);偏移量为4是越过堆栈指针,偏移量为1表示Thumbmode。
  4. 指定新的堆栈起始指针:__set_MSP((uint32_t)*APPLICATION_ADDRESS),第二个映像的前四个字节将包含新的堆栈指​​针。
  5. 通过调用函数跳转:jumpFunction();
  6. 在第二个程序中,默认初始化将尝试设置向量表偏移量(VTOR)SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;。您必须将其更改为SCB->VTOR = APPLICATION_ADDRESS | VECT_TAB_OFFSET;
  7. 我在FLASH_BASE有一个POST程序,它使用核心耦合的SRAM作为其堆栈,然后在主SRAM上运行内存检查,检查主程序的真实性,然后跳转到主程序。 / p>

    我仍然可以调试主程序,好像没有任何改变。

    NB!我最近才这样做。我需要验证一些事项。一个问题是软件重置会发生什么。如果从第二个程序调用它,我认为它将进入第二个程序的重置例程,而不是第一个程序。

答案 3 :(得分:2)

您可以模拟引导加载程序条件。将BOOT引脚与地之间的电容和并联电阻连接起来。将另一个自由引脚连接到BOOT引脚。电容可以通过外部引脚充电,并通过电阻放电。我不记得你可以计算/试验它们的确切值(重要的是RC电路的时间常数)。

通过将外部引脚设置为1来为此电容器充电,通过NVIC_SystemReset执行软件复位。重置后,它将运行bootloader。连接到电容器的电阻器将执行放电。固件更新后,您可以重置设备,它将运行到您的应用程序。

我们在某些应用程序中使用它并且运行良好。这个解决方案的缺点是你需要外部电路,但它很容易实现,并且它适用于所有STM32设备。

答案 4 :(得分:0)

我在此问题上挣扎了一段时间,试图从 STM32L4A6xx 上的 FreeRTOS 应用程序跳转到 USB OTG DFU。经过大量的反复试验,我能够使它工作,所以我想我会在这里发布它,因为我在其他任何地方都找不到明确的说明。

注意此代码适用于 STM32L4,遵循相同的模式应该适用于其他代码。

此外,当您使用 STM32CubeProgrammer 刷写映像时,请务必选中“运行应用程序”复选框,否则它往往会停留在引导加载程序模式。

void JumpToBootloader(void)
{
     HAL_SuspendTick();

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

     HAL_FLASH_Unlock();

     HAL_FLASH_OB_Unlock();

     // RM0351 Rev 7 Page 93/1903
     // AN2606 Rev 44 Page 23/372
     CLEAR_BIT(FLASH->OPTR, FLASH_OPTR_nBOOT0);
     SET_BIT(FLASH->OPTR, FLASH_OPTR_nBOOT1);
     CLEAR_BIT(FLASH->OPTR, FLASH_OPTR_nSWBOOT0);

     SET_BIT(FLASH->CR, FLASH_CR_OPTSTRT);

     while(READ_BIT(FLASH->SR, FLASH_SR_BSY));

     HAL_FLASH_OB_Launch();
}