Abl*_*ose 3 embedded assembly arm stm32 bootloader
当从引导加载程序跳转到应用程序时,我们通常将堆栈指针更新为应用程序堆栈指针,然后将程序计数器更新为应用程序的 Reset_Handler。
void jump_to_application(void)
{
/* Function pointer to the address of the user application. */
fnc_ptr_for_app jump_to_app;
jump_to_app = (fnc_ptr_for_app)(*(volatile uint32_t*) (FLASH_APP_START_ADDRESS+4u));
__CRC_CLK_DISABLE();
HAL_DeInit();
/* Change the main stack pointer. */
__set_MSP(*(volatile uint32_t*)FLASH_APP_START_ADDRESS);
jump_to_app();
}
Run Code Online (Sandbox Code Playgroud)
Reset_Handler的第一行代码如下,将栈指针初始化为自己的栈指针值
Reset_Handler:
ldr sp, =_estack /* Atollic update: set stack pointer */
/* Copy the data segment initializers from flash to SRAM */
movs r1, #0
b LoopCopyDataInit
Run Code Online (Sandbox Code Playgroud)
但仍然建议使用应用程序的堆栈指针更新堆栈指针。
为什么需要,不更新就跳转有什么副作用
在 ARM Cortex-M 中,初始堆栈指针存储在向量表的前 4 个字节中,初始程序计数器存储在后 4 个字节中。复位时,硬件从这 8 个字节加载 SP 和 PC。例如,这允许使用 C 而不是汇编程序编写 Cortex-M 启动代码。
您的引导加载程序通过将硬件取消初始化为其复位状态并从应用程序的向量表加载 SP 和 PC 来模拟这种复位行为。这允许应用程序像从复位开始一样启动,而无需依赖引导加载程序的任何初始化或设置。
引导加载程序与应用程序分开编译和链接,并且必须能够加载具有适当起始地址的任何应用程序代码。因此,引导加载程序无法确定或强制加载的任何应用程序代码将设置堆栈指针,因为它可能合理地假设硬件已经设置了它。同样在这种情况下,_estack != *(volatile uint32_t*)FLASH_APP_START_ADDRESS)在任何情况下都是完全可能的。
对引导加载程序的一个观察是,它不会将向量表设置为应用程序的向量表,这具有潜在危险 - 如果启动代码启用中断而未先设置向量表,则可能会导致引导加载程序中断处理程序错误地调用。拥有以下内容会更安全:
// Switch vector table
SCB->VTOR = APPLICATION_START_ADDR ;
Run Code Online (Sandbox Code Playgroud)
之前jump_to_app()。或者,如果您选择使用 HAL:
NVIC_SetVectorTable( NVIC_VectTab_FLASH, APPLICATION_START_ADDR ) ;
Run Code Online (Sandbox Code Playgroud)