一、上篇回顧
上一篇文章中,我們完成了兩個任務使用PendSV實現(xiàn)了互相切換的功能,下面我們接著其思路往下做。這次我們完成OS基本框架,即實現(xiàn)一個非搶占式(已經(jīng)調(diào)度的進程執(zhí)行完成,然后根據(jù)優(yōu)先級調(diào)度等待的進程)的任務調(diào)度系統(tǒng),至于搶占式的,就留給大家思考了。上次代碼中Task_Switch實現(xiàn)了兩個任務的切換,代碼如下:
voidTask_Switch(){if(g_OS_Tcb_CurP==&TCB_1)g_OS_Tcb_HighRdyP=&TCB_2;elseg_OS_Tcb_HighRdyP=&TCB_1;OSCtxSw();}
我們把要切換任務指針付給跟_OS_Tcb_HighRdyP,然后調(diào)用OSCtxSw觸發(fā)PendSV異常,就實現(xiàn)了任務的切換。如果是多個任務,我們只需找出就緒任務中優(yōu)先級最大的切換之即可。
二、添加任務調(diào)度功能
為了實現(xiàn)這一目標我們至少需要知道任務的狀態(tài)和時間等數(shù)據(jù)。我們定義了一個任務狀態(tài)枚舉類型OS_TASK_STA,方便添加修改狀態(tài)。在OS_TCB結(jié)構(gòu)體中添加了兩個成員TimeDly和State,TimeDly是為了實現(xiàn)OS_TimeDly,至于State與優(yōu)先級一起是作為任務切換的依據(jù)。
typedefenumOS_TASK_STA{TASK_READY,TASK_DELAY,}OS_TASK_STA;typedefstructOS_TCB{OS_STK*StkAddr;OS_U32TimeDly;OS_TASK_STAState;}OS_TCB,*OS_TCBP;
說到任務切換,我們必須面對臨界區(qū)的問題,在一些臨界的代碼兩端不加臨界區(qū)進去和退出代碼,會出現(xiàn)許多意想不到的問題。以下地方需要特別注意,對關鍵的全局變量的寫操作、對任務控制塊的操作等。進入臨界區(qū)和退出臨界區(qū)需要關閉和開啟中斷,我們采用uCOS中的一部分代碼:
PUBLICOS_CPU_SR_SavePUBLICOS_CPU_SR_RestoreOS_CPU_SR_SaveMRSR0,PRIMASKCPSIDIBXLROS_CPU_SR_RestoreMSRPRIMASK,R0BXLR
#defineOS_USE_CRITICALOS_U32cpu_sr;#defineOS_ENTER_CRITICAL(){cpu_sr=OS_CPU_SR_Save();}#defineOS_EXIT_CRITICAL(){OS_CPU_SR_Restore(cpu_sr);}#defineOS_PendSV_Trigger()OSCtxSw()
一個OS至少要有任務表,我們可以用數(shù)組,當然也可以用鏈表。為了簡單,我們使用數(shù)組,使用數(shù)組下表作為優(yōu)先級。當然,必要的地方一定要做數(shù)組越界檢查。
#defineOS_TASK_MAX_NUM32OS_TCBPOS_TCB_TABLE[OS_TASK_MAX_NUM];
為了使OS更完整,我們定義幾個全局變量,OS_TimeTick記錄系統(tǒng)時間,g_Prio_Cur記錄當前運行的任務優(yōu)先級,g_Prio_HighRdy記錄任務調(diào)度后就緒任務中的最高優(yōu)先級。
OS_U32OS_TimeTick;OS_U8g_Prio_Cur;OS_U8g_Prio_HighRdy;
下面三個函數(shù)與PendSV一起實現(xiàn)了任務的調(diào)度功能。
OS_Task_Switch函數(shù)功能:找出已就緒最高優(yōu)先級的任務,并將其TCB指針賦值給g_OS_Tcb_HighRdyP,將其優(yōu)先級賦值g_Prio_HighRdy。注意其中使用了臨界區(qū)。
voidOS_Task_Switch(void){OS_S32i;OS_TCBPtcb_p;OS_USE_CRITICALfor(i=0;iState==TASK_READY)break;}OS_ENTER_CRITICAL();g_OS_Tcb_HighRdyP=tcb_p;g_Prio_HighRdy=i;OS_EXIT_CRITICAL();}
OS_TimeDly至當前任務為延時狀態(tài),并將延時時間賦值給當前TCB的TimeDly成員,并調(diào)用OS_Task_Switch函數(shù),然后觸發(fā)PendSV進行上下文切換。OS_Task_Switch找到就緒狀態(tài)中優(yōu)先級最高的,并將其賦值相關全局變量,作為上下文切換的依據(jù)。
voidOS_TimeDly(OS_U32ticks){OS_USE_CRITICALOS_ENTER_CRITICAL();g_OS_Tcb_CurP->State=TASK_DELAY;g_OS_Tcb_CurP->TimeDly=ticks;OS_EXIT_CRITICAL();OS_Task_Switch();OS_PendSV_Trigger();}
SysTick_Handler實現(xiàn)系統(tǒng)計時,并遍歷任務表,任務若是延時狀態(tài),就令其延時值減一,若減完后為零,就將其置為就緒狀態(tài)。
voidSysTick_Handler(void){OS_TCBPtcb_p;OS_S32i;OS_USE_CRITICALOS_ENTER_CRITICAL();++OS_TimeTick;for(i=0;iState==TASK_DELAY){--tcb_p->TimeDly;if(tcb_p->TimeDly==0)tcb_p->State=TASK_READY;}}OS_EXIT_CRITICAL();}
當所有任務都沒就緒怎么辦?這時就需要空閑任務了,我們把它設為優(yōu)先級最低的任務。WFE指令為休眠指令,當來中斷時,退出休眠,然后看看有沒有已就緒的任務,有則調(diào)度之,否則繼續(xù)休眠,這樣可以減小功耗哦。
voidOS_Task_Idle(void){while(1){asm("WFE");OS_Task_Switch();OS_PendSV_Trigger();}}
當一個任務只運行一次時(例如下面main.c的task1),結(jié)束時就會調(diào)用OS_Task_End函數(shù),此函數(shù)會調(diào)用OS_Task_Delete函數(shù)從任務表中刪除當前的任務,然后調(diào)度任務。
voidOS_Task_Delete(OS_U8prio){if(prio>=OS_TASK_MAX_NUM)return;OS_TCB_TABLE[prio]=0;}voidOS_Task_End(void){printf("TaskofPrio%dEndn",g_Prio_Cur);OS_Task_Delete(g_Prio_Cur);OS_Task_Switch();OS_PendSV_Trigger();}
三、OS實戰(zhàn)
下是完整的main.c代碼:
#include"stdio.h"#include"stm32f4xx.h"#defineOS_EXCEPT_STK_SIZE1024#defineTASK_1_STK_SIZE128#defineTASK_2_STK_SIZE128#defineTASK_3_STK_SIZE128#defineTASK_IDLE_STK_SIZE1024#defineOS_TASK_MAX_NUM32#defineOS_TICKS_PER_SECOND1000#defineOS_USE_CRITICALOS_U32cpu_sr;#defineOS_ENTER_CRITICAL(){cpu_sr=OS_CPU_SR_Save();}#defineOS_EXIT_CRITICAL(){OS_CPU_SR_Restore(cpu_sr);}#defineOS_PendSV_Trigger()OSCtxSw()typedefsignedcharOS_S8;typedefsignedshortOS_S16;typedefsignedintOS_S32;typedefunsignedcharOS_U8;typedefunsignedshortOS_U16;typedefunsignedintOS_U32;typedefunsignedintOS_STK;typedefvoid(*OS_TASK)(void);typedefenumOS_TASK_STA{TASK_READY,TASK_DELAY,}OS_TASK_STA;typedefstructOS_TCB{OS_STK*StkAddr;OS_U32TimeDly;OS_U8State;}OS_TCB,*OS_TCBP;OS_TCBPOS_TCB_TABLE[OS_TASK_MAX_NUM];OS_TCBPg_OS_Tcb_CurP;OS_TCBPg_OS_Tcb_HighRdyP;OS_U32OS_TimeTick;OS_U8g_Prio_Cur;OS_U8g_Prio_HighRdy;staticOS_STKOS_CPU_ExceptStk[OS_EXCEPT_STK_SIZE];OS_STK*g_OS_CPU_ExceptStkBase;staticOS_TCBTCB_1;staticOS_TCBTCB_2;staticOS_TCBTCB_3;staticOS_TCBTCB_IDLE;staticOS_STKTASK_1_STK[TASK_1_STK_SIZE];staticOS_STKTASK_2_STK[TASK_2_STK_SIZE];staticOS_STKTASK_3_STK[TASK_3_STK_SIZE];staticOS_STKTASK_IDLE_STK[TASK_IDLE_STK_SIZE];externOS_U32SystemCoreClock;externvoidOSStart_Asm(void);externvoidOSCtxSw(void);externOS_U32OS_CPU_SR_Save(void);externvoidOS_CPU_SR_Restore(OS_U32);voidtask_1(void);voidtask_2(void);voidtask_3(void);voidOS_Task_Idle(void);voidOS_TimeDly(OS_U32);voidOS_Task_Switch(void);voidOS_Task_Create(OS_TCB*,OS_TASK,OS_STK*,OS_U8);voidOS_Task_Delete(OS_U8);voidOS_Task_End(void);voidOS_Init(void);voidOS_Start(void);voidtask_1(void){printf("[%d]Task1Runing!!!n",OS_TimeTick);OS_Task_Create(&TCB_2,task_2,&TASK_2_STK[TASK_2_STK_SIZE-1],5);OS_Task_Create(&TCB_3,task_3,&TASK_3_STK[TASK_3_STK_SIZE-1],7);}voidtask_2(void){while(1){printf("[%d]Task2Runing!!!n",OS_TimeTick);OS_TimeDly(1000);}}voidtask_3(void){while(1){printf("[%d]Task3Runing!!!n",OS_TimeTick);OS_TimeDly(1500);}}voidOS_Task_Idle(void){while(1){asm("WFE");OS_Task_Switch();OS_PendSV_Trigger();}}voidOS_TimeDly(OS_U32ticks){OS_USE_CRITICALOS_ENTER_CRITICAL();g_OS_Tcb_CurP->State=TASK_DELAY;g_OS_Tcb_CurP->TimeDly=ticks;OS_EXIT_CRITICAL();OS_Task_Switch();OS_PendSV_Trigger();}voidOS_Task_Switch(void){OS_S32i;OS_TCBPtcb_p;OS_USE_CRITICALfor(i=0;iState==TASK_READY)break;}OS_ENTER_CRITICAL();g_OS_Tcb_HighRdyP=tcb_p;g_Prio_HighRdy=i;OS_EXIT_CRITICAL();}voidOS_Task_Delete(OS_U8prio){if(prio>=OS_TASK_MAX_NUM)return;OS_TCB_TABLE[prio]=0;}voidOS_Task_End(void){printf("TaskofPrio%dEndn",g_Prio_Cur);OS_Task_Delete(g_Prio_Cur);OS_Task_Switch();OS_PendSV_Trigger();}voidOS_Task_Create(OS_TCB*tcb,OS_TASKtask,OS_STK*stk,OS_U8prio){OS_USE_CRITICALOS_STK*p_stk;if(prio>=OS_TASK_MAX_NUM)return;OS_ENTER_CRITICAL();p_stk=stk;p_stk=(OS_STK*)((OS_STK)(p_stk)&0xFFFFFFF8u);*(--p_stk)=(OS_STK)0x01000000uL;//xPSR*(--p_stk)=(OS_STK)task;//EntryPoint*(--p_stk)=(OS_STK)OS_Task_End;//R14(LR)*(--p_stk)=(OS_STK)0x12121212uL;//R12*(--p_stk)=(OS_STK)0x03030303uL;//R3*(--p_stk)=(OS_STK)0x02020202uL;//R2*(--p_stk)=(OS_STK)0x01010101uL;//R1*(--p_stk)=(OS_STK)0x00000000u;//R0*(--p_stk)=(OS_STK)0x11111111uL;//R11*(--p_stk)=(OS_STK)0x10101010uL;//R10*(--p_stk)=(OS_STK)0x09090909uL;//R9*(--p_stk)=(OS_STK)0x08080808uL;//R8*(--p_stk)=(OS_STK)0x07070707uL;//R7*(--p_stk)=(OS_STK)0x06060606uL;//R6*(--p_stk)=(OS_STK)0x05050505uL;//R5*(--p_stk)=(OS_STK)0x04040404uL;//R4tcb->StkAddr=p_stk;tcb->TimeDly=0;tcb->State=TASK_READY;OS_TCB_TABLE[prio]=tcb;OS_EXIT_CRITICAL();}voidSysTick_Handler(void){OS_TCBPtcb_p;OS_S32i;OS_USE_CRITICALOS_ENTER_CRITICAL();++OS_TimeTick;for(i=0;i State==TASK_DELAY){--tcb_p->TimeDly;if(tcb_p->TimeDly==0)tcb_p->State=TASK_READY;}}OS_EXIT_CRITICAL();}voidOS_Init(void){inti;g_OS_CPU_ExceptStkBase=OS_CPU_ExceptStk+OS_EXCEPT_STK_SIZE-1;asm("CPSIDI");for(i=0;i