freeertos中关于PendSV中断服务函数的解析

断了今生、忘了曾经 提交于 2019-12-02 22:01:16
__asm void xPortPendSVHandler( void )
{
    extern uxCriticalNesting;
    extern pxCurrentTCB;
    extern vTaskSwitchContext;

    PRESERVE8        //栈的8字节对齐

    mrs r0, psp          //读取当前psp进程指针,存入r0
    isb
    /* 获取当前任务控制块 */
    ldr    r3, =pxCurrentTCB  //把当前任务控制块的指针给r3
    ldr    r2, [r3]        //把r3地址中的值给r2,r2中就存储当前的任务控制块

    /* 是否使用了FPU,使用的话要手动保存s16~s31 */
    tst r14, #0x10
    it eq
    vstmdbeq r0!, {s16-s31}

    /* Save the core registers. */
    stmdb r0!, {r4-r11, r14}    //含义::依次压栈r0 = r0 - 4,先压r14,r0 = r14(即将r14中的内容放入r0所指的内存地址)
                                             // r0 = r0 - 4,再压r11,r0 = r11。
                                             // r0 = r0 - 4,再压r10,r0 = r10......r0 = r0 - 4,最后压r4,r0 = r4。
                                             // 则r0中就保存最新的栈顶指针值                

    /* 保存最新的栈顶指针到当前任务控制块的第一字段*/
    str r0, [r2]          //把r0的值存入r2的地址,相当于*r2 = r0,[r2]中已经保存了最新的任务的控制块,经过上面的两个ldr指令,r2中已经保存最新的任务控制块的地址

    stmdb sp!, {r3}      //将寄存器R3的值临时压栈,寄存器r3中仍然保存着当前任务的任务控制块,
                         //而接下来要调用函数vTaskSwitchContext,防止r3的值被改写,故临时压栈
    
    mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
    msr basepri, r0         //关中断,进入临界区
    
    dsb
    isb
    bl vTaskSwitchContext  //调用函数vTaskSwitchContext,此函数用来获取下一个要运行的任务,并将pxCurrentTCB更新为要运行的这个任务
    
    mov r0, #0
    msr basepri, r0      //开中断,退出临界区
    
    ldmia sp!, {r3}   //刚刚保存的寄存器R3的值出栈,恢复寄存器R3的值。注意,经过调用函数vTaskSwitchContext,此时
                      //pxCurrentTCB的值已经改变了,所以读取R3所保存的地址处的数据就会发现其值改变了,成
                      //为了下一个要运行的任务的任务控制块。


    ldr r1, [r3]
    ldr r0, [r1]     //获取新的运行任务的栈顶,并存到r0中去

    /* 弹出内核寄存器 */
    ldmia r0!, {r4-r11, r14}  //含义::依次出栈    r0 = r0 + 4,先弹出r14,r0 = r14(即将r14中的内容放入r0所指的内存地址)
                                              // r0 = r0 + 4,再弹r11,r0 = r11。
                                              // r0 = r0 + 4,再弹r10,r0 = r10......r0 = r0 - 4,最后出r4,r0 = r4。

    /*是够使用了FPU,使用了徐要手动保存s16~s31*/ 
    tst r14, #0x10
    it eq
    vldmiaeq r0!, {s16-s31}

    msr psp, r0 //更新进程栈指针PSP的值
    isb
    #ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata *///这块暂时不用管
        #if WORKAROUND_PMU_CM001 == 1
            push { r14 }
            pop { pc }
            nop
        #endif
    #endif

    bx r14    //执行此行代码以后硬件自动恢复寄存器R0~R3、R12、LR、PC和xPSR的值,确定异常返回以后应该进入处理器模式还是进程模式,使用主栈指针(MSP)还是进程栈指针(PSP)。          很明显这里会进入进程模式,并且使用进程栈指针(PSP), 寄存器PC值会被恢复为即将运行的任务的任务函数,新的任务开始运行!至此,任务切换成功。
}

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!