在任務創(chuàng)建時指定棧頂、棧底,在任務運行一段時間后,一般需在堆棧使用最充分后才去檢驗較為準確;任務可以檢查自己或者其他任務的堆棧使用情況,堆棧檢驗函數(shù)確定堆棧的實際空間字節(jié)數(shù)和已被占用的字節(jié)數(shù),放在入口參數(shù)OS_STK_DATA數(shù)據(jù)結構中;
系統(tǒng)在任何響應后,都需要進行任務調度,確保系統(tǒng)中優(yōu)先級最高的任務被執(zhí)行,那么系統(tǒng)采用的任務調度切換函數(shù)就是void
OS_Sched(void);
//Cortex-M3進入異常服務例程時,使用的是MSP指向的堆??臻g,且自動壓入了R0-R3,R12,LR(R14,連接寄存器),PSR(程序狀態(tài)寄存器)和PC(R15),這些寄存器是任務被中斷時的現(xiàn)場記錄,此時任務所使用的PSP的值沒有變化。
//M3處理器的控制寄存器的CONTROL[1] =
1(系統(tǒng)復位后默認是0),表明M3的線程模式的堆棧指針SP選用PSP【handler模式只允許使用MSP】,這樣兩個模式下SP使用不同的堆棧指針,且訪問SP時,訪問到的是當前被使用的堆棧指針,這時另一個堆棧指針要通過MRS、MSR命令來訪問,即R13(PSP)或R13(MSP),R13是PSP還是MSP由系統(tǒng)當前的狀態(tài)與CONTROL[1]決定,PSP是某個任務在RAM中的堆棧指針,MSP是異常模式下在RAM中的堆棧指針。
//一個任務占用CPU運行,它的運行現(xiàn)場是R0-R3,R4-R11,R12,R13(PSP/MSP),R14(LR),PSR和R15(PC),如果該任務被中斷,那么在進中斷過程中,硬件會自動對CPU的部分現(xiàn)場寄存器(R0-R3,R12,R14,PSR和R15)進行壓棧,其他寄存器硬件不壓棧,如果CONTROL[1]
= 1,那么硬件就會將相關寄存器壓入PSP(R13)指向的堆棧中(線程模式時SP為PSP),CONTROL[1] =
0,硬件就會將相關寄存器壓入MSP(R13)指向的堆棧中;CONTROL[1]
= 1時如下圖所示,線程模式與handler模式的SP不一樣。
OSPendSV 【在進入中斷前 R0-R3,R12,R14,PSR和R15已被壓入任務所使用的堆棧中,下面是將其他的寄
存器再壓入任務的堆棧中,以及保存該任務的堆棧棧頂?shù)闹档絇SP指向的堆棧中去,任務使用
的是PSP,異常模式下使用的是MSP】
MRS
R0, PSP
//R0<=PSP 異常模式下使用的是MSP,所以只能通過MRS讀取被中斷的任務的堆棧指針,
CBZ
R0, OSPendSV_nosave
//如果PSP==0,則跳轉到 OSPendSV_nosave ,否則往下執(zhí)行【在系統(tǒng)剛開
//始啟動時,OSStart()會調用OS_SchedNew()與OSStartHighRdy()來使
//系統(tǒng)中最高優(yōu)先級的任務運行,在OSStartHighRdy中,PSP設置為0,因為
//在OSStart前還沒有任務運行,所以進入軟中斷后不用保存此時CPU的值,
//只要將最高優(yōu)先級任務的現(xiàn)場恢復到CPU的各寄存器就好了】
SUBS
R0, R0, #0x20
//R0(PSP)!=0,那么R0的值減去0X20,32個字節(jié)(下面保持8個32位的寄存器)
STM
R0, {R4-R11}
//將R4~R11這8個寄存器存儲到PSP對應的空間中去,這些寄存器是當前任務被中
//斷時的現(xiàn)場
LDR
R1, __OS_TCBCur //__OS_TCBCur
DCD OSTCBCur
//當前任務(被中斷的任務)的任務控制塊OSTCBCur, OSTCBCur是當前任務控制
//塊的首地址,也代表了它的第一個成員的地址,所以下面其實是對他的第一個
//成員OS_STK
*OSTCBStkPtr(指向任務的堆棧棧頂?shù)闹羔槪┻M行賦值
LDR
R1, [R1]
//將OSTCBStkPtr中保持的值賦給R1,該值指向任務的堆棧棧頂?shù)闹羔?/span>
//即R1存儲被中斷的當前任務的堆棧棧頂?shù)闹羔?/span>
STR
R0, [R1]
//將被中斷的當前任務的堆棧棧頂?shù)闹羔槾娴絇SP指向的堆棧中去,即保存被中斷
//任務所使用的堆棧棧頂?shù)闹羔?上面其他的寄存器是任務被中斷的線場
OSPendSV_nosave
【上面的程序執(zhí)行完后,接著繼續(xù)執(zhí)行下面的程序】
PUSH
{R14}
//保存R14寄存器的值 壓入到MSP指向的堆棧中,該寄存器在中斷返回時大有作用
LDR
R0, __OS_TaskSwHook //調用回調函數(shù)
鉤子函數(shù)
BLX
R0
POP
{R14}
-------------------------------------------
//相當于定義指針變量:__OS_PrioCur DCD
OSPrioCur;
__OS_PrioHighRdy DCD
OSPrioHighRdy
LDR
R0, __OS_PrioCur
//INT8U OSPrioCur當前任務的優(yōu)先級
LDR
R1, __OS_PrioHighRdy //OSPrioHighRdy系統(tǒng)中最高的優(yōu)先級
LDRB
R2, [R1]
//上面兩句是將變量的地址傳給了R0與R1,[R1]最高優(yōu)先級的值存入R2
STRB
R2, [R0]
//將R2中的值存入到OSPrioCur變量中去(即接下來運行最高優(yōu)先級任務)
//即實現(xiàn):OSPrioCur
= OSPrioHighRdy;
-------------------------------------------------
//相當于定義指針變量:__OS_TCBCur DCD
OSTCBCur;
__OS_TCBHighRdy DCD
OSTCBHighRdy
LDR
R0, __OS_TCBCur
//R0=&OSTCBCur;
OS_TCB *OSTCBCur; OS_TCB
*OSTCBHighRdy;
LDR
R1, __OS_TCBHighRdy
//R1=&OSTCBHighRdy;這四句同理上面,OSTCBCur
= OSTCBHighRdy
LDR
R2, [R1]
//R2 = *R1;
R2=OSTCBHighRdy 指向優(yōu)先級最高的任務的任務控制塊
STR
R2, [R0]
//*R0 =
R2; 即實現(xiàn):OSTCBCur
= OSTCBHighRdy;
//系統(tǒng)中用OSPrioCur
OSTCBCur 來表示正在運行的任務
----------------------------------------------
【通過R2(OSTCBHighRdy)將OSTCBHighRdy->OSTCBStkPtr的值賦給R0,再通過LDM將最高優(yōu)先級任務的堆棧中保存好的R4-R11恢復到當前CPU的R4-R11寄存器中(這個過程與上面的保存過程是相逆的),因為R0-R3,R12,R14,PSR和R15是硬件自動壓入任務的堆棧的,在中斷中后來再保存R4-R11的(是先對SP減了32后再保存,相當于SP的值沒有變化),且中斷退出時會自動彈出R0-R3,R12,R14,PSR,所以下面的代碼只要恢復最高優(yōu)先級任務的R4-R11(OSTCBHighRdy->OSTCBStkPtr通過LDM自減了32,),然后將加上32,
OSTCBHighRdy->OSTCBStkPtr 相當于沒有變化,再將OSTCBHighRdy->OSTCBStkPtr賦給PSP,這樣硬件在中斷退出時會自動恢復
R0-R3,R12,R14,PSR ,這樣就完全恢復了,一運行就是最高優(yōu)先級的任務了】
LDR
R0, [R2]
//[R2]為*OSTCBHighRdy,R2是4字節(jié)的寄存器,所以[R2]是OSTCBHighRdy
//指向地址的后4個字節(jié)的值,即OSTCBHighRdy->OSTCBStkPtr,
//即 R0
= OSTCBHighRdy->OSTCBStkPtr
LDM
R0, {R4-R11}
//將 OSTCBHighRdy->OSTCBStkPtr(R0)指向的堆棧(向下生長)棧頂后
//的32個字節(jié)依次存入R4-R11(R0的值自減,后綴為!表示自增),因為堆
//棧保持時是自減的,這里相當于恢復最高優(yōu)先級任務的現(xiàn)場)
ADDS
R0, R0, #0x20
//將R0的值加上0x20后賦給R0
MSR
PSP, R0
//PSP <= R0
ORR LR, LR, #0x04
//按位或
將LR的第二位置1,這樣中斷返回時,就將從進程的堆棧中做
//出棧操作,返回后使用PSP(否則將從主堆棧中做出棧操作,返回后
//使用MSP)
BX
LR
//中斷返回
xPSR、PC、LR、R12、R3、R2、R1、R0會被硬件按一定的次序壓入PSP所指向的堆棧中(同時被中斷的任務后面要恢復執(zhí)行,還需保存R4-R11,保存后一定要保證堆棧指針指向硬件自動壓棧后的原位置,這樣硬件才能在恢復任務時硬件自動正確出棧)
2:在中斷中實現(xiàn)任務的切換
uC/OS II 的中斷服務函數(shù)必須遵循一定的架構來實現(xiàn),
void SysTickHandler(void)
{
OS_CPU_SR cpu_sr;
OS_ENTER_CRITICAL();
//保存全局中斷標志,關總中斷
OSIntNesting++;
OS_EXIT_CRITICAL(); //恢復全局中斷標志
OSTimeTick();
OSIntExit();
//在os_core.c文件里定義,如果有更高優(yōu)先級的任務就緒了,則執(zhí)行一次任務切換
OSInit函數(shù)
系統(tǒng)初始化函數(shù)OSInit()初始化所有的變量和數(shù)據(jù)結構,同時也會建立空閑任務OS_TaskIdle(),該任務永遠處于就緒態(tài),如果統(tǒng)計任務使能,那么他還要建立統(tǒng)計任務OS_TaskStat(),并使其進入就緒態(tài)。
uC/OS II總要建立一個空閑任務,idle
task,這個任務在沒有其他任務進入就緒態(tài)時投入運行,它的優(yōu)先級永遠設為最低優(yōu)先級,即OS_LOWEST_PRIO,且空閑任務是不能被應用軟件刪除的;
空閑任務不停的給一個32位的OSIdleCtr的變量加1,統(tǒng)計任務用這個計數(shù)器變量確定當前應用軟件實際消耗CPU的時間,計數(shù)器加1前后分別關閉打開中斷,OS_TaskIdle可以借助OSTaskIdleHook()做CPU的睡眠等;OS_TaskIdle總是處于就緒態(tài)。
統(tǒng)計任務OS_TaskStat
只要將OS_TASK_STAT_EN宏使能,那么統(tǒng)計任務就會建立,一旦運行,它將每秒運行一次,計算當前CPU的利用率,將值放在OSCPUsage這個8位的變量中,用百分比表示,精度為1%.如果應用程序打算使用統(tǒng)計任務,那么必須在初始化時建立的第一個也是唯一的任務中調用統(tǒng)計任務初始化函數(shù)OSStatInit(),也就是在調用系統(tǒng)啟動函數(shù)OSStart前,用戶初始化代碼中必須先建立一任務,在這個任務中調用系統(tǒng)統(tǒng)計初始化函數(shù)OSStatInit,然后再建立應用程序中的其他任務。它的優(yōu)先級是OS_LOWEST_PRIO-1
OSStart操作系統(tǒng)啟動函數(shù)
在uC/OS
II啟動之前,至少須建立一個應用程序,因為如果使能統(tǒng)計任務,那么統(tǒng)計任務要求必須先建立一個也是唯一一個用戶任務后,再啟動系統(tǒng),再創(chuàng)建其他的任務,如果什么任務都沒有建立,只要空閑任務,那系統(tǒng)也就一直空閑,所以需要先創(chuàng)建至少一個任務。