zoukankan      html  css  js  c++  java
  • FreeRTOS 任务切换细节

    知识点:

    MSP:主堆栈指针,系统复位后,默认使用MSP指针,MSP指针用于操作内核以及处理异常和中断(异常是中断的一种,中断服务程序默认强制使用MSP指针,这是硬件自动设置的)

        不使用OS,非中断函数和中断函数都使用MSP

    PSP:进程堆栈指针,任务(进程)使用PSP指针,在vPortSVCHandler中断服务函数中,通过修改 R14 的值从MSP指针切换到PSP指针

        使用OS,main函数和中断使用MSP,各个task使用PSP

    R13: 堆栈指针SP,分为MSP和PSP

    在执行异常的时候,SP以MSP为栈指针,在执行任务时SP以PSP为栈指针

    每个任务都有一个任务堆栈

    栈的地址是从高往低生长,堆栈指针最低两位永远是0,意味着堆栈总是4字节对齐

    任务TCB数据结构第一个成员一定是指向任务当前堆栈栈顶的指针变量pxTopOfStack

    R14作用:连接寄存器。调用子函数时存储返回地址;异常(中断)发生时,R14中保存异常返回标志,包括返回后进入线程模式还是处理器模式、指定SP使用PSP堆栈指针还是MSP堆栈指针。当调用 BX r14 指令后,硬件会知道要从异常返回,然后出栈,这个时候堆栈指针PSP已经指向了新任务堆栈的正确位置,当新任务的运行地址被出栈到PC寄存器后,新的任务也会被执行。

    PC(R15):程序计数器,记录下一个要执行指令的地址,改变它的值就能改变程序的执行流

    R0:任务形参

    通用寄存器:用于数据操作

    切换流程:

    1、进入PendSV中断前,硬件自动将xPSR、PC(R15)、LR(R14)、R12、R0~R3压入任务堆栈中(使用PSP堆栈指针)

    2、手动将R4~R11压入堆栈,将PSP栈顶值存入TCB(任务控制块)

    3、关高优先级中断

    4、调用任务切换函数获得新的任务控制块

    5、开中断

    6、通过新的任务控制块更新PSP栈顶值,手动将R4~R11出栈

    7、退出中断时,硬件自动将xPSR、PC、LR、R12、R0~R3任务堆栈中出栈(使用PSP堆栈指针)

    8、根据新的寄存器值和PSP执行新的任务

     

    xPortPendSVHandler:
        mrs r0, psp
        isb
        ldr    r3, =pxCurrentTCB            /* Get the location of the current TCB. */
        ldr    r2, [r3]
    
        stmdb r0!, {r4-r11}                /* Save the remaining registers. */
        str r0, [r2]                    /* Save the new top of stack into the first member of the TCB. */
    
        stmdb sp!, {r3, r14}

      /* 接下来两句是进入临界区,即屏蔽高优先级中断 */
    mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY msr basepri, r0 dsb isb bl vTaskSwitchContext
      /* 接下来两句是退出临界区,即恢复高优先级中断 */
    mov r0, #0 msr basepri, r0 ldmia sp!, {r3, r14} ldr r1, [r3] ldr r0, [r1] /* The first item in pxCurrentTCB is the task top of stack. */ ldmia r0!, {r4-r11} /* Pop the registers. */ msr psp, r0 isb bx r14

    isb:清流水线,保证此操作完成后再执行下一条指令

    ldr  r3, =pxCurrentTCB:把 pxCurrentTCB 的地址加载到 r3,相当于两个指针变量的赋值操作,此后 [r3] 相当于 pxCurrentTCB 的值,给 pxCurrentTCB 赋值相当于给 r3 赋值

    ldr  r2,  [r3]:把r3指向的内容赋值给r2,相当于两个变量的赋值操作

    stmdb  r0!,  {r4-r11}:先将 r0 指向的地址递减,再以新 r0 将 CPU 的 R4-R11 手动入栈,此时 R0 具体指向如下图:

    str r0, [r2]:将R0的值存储到R2指向的内容,因为R2等于pxCurrentTCB,而pxCurrentTCB的第一个成员为pxTopOfStack,所以就是将R0存储到上一个任务的pxTopOfStack任务栈顶指针中。完成了上文的保存

    stmdb  sp!,  {r3,  r14}:将 R3 和 R14 入栈,此刻的 SP 指向 MSP。对这两个寄存器入栈的原因:此刻的 R3 指向了 pxCurrentTCB(R3 保存了 pxCurrentTCB 的地址),在接下来的函数调用可能被修改,而这个值在函数调用后还要用到;R14 值在函数调用后同样要用到,而函数调用会修改 R14 的值(存储调用子函数后的返回地址)

    ldr  r1,  [r3]: 调用函数 vTaskSwitchContext 后,pxCurrentTCB指向的内容就是新的TCB,进而 R3 指向的内容也是新的TCB

    参考链接:

    https://blog.csdn.net/qingzhuyuxian/article/details/80604129

    https://blog.csdn.net/qq_15100379/article/details/86166994

  • 相关阅读:
    多级导航Menu的CSS
    Centos7在线安装PostgreSQL和PostGIS
    PostGis 根据经纬度查询两点之间距离
    在PowerDesigner中表显示中添加Code的显示
    Tomcat部署Geoserver
    PostGIS之路AddGeometryColumn函数添加一个几何类型字段
    怎样把多个excel文件合并成一个
    Error:java: 无效的目标发行版: 11
    PowerDesigner导出Excel
    GeoServer发布高清电子地图
  • 原文地址:https://www.cnblogs.com/god-of-death/p/14855296.html
Copyright © 2011-2022 走看看