固定タスク間の切り替え
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構造の定義
R 4-R 12、SP、LR、XPSRを保存します.
3.初期化タスク関数
関数全体の目的はpuitaskstackがアドレス内を指す値(TCBブロック)を変更し、pstrTcbがpuiTaskStackを指し、pstrStackRegがpstrTcb内のレジスタ制フィールドを指し、pstrStackReg内のすべてのレジスタを初期化することである.
4.タスク開始関数テスト関数は2つのタスクを初期化しましたが、2つのタスクは互いに切り替えられません.この関数を使用して次の実行関数のTCBを取得してジャンプし、タスクスケジューリングを開始します.
5.SwitchToTask
このプログラムを実行すると、レジスタが表すプログラムセグメントにジャンプし、前の関数が入力したtask 1のTCBのレジスタセグメントにジャンプします.
6.Task1
ジョブ1で印刷関数をループして実行し、印刷後にジャンプします.
7.LOS_TaskSwitch
2つのタスクは2つの異なるスタックで保存されます.
プログラム切替の原理
関数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つの異なるスタックで保存されます.