一 跳转方法
1、检查栈顶地址是否合法
if (((*(uint32_t*)(NRF52840_APP_BASE)) & 0xffff0000 ) == 0x20040000 )
{
nrf_bootloader_app_start();
}
在编译生成的APP.bin文件中,前4个字节存放的是__initial_sp,紧接着第二个地址存放的是Reset_Handler;这两个正是所谓的栈顶地址 reset 入口。
代码 | 含义 | 备注 |
---|---|---|
__initial_sp | 栈顶地址 | 程序占用的最大RAM地址 |
Reset_Handler | reset入口 | 程序上电后,第一个运行的函数地址 |
2、跳转前需要关闭无关中断。 |
// 具体实现细节可能不同
NVIC->ICER[0]=0xFFFFFFFF; // 关中断
NVIC->ICPR[0]=0xFFFFFFFF; // 清除中断标志位
3、设置栈顶地址
方法:
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(uint32_t addr)
{
MSR MSP, r0 //set Main Stack value
BX r14
}
另外一种写法
/**
\brief Set Main Stack Pointer
\details Assigns the given value to the Main Stack Pointer (MSP).
\param [in] topOfMainStack Main Stack Pointer value to set
*/
__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack)
{
__ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : );
}
设置栈顶地址函数的参考链接
4、跳转到指定app
void jump_to_addr(uint32_t new_msp, uint32_t addr)
{
__set_MSP(new_msp);
((void (*)(void))addr)();
}
void(*)(void)是函数指针,返回void,参数void.
把addr 强制转化为函数指针
最后()起到调用函数作用
引申
栈的分类
栈是内存中用于暂时存储数据,栈可以分为满增栈、满减栈、空增栈、空减栈。“满”的意思是当前PC指针指向地址空间已经有了数据。存储数据时需要先移动指针,再存储数据。如下图
“空”则表示当前PC指针指向的地址空间没有存储数据,存储数据时需要先存储数据,再移动指针。如下图:
“增”,“减”则表示栈的增长方向,存储数据是栈是增长就是增栈,减小就是减栈,与前面的满空结合,就出现了4种结果
1、满减 2、满增 3、空减 4、空增
ARM 属于满减栈
判断当前CPU是否在中断
IPSR(中断程序状态寄存器),IPSR包含了当前正在执行的中断服务程序编号,用于识别当前中断
const uint32_t current_isr_num = (__get_IPSR() & IPSR_ISR_Msk);
ASSERT(current_isr_num == 0); // If this is triggered, the CPU is currently in an interrupt.