固定タスク間の切り替え

3555 ワード

本格的にオペレーティングシステムの作成を開始し、2つの固定タスク間の切り替えを実現するだけですが、通常のCプログラムのワークフローを変更し、オペレーティングシステムがないからオペレーティングシステムがあるまでの飛躍を完了しました.

プログラム切替の原理


関数1実行保存関数1レジスタグループ回復関数2レジスタグループ関数2実行保存関数2レジスタグループ回復関数1レジスタグループ関数1実行
上記のプロセスは人物切替プロセスであり,コンテキスト切替(context switch)である.タスク切替とは、オペレーティングシステムがタスクレジスタをバックアップし、復元するプロセスです.異なる関数は異なるスタックをバックアップする必要があり、そうでなければ関数データは互いに破壊され、関数切替を行う際にもスタックの切替を行い、ここではMSPのみを使用するが、MSPを異なるスタック空間に向けて複数のスタックに対する処理を実現させる.

プログラム切替運転フロー


1.定義TCBブロックTCBはtask control blockの略称であり、タスク制御ブロックを意味し、タスクに関する情報はTCBに保存される.ここでTCBはタスクのタスクスタックレジスタ群を保存している.TCBにはStackregが保存されています
2.STACKREG構造の定義
typedef struct stackreg
{
    U32 stackR4;
    U32 stackR5;
    U32 stackR6;
    U32 stackR7;
    U32 stackR8;
    U32 stackR9;
    U32 stackR10;
    U32 stackR11;
    U32 stackR12;
    U32 stackSP;
    U32 stackLR;
    U32 stackXPSR;
}STACKREG;

R 4-R 12、SP、LR、XPSRを保存します.
3.初期化タスク関数
L_TCB* LOS_TaskInit(VFUNC vfFuncPointer, U32* puiTaskStack)
{
    L_TCB* pstrTcb;              
    STACKREG* pstrStackReg;  

    pstrTcb=(L_TCB*)((U32)puiTaskStack - sizeof(L_TCB));   

    /*  */
    pstrStackReg = &pstrTcb->strStackReg;

    pstrStackReg->stackR4 = 0;                     /* R4 */
    pstrStackReg->stackR5 = 0;                     /* R5 */
    pstrStackReg->stackR6 = 0;                     /* R6 */
    pstrStackReg->stackR7 = 0;                     /* R7 */
    pstrStackReg->stackR8 = 0;                     /* R8 */
    pstrStackReg->stackR9 = 0;                     /* R9 */
    pstrStackReg->stackR10 = 0;                    /* R10 */
    pstrStackReg->stackR11 = 0;                    /* R11 */
    pstrStackReg->stackR12 = 0;                    /* R12 */
    pstrStackReg->stackSP = (U32)pstrTcb;          
    pstrStackReg->stackLR = (U32)vfFuncPointer;   
    pstrStackReg->stackXPSR = MODE_USR;            /* XPSR  */

    return pstrTcb;
}

関数全体の目的はpuitaskstackがアドレス内を指す値(TCBブロック)を変更し、pstrTcbがpuiTaskStackを指し、pstrStackRegがpstrTcb内のレジスタ制フィールドを指し、pstrStackReg内のすべてのレジスタを初期化することである.
4.タスク開始関数テスト関数は2つのタスクを初期化しましたが、2つのタスクは互いに切り替えられません.この関数を使用して次の実行関数のTCBを取得してジャンプし、タスクスケジューリングを開始します.
void LOS_TaskStart(void)
{
    STACKREG* pstrNextTaskStackRegAddr;

    /*   */
    pstrNextTaskStackRegAddr = &gpstrTask1Tcb->strStackReg;

    /*  flag */
    curTask = 1;

    /*  */
    LOS_SwitchToTask(pstrNextTaskStackRegAddr);
}

5.SwitchToTask
LOS_SwitchToTask       ; 
    LDMIA  R0!, {R4 - R12}  ;
    LDMIA  R0, {R13}        ;
    ADD    R0, #8           ;
    LDMIA  R0, {R1}         ;
    MSR    XPSR, R1         ;
    SUB    R0, #4           ;
    LDMIA  R0, {PC}         ;

    ALIGN

    END

このプログラムを実行すると、レジスタが表すプログラムセグメントにジャンプし、前の関数が入力したtask 1のTCBのレジスタセグメントにジャンプします.
6.Task1
void TEST_TestTask1(void)
{
    while(1)
    {
                printf("task1
"); Delay_ms(1000); LOS_TaskSwitch(); } }

ジョブ1で印刷関数をループして実行し、印刷後にジャンプします.
7.LOS_TaskSwitch
LOS_ContextSwitch
    STMIA  R0!, {R4 - R12} 
    STMIA  R0!, {R13}      
    STMIA  R0!, {R14}       
    MRS    R2, XPSR        
    STMIA  R0, {R2}         

    
    LDMIA  R1!, {R4 - R12}  
    LDMIA  R1, {R13}       
    ADD    R1, #8           
    LDMIA  R1, {R0}         
    MSR    XPSR, R0        
    SUB    R1, #4           
    LDMIA  R1, {PC}        

2つのタスクは2つの異なるスタックで保存されます.