8

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

4

5 回答 5

26

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

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

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

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

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

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

于 2014-11-16T15:26:29.170 回答
6

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

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

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

于 2015-03-30T17:19:03.603 回答
5

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

可以在他们的源代码库中找到实现的 C 代码。

我已经广泛使用了#elseSTM32F4 版本()和 F7 版本几次(尽管已经有一段时间了)。

我将把函数的主体放在这里,因为如果该文件发生更改,上述链接可能会变得陈旧:

// 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);
}

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

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

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

于 2016-02-03T02:36:47.767 回答
4

跳转到新图像并不难。作为开机自检的一部分,我已经成功地完成了它。

  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;

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

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

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

于 2015-02-24T06:49:33.603 回答
0

我一直在努力解决这个问题,试图从一个 FreeRTOS 应用程序跳转STM32L4A6xx到 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();
}
于 2020-12-22T03:23:48.030 回答