C语言 通过应用程序跳转到 STM32 中的引导加载程序,即在引导模式下从用户闪存使用引导 0 和引导 1 引脚

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26891432/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 11:33:04  来源:igfitidea点击:

Jump to Bootloader in STM32 through application i.e using Boot 0 and Boot 1 Pins in Boot mode from User flash

cusbstm32dfu

提问by Jasdeep Singh Arora

I have a requirement for firmware upgrade. I am planning to use USB DFU class. But command for firmware upgrade will come from PC application in my case . so i need to switch to bootloader which is there in System Memory. As initially i am running application so it is getting booted from User flash i.e i have Boot0 and Boot 1 pins configured for User flash. As DFU bootloader is there in System flash ,now for that Boot0 and Boot1 pins settings need to be changed . is there a way like Boot 0 and Boot 1 settings remain same as User Flash memory and in the application we jump to System Memory?

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

回答by JF002

Boot0/1 pins are sampled only when the processor starts, in order to check if it should load the user code from memory or if it should load the bootloader. The state of these pins has no effect of the bootloader afterwards.

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

I've been faced to a similar request, and found 2 ways to load the bootloader on-demand.

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

First, you can "JUMP" from user-code to the bootloader. For example, you could jump to the bootloader when a button is pressed.

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

But... this is far more complicated than a simple JUMP instruction : some registers and devices must be reconfigured correctly to work with the bootloader, you have to ensure that no IRQ will be triggered during the JUMP,... In fact, you have to reconfigure the processor as if it was just started after reset. You can find some information about this technic : on this video from ST.

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

I managed to do this kind of things on STM32F1xx project. However, on a more complex project based on STM32F4, this would become really difficult... I would have to stop all devices (timers, communication interface, ADC, DAC,...), ensure that no IRQ would be triggered, reconfigure all the clocks,...

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

Instead, I decided to implement this second solution: When I want to jump to the bootloader, I write a byte in one of the backup register and then issue a soft-reset. Then, when the processor will restart, at the very beginning of the program, it will read this register. This register contains the value indicating that it should reboot in bootloader mode. Then, the jump to the bootloader is much easier, as presented in the youtube video.

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

回答by j123b567

You can simulate the bootloader condition. Connect capacitor and paralel resistor from BOOT pin to ground. Connect another free pin to BOOT pin. Capacitor can be charged by external pin and is discharged by resistor. I don't remember exact values you can calculate/experiment them (important is time constant of RC circuit).

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

Charge this capatitor by setting external pin to 1, perform software reset by NVIC_SystemReset. After reset, it will run bootloader. Resistor connected to Capacitor will perform discharging. After firmware update, you can reset the device and it will run to your application.

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

We are using this in some applications and it works well. Drawback of this solution is that you need external circuitry, but it is very easy to implement and it is universal for all STM32 devices.

我们在一些应用程序中使用它,它运行良好。这种解决方案的缺点是需要外部电路,但实现起来非常容易,并且对所有STM32设备都是通用的。

回答by Dave Hylands

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

在 MicroPython 中有一个 pyb.bootloader() 函数,用于进入 DFU 模式。

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

可以在其源存储库中找到实现的 C 代码。

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

我用的STM32F4广泛的版本(#else),和F7变种几次(虽然它已经有一段时间)。

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.

pyb_usb_dev_deinit() 函数关闭 USB,storage_flush 写出任何缓存的文件系统数据。HAL 函数来自 STM32Cube HAL 文件。

If you use a newer version of dfu-util (IIRC 0.8 or newer) then you can specify the -s :leavecommand 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.

如果您使用较新版本的 dfu-util(IIRC 0.8 或更新版本),那么您可以指定-s :leave命令行选项让您的新烧录程序在烧录结束时执行。结合上述内容,我无需接触电路板即可完成闪存/测试循环,并且仅在固件硬崩溃时使用 BOOT0/RESET。

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

还有一个叫做pydfu.py的python DFU flasher:https: //github.com/micropython/micropython/blob/master/tools/pydfu.py,它比dfu-util快一点。

回答by Flip

To jump to a new image is not thatdifficult. I have done it successfully as part of a power on self test.

要跳转到一个新的形象是不是困难。作为开机自检的一部分,我已经成功地完成了它。

  1. You have to pass the address of where your second image (the bootloader in this case) resides in flash to the linker when you link the second image. You could possibly use position independent code instead, but that has other issues.
  2. You have to, obviously, flash program the second image starting from the same address as you gave the linker.
  3. Set the jump function: void (* const jumpFunction)(void) = (void (*)(void))(APPLICATION_ADDRESS + 4ul + 1ul);The offset by four is to get past the stack pointer, the offset by one is for Thumbmode.
  4. Specify the new stack starting pointer: __set_MSP((uint32_t)*APPLICATION_ADDRESS), the first four bytes from the second image will contain the new stack pointer.
  5. Jump by calling the function: jumpFunction();
  6. In the second program, the default initialisation will try and set the vector table offset (VTOR) SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;. You have to change this to SCB->VTOR = APPLICATION_ADDRESS | VECT_TAB_OFFSET;
  1. 链接第二个映像时,您必须将第二个映像(在本例中为引导加载程序)驻留在闪存中的地址传递给链接器。您可以改用与位置无关的代码,但这还有其他问题。
  2. 显然,您必须从与您提供链接器的地址相同的地址开始对第二个图像进行闪存编程。
  3. 设置跳转函数:void (* const jumpFunction)(void) = (void (*)(void))(APPLICATION_ADDRESS + 4ul + 1ul);偏移四为越过栈指针,偏移一为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;

I have a POST program at FLASH_BASEthat uses the core coupled SRAM for the its stack and then runs memory checks on the main SRAM, checks the authenticity of the main program and then jumps to the main program.

我有一个 POST 程序,FLASH_BASE它使用核心耦合 SRAM 作为其堆栈,然后在主 SRAM 上运行内存检查,检查主程序的真实性,然后跳转到主程序。

I can still debug the main program as if nothing changed.

我仍然可以调试主程序,就好像什么都没有改变一样。

NB! I have only recently done this myself. There are a few things I need to verify. One concern is what would happen with a software reset. If called from the second program it will go, I think, to the reset routine of the second program, not the first.

注意!我最近才自己做这件事。有几件事我需要验证。一个问题是软件复位会发生什么。如果从第二个程序调用,我认为它将转到第二个程序的重置例程,而不是第一个程序。