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

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

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

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

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

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

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

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

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

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

我已经广泛使用了STM32F4版本( #else块 )和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写出任何缓存的文件系统数据。 HALfunction来自STM32Cube HAL文件。

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

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

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

  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 = FLASH_BASE | VECT_TAB_OFFSET; 。 您必须将其更改为SCB->VTOR = APPLICATION_ADDRESS | VECT_TAB_OFFSET; SCB->VTOR = APPLICATION_ADDRESS | VECT_TAB_OFFSET;

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

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

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

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

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

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