FreeRTOS学习 --- 任务切换

任务切换的本质:就是CPU寄存器的切换。

        假设当由任务A切换到任务B时,主要分为两步:

        第一步:需暂停任务A的执行,并将此时任务A的寄存器保存到任务堆栈,这个过程叫做保存现场

        第二步:将任务B的各个寄存器值(被存于任务堆栈中)恢复到CPU寄存器中,这个过程叫做恢复现场

        对任务A保存现场,对任务B恢复现场,这个整体的过程称之为:上下文切换

FreeRTOS学习 --- 任务切换_第1张图片

         注意:任务切换过程PendSV中断服务函数里边完成

PendSV中断是如何触发的?

        1、滴答定时器中断触发PendSV中断

        2、执行FreeRTOS提供的相关API函数:portYIELD()触发PendSV中断

        本质:通过向中断控制和状态寄存器 ICSR bit28 写入 1 挂起 PendSV 来启动 PendSV 中断

FreeRTOS学习 --- 任务切换_第2张图片

上表摘取于《Cortex M3权威指南(中文)》131页。 

 查找最高优先级任务

        vTaskSwitchContext( )  /* 查找最高优先级任务 */

        taskSELECT_HIGHEST_PRIORITY_TASK( )  /* 通过这个函数完成 */

 #define taskSELECT_HIGHEST_PRIORITY_TASK()                                               \
    {                                                                                     \
        UBaseType_t uxTopPriority;                                                        \
        portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority );         			  \
        configASSERT( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ uxTopPriority ] ) ) > 0 ); 	\
        listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) );   	\
    }

获取最高优先级任务的任务控制块

#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )                             \
    {                                                       
        List_t * const pxConstList = ( pxList );                           		 \
        ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;             \
        if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) 			 \
        {                                                                                      							 \
            ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;                       			 \
        }                                                                                      							 \		
        ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;                                         			 	 \ 
    }

        通过该函数获取当前最高优先级任务的任务控制块,同时同等优先级的任务会在一个时间片后进行切换,这就是时间片调度。

任务切换具体步骤:

1.mrs r0, psp

        把psp存到r0,当前的psp是任务A的栈指针,这时的psp指向的是任务栈A中的R0

2. ldr r3, =pxCurrentTCB

    ldr r2, [ r3 ]

        获取当前运行任务的栈顶地址即R2保存的栈顶地址,注意R3等于pxCurrentTCB的地址

3.stmdb r0!, {r4-r11, r14}

        压栈,从上往下压,将r0的值,当压栈的起始地址,开始压栈(保存现场)

4.str r0, [ r2 ]

        将r0的值(前面的底部地址),写到r2地址所指向的内存中(即栈顶地址指向的内存, pxTopOfStack中)

5.stmdb sp!, {r0, r3}

        把R0和R3的值压入MSP 

6.bl vTaskSwitchContext

        通过该函数,获取下一个执行任务的任务控制块,赋值给pxCurrentTCB

7.ldmia sp!, {r0, r3}

        从sp(MSP)恢复r3,即把r3恢复成&pxCurrentTCB。后续就可以利用r3得到新的任务控制块了。

8.ldmia r0!, {r4-r11, r14}

        出栈,以寻址地址开始,从下往上进行出栈,将保存在这些地址的值恢复到寄存器里边去

9.msr psp, r0

   bx r14

        ①将r0更新给psp线程堆栈

        ②返回线程模式,执行新任务

你可能感兴趣的:(学习)