Freertos 任务切换的理解(仅自己遇到的困难)

触发任务切换

1.手动触发(主动申请任务切换,例如从阻塞态变位就绪态具体流程目前不懂)

 2.定时器触发:就是我们设置的每一毫秒进一次定时中断

两种方式都是调用PENDSV中断来保存上文切换下文。

__asm void xPortPendSVHandler( void )
{
    extern uxCriticalNesting;
    extern pxCurrentTCB;            /* 指向当前激活的任务 */
    extern vTaskSwitchContext;      
 
 
    PRESERVE8
 
 
    mrs r0, psp                   /* PSP内容存入R0 */    //将特殊功能寄存器的值存到通用寄存器
    isb                           /* 指令同步隔离,清流水线 */  //
 
 
    ldr    r3, =pxCurrentTCB     /* 当前激活的任务TCB指针存入R2 */
    ldr    r2, [r3]
 
 
    stmdb r0!, {r4-r11}          /* 保存剩余的寄存器,异常处理程序执行前,硬件自动将xPSR、PC、LR、R12、R0-R3入栈 */
    str r0, [r2]             /* 将新的栈顶保存到任务TCB的第一个成员中 */
 
 
    stmdb sp!, {r3, r14}         /* 将R3和R14临时压入堆栈,因为即将调用函数vTaskSwitchContext,调用函数时,返回地址自动保存到R14中,所以一旦调用发生,R14的值会被覆盖,因此需要入栈保护; R3保存的当前激活的任务TCB指针(pxCurrentTCB)地址,函数调用后会用到,因此也要入栈保护*/
    mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY   /* 进入临界区 */
    msr basepri, r0
    dsb                         /* 数据和指令同步隔离 */
    isb
    bl vTaskSwitchContext        /* 调用函数,寻找新的任务运行,通过使变量pxCurrentTCB指向新的任务来实现任务切换 */
    mov r0, #0                   /* 退出临界区*/
    msr basepri, r0
    ldmia sp!, {r3, r14}         /* 恢复R3和R14*/
 
 
    ldr r1, [r3]
    ldr r0, [r1]             /* 当前激活的任务TCB第一项保存了任务堆栈的栈顶,现在栈顶值存入R0*/
    ldmia r0!, {r4-r11}         /* 出栈*/
    msr psp, r0 
    isb
    bx r14                      /* 异常发生时,R14中保存异常返回标志,包括返回后进入线程模式还是处理器模式、使用PSP堆栈指针还是MSP堆栈指针,当调用 bx r14指令后,硬件会知道要从异常返回,然后出栈,这个时候堆栈指针PSP已经指向了新任务堆栈的正确位置,当新任务的运行地址被出栈到PC寄存器后,新的任务也会被执行。*/
    nop
}

mrs r0, psp                   /* PSP内容存入R0 */    //将特殊功能寄存器的值存到通用寄存器

 isb                           /* 指令同步隔离,清流水线 */  //CM3的权威指南里,保证前面的指令已经执行完了

 ldr    r3, =pxCurrentTCB     /* 当前激活的任务TCB指针存入R2 */ //加载pxCurrentTCB(指针的地址)到寄存器。

 stmdb r0!, {r4-r11}          /* 保存剩余的寄存器,异常处理程序执行前,硬件自动将xPSR、PC、LR、R12、R0-R3入栈 */ 这句话很重要。保存当前任务的寄存器到当前堆栈。其中PC.LR等有硬件先一步保存在栈中,PC最后保存。

msr basepri, r0 。basepri寄存器用于中断屏蔽,将优先级小于5的屏蔽

dsb                         /* 数据和指令同步隔离 */、、仅当所有在它前面的存储器访问操作都执行完毕后,才执行在它后面的指令

    ldr r1, [r3]
    ldr r0, [r1]             /* 当前激活的任务TCB第一项保存了任务堆栈的栈顶,现在栈顶值存入R0*/

因为R3保存的的是currentTCB的地址,所以r1得到currentTCB的值是任务TCB第一项的值(指针),新任务的栈顶存入R0.

 ldmia r0!, {r4-r11}         /* 出栈*/ //r4-r11是新任的栈,Ldmia r0!, {r4-r11} 是 ARM 汇编语言指令,用于一次性从内存中加载多个寄存器的值。该指令用于从存储在地址 r0 中的连续内存位置中加载 8 个字(每个字 4 字节),并将它们分别存储到寄存器 r4 ~ r11 中。在将数据从内存加载到寄存器之后,该指令会自动更新寄存器 r0 以指向下一个内存位置。因为r0存储的是(新任务的)栈顶地址
    msr psp, r0 

 bx r14          退出中断

这样新任务堆栈的PC、LR寄存器会弹出堆栈。(这部分是硬件自动的,只有这种机制才能进行多任务吧,不然你都不知道新任务的堆栈PC存在哪里

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值