通过应用程序跳转到 STM32 中的引导加载程序,即在引导模式下从用户闪存使用引导 0 和引导 1 引脚
Posted
技术标签:
【中文标题】通过应用程序跳转到 STM32 中的引导加载程序,即在引导模式下从用户闪存使用引导 0 和引导 1 引脚【英文标题】:Jump to Bootloader in STM32 through application i.e using Boot 0 and Boot 1 Pins in Boot mode from User flash 【发布时间】:2015-01-09 13:38:31 【问题描述】:我需要固件升级。我打算使用 USB DFU 类。但就我而言,固件升级命令将来自 PC 应用程序。所以我需要切换到系统内存中的引导加载程序。最初我正在运行应用程序,因此它是从用户闪存启动的,即我为用户闪存配置了 Boot0 和 Boot 1 引脚。由于 DFU 引导加载程序存在于系统闪存中,现在需要更改 Boot0 和 Boot1 引脚设置。有没有办法像 Boot 0 和 Boot 1 设置保持与用户闪存相同,在应用程序中我们跳转到系统内存?
【问题讨论】:
【参考方案1】:Boot0/1 引脚仅在处理器启动时进行采样,以检查它是否应该从内存中加载用户代码或是否应该加载引导加载程序。 之后这些引脚的状态对引导加载程序没有影响。
我遇到过类似的请求,并找到了两种按需加载引导加载程序的方法。
首先,您可以从用户代码“跳转”到引导加载程序。例如,您可以在按下按钮时跳转到引导加载程序。
但是...这比简单的 JUMP 指令复杂得多:必须正确重新配置某些寄存器和设备才能与引导加载程序一起使用,您必须确保在 JUMP 期间不会触发任何 IRQ,...事实上,您必须重新配置处理器,就好像它在重置后刚刚启动一样。 您可以找到有关此技术的一些信息:on this video from ST。
我设法在 STM32F1xx 项目上做这种事情。 但是,在基于 STM32F4 的更复杂的项目中,这将变得非常困难……我必须停止所有设备(定时器、通信接口、ADC、DAC……),确保不会触发任何 IRQ,重新配置所有的时钟,...
相反,我决定实施第二种解决方案:当我想跳转到引导加载程序时,我在其中一个备份寄存器中写入一个字节,然后发出软复位。然后,当处理器重新启动时,在程序的最开始,它会读取这个寄存器。该寄存器包含指示它应该在引导加载程序模式下重新启动的值。然后,跳转到引导加载程序要容易得多,如the youtube video 所示。
【讨论】:
需要记住的一点:根据Reference Manual:“从待机模式退出时,BOOT 引脚也会重新采样。因此,它们必须在待机模式下保持所需的引导模式配置。” 请记住,“退出待机”实际上意味着设备重置。 @JF002 两个视频链接都一样。【参考方案2】:您可以模拟引导加载程序条件。将电容和并联电阻从 BOOT 引脚连接到地。将另一个空闲引脚连接到 BOOT 引脚。电容可以通过外部引脚充电,通过电阻放电。我不记得你可以计算/实验它们的确切值(重要的是 RC 电路的时间常数)。
通过将外部引脚设置为 1 为该电容器充电,通过NVIC_SystemReset
执行软件复位。复位后,它将运行引导加载程序。连接到电容器的电阻器将执行放电。固件更新后,您可以重置设备,它将运行到您的应用程序。
我们在一些应用程序中使用它并且效果很好。该方案的缺点是需要外接电路,但实现起来非常简单,对所有 STM32 设备都通用。
【讨论】:
【参考方案3】:在 MicroPython 中有一个 pyb.bootloader() 函数用于进入 DFU 模式。
可以在in their source repository找到实现的C代码。
我已经广泛使用了 STM32F4 版本 (the #else
block) 和 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:https://github.com/micropython/micropython/blob/master/tools/pydfu.py 的 python DFU flasher,它比 dfu-util 快一点。
【讨论】:
【参考方案4】:跳转到新图像并不那么困难。作为开机自检的一部分,我已经成功地完成了它。
-
在链接第二个映像时,您必须将第二个映像(在本例中为引导加载程序)驻留在闪存中的地址传递给链接器。您可以改用与位置无关的代码,但这还有其他问题。
显然,您必须从与链接器相同的地址开始对第二个映像进行闪存编程。
设置跳转函数:
void (* const jumpFunction)(void) = (void (*)(void))(APPLICATION_ADDRESS + 4ul + 1ul);
偏移量四是越过堆栈指针,偏移量一是Thumbmode。
指定新的堆栈起始指针:__set_MSP((uint32_t)*APPLICATION_ADDRESS)
,第二个图像的前四个字节将包含新的堆栈指针。
通过调用函数跳转:jumpFunction();
在第二个程序中,默认初始化将尝试设置向量表偏移量 (VTOR) SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
。您必须将其更改为 SCB->VTOR = APPLICATION_ADDRESS | VECT_TAB_OFFSET;
我在FLASH_BASE
有一个 POST 程序,它使用核心耦合 SRAM 作为其堆栈,然后在主 SRAM 上运行内存检查,检查主程序的真实性,然后跳转到主程序。
我仍然可以调试主程序,就好像什么都没有改变。
注意!我自己最近才这样做。我需要验证几件事。一个问题是软件重置会发生什么。如果从第二个程序调用它,我认为它将进入第二个程序的重置例程,而不是第一个程序。
【讨论】:
【参考方案5】:我在尝试从 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();
【讨论】:
以上是关于通过应用程序跳转到 STM32 中的引导加载程序,即在引导模式下从用户闪存使用引导 0 和引导 1 引脚的主要内容,如果未能解决你的问题,请参考以下文章
STM32L073RZ (rev Z) IAP 跳转到引导加载程序(系统内存)
如何让STM32F429NI中的bootloader跳转到外部Nor Flash