• FreeRtos 任务切换深入分析


    一、背景知识:

    1、任务切换包含三个基本流程:保护现场、更新TCB、恢复现场并跳转

    2、freertos的任务切换是在xPortPendSVHandler 中断函数中完成的

    3、中断函数在调用之前,硬件已经保存了r0,r1,r2,r3,r12,r14(LR),r15(pc),恢复现场的时候由硬件自动恢复r0,r1,r2,r3,r12,r14(LR),r15(pc),即r0~r3在中断服务函数中是可以随便使用的,剩下的r4~r11需要在中断函数中手动保存。

    4、ARM中的栈有两个,msp和psp,主程序和中断中用msp,线程中是psp

    5、ARM一般是满减栈,freertos中,有portSTACK_GROWTH宏控制,portSTACK_GROWTH 小于0为向下增长,大于0向上增长

    二、xPortPendSVHandler源代码:

    1. void xPortPendSVHandler( void )
    2. {
    3. /* This is a naked function. */
    4. __asm volatile
    5. (
    6. " mrs r0, psp \n"
    7. " isb \n"
    8. " \n"
    9. " ldr r3, pxCurrentTCBConst \n" /* Get the location of the current TCB. */
    10. " ldr r2, [r3] \n"
    11. " \n"
    12. " tst r14, #0x10 \n" /* Is the task using the FPU context? If so, push high vfp registers. */
    13. " it eq \n"
    14. " vstmdbeq r0!, {s16-s31} \n"
    15. " \n"
    16. " stmdb r0!, {r4-r11, r14} \n" /* Save the core registers. */
    17. " str r0, [r2] \n" /* Save the new top of stack into the first member of the TCB. */
    18. " \n"
    19. " stmdb sp!, {r0, r3} \n"
    20. " mov r0, %0 \n"
    21. " cpsid i \n" /* Errata workaround. */
    22. " msr basepri, r0 \n"
    23. " dsb \n"
    24. " isb \n"
    25. " cpsie i \n" /* Errata workaround. */
    26. " bl vTaskSwitchContext \n"
    27. " mov r0, #0 \n"
    28. " msr basepri, r0 \n"
    29. " ldmia sp!, {r0, r3} \n"
    30. " \n"
    31. " ldr r1, [r3] \n" /* The first item in pxCurrentTCB is the task top of stack. */
    32. " ldr r0, [r1] \n"
    33. " \n"
    34. " ldmia r0!, {r4-r11, r14} \n" /* Pop the core registers. */
    35. " \n"
    36. " tst r14, #0x10 \n" /* Is the task using the FPU context? If so, pop the high vfp registers too. */
    37. " it eq \n"
    38. " vldmiaeq r0!, {s16-s31} \n"
    39. " \n"
    40. " msr psp, r0 \n"
    41. " isb \n"
    42. " \n"
    43. #ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata workaround. */
    44. #if WORKAROUND_PMU_CM001 == 1
    45. " push { r14 } \n"
    46. " pop { pc } \n"
    47. #endif
    48. #endif
    49. " \n"
    50. " bx r14 \n"
    51. " \n"
    52. " .align 4 \n"
    53. "pxCurrentTCBConst: .word pxCurrentTCB \n"
    54. ::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY)
    55. );
    56. }

    三、xPortPendSVHandler分析:

    1、先在xPortPendSVHandler中把pxCurrentTCB打印出来:

    2、 

    将psp给r0 

    3、 

    这两行,将r3保存了pxCurrentTCB,r2指向了上一个task的栈顶位置:pxTopOfStack

    4、

     这是对FPU寄存器的判断,可以先不看

    5、

    将r4-r11,r14保存到任务栈psp中。并将栈顶位置写入到r2中,即 pxTopOfStack这个变量

    6、

    此时的sp为msp,即将r3、r0保存到msp中。

    至此,现场保护已经完成。

    7、


    跳转到vTaskSwitchContext这个C函数中执行,这个函数中就将下一个要执行的任务的TCP更新到pxCurrentTCB中,主要代码如下:

    这里要时刻注意,不要迷糊,更新pxCurrentTCB的同时,r3也会指向这个新的pxCurrentTCB。

    8、

    将r3和r0从msp中恢复出来,注意:此时的r3中的pxCurrentTCB已经是更新之后的了。

    9、

     这两句,将新的pxCurrentTCB中的pxTopOfStack给了r0,即r0中记录了新task的栈顶位置

    10、

    从新task中恢复现场即恢复r4~r11,r14 

    11、

    恢复FPU相关寄存器

    12、

     将栈顶的位置给psp

    至此,恢复现场已经全部完成

    13、

     

    r14即LR中记录了中断返回的地址,跳转执行

  • 相关阅读:
    创建vue3项目并引用elementui
    CSS基础-嵌套规范(拓展),居中方法,CSS 特性
    LeetCode_643_子数组的最大平均数Ⅰ
    3D视觉应用案例:法兰件/引擎盖/控制臂上料,轮毂抓取上架
    软件工程概论
    es6对象的扩展、对象的新增方法
    力扣第35天----第1049题、第494题、第474题
    Java:正则表达式的命名捕获组
    数据结构(单链表)
    UDP 客户端接收数据测试用例
  • 原文地址:https://blog.csdn.net/weixin_40204595/article/details/134477284