当从引导器跳转到应用程序时,我们通常会将堆栈指针更新到应用程序堆栈指针,然后将程序计数器更新为应用程序的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();
}
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
但是仍然建议用应用程序的指针更新堆栈指针。
为什么需要它,如果我们跳过而不更新它会产生什么副作用?
发布于 2019-12-23 16:25:17
在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 ;
在jump_to_app()
之前。或者如果您选择使用HAL:
NVIC_SetVectorTable( NVIC_VectTab_FLASH, APPLICATION_START_ADDR ) ;
https://stackoverflow.com/questions/59458156
复制相似问题