lpc2200移植ucos-II總結(四 編寫os_cpu_c.c檔案)
2.4 編寫os_cpu_c.c檔案
OSTaskStkInt()任務堆疊初始化函式,在編寫此函式之前,必須先確定任務的堆疊結構。而任務的堆疊結構是與CPU的體系結構、編譯器有密切的關聯。本移植的堆疊結構見圖2.1所示。
圖2.1 任務堆疊結構圖
1. 函式OSTaskStkInt()程式碼
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt)
{
OS_STK *stk;
opt = opt; /* 'opt' 沒有使用。作用是避免編譯器警告 */
stk = ptos; /* 獲取堆疊指標 */
/* 建立任務環境,ADS1.2使用滿遞減堆疊 */
*stk = (OS_STK) task; /* pc */
*--stk = (OS_STK) task; /* lr */
*--stk = 0; /* r12 */
*--stk = 0; /* r11 */
*--stk = 0; /* r10 */
*--stk = 0; /* r9 */
*--stk = 0; /* r8 */
*--stk = 0; /* r7 */
*--stk = 0; /* r6 */
*--stk = 0; /* r5 */
*--stk = 0; /* r4 */
*--stk = 0; /* r3 */
*--stk = 0; /* r2 */
*--stk = 0; /* r1 */
*--stk = (unsigned int) pdata; /* r0,第一個引數使用R0傳遞 */
*--stk = (USER_USING_MODE|0x00); /* spsr,允許 IRQ, FIQ 中斷 */
*--stk = 0; /* 關中斷計數器OsEnterSum; */
return (stk);
}
OsEnterSum是作者定義的一個全域性變數,主要是用它來儲存關中斷的次數,這樣關中斷和開中斷就可以嵌套了。在呼叫OS_ENTER_CRITICAL()時,它的值加1,同時關中斷。在呼叫OS_EXIT_CRITICAL()時,它的值減1,同時開中斷。每個任務都有獨立的OsEnterSum,在任務切換時儲存和恢復各自的OsEnterSum。這樣,各個任務開關中斷的狀態可以不同,任務不必過分考慮關中斷對別的任務的影響。
說明:使用者建立任務時,OSTasKCreat()會呼叫OSTaskStkInt()函式初始化該任務的堆疊,並把返回的堆疊指標儲存到該任務的TCB結構中的最前面的引數OSTCBStkPtr中,當該任務要被恢復時,任務切換函式從其TCB塊中取得其任務堆疊指標,依次將堆疊內容彈到處理器對應的CPSR、r0、r1,…,r12,lr,pc的暫存器中,完成現場的恢復和程式指標PC的返回。
2. 軟體中斷異常SWI服務程式C語言部分
引數SWI_Num為功能號,而Regs為指向堆疊中儲存暫存器的值的位置。軟中斷的0、1號功能並沒有在這裡實現,而是在OS_CUP_A.S中實現。
軟中斷的C語言部分程式碼
void SWI_Exception(int SWI_Num, int *Regs)
{
OS_TCB *ptcb;
switch(SWI_Num)
{
//case 0x00: /* 任務切換函式OS_TASK_SW,參考os_cpu_s.s檔案 */
// break;
//case 0x01: /* 啟動任務函式OSStartHighRdy,參考os_cpu_s.s檔案 */
// break;
case 0x02: /* 關中斷函式OS_ENTER_CRITICAL(),參考os_cpu.h檔案 */
__asm /*C語言內嵌彙編指令關鍵字,通過__asm關鍵字可以在C程式中內嵌彙編程式*/
{
MRS R0, SPSR /*讀取SPSR暫存器的值,儲存到r0中
ORR R0, R0, #NoInt /*暫存器r0與立即數NoInt相或,並儲存到r0中,
MSR SPSR_c, R0 /*將r0的值寫入SPSR[7:0],關中斷
}
OsEnterSum++;
break;
case 0x03: /* 開中斷函式OS_EXIT_CRITICAL(),參考os_cpu.h檔案 */
if (--OsEnterSum == 0)
{
__asm
{
MRS R0, SPSR
BIC R0, R0, #NoInt
MSR SPSR_c, R0
}
}
break;
case 0x80: /* 任務切換到系統模式 */
__asm
{
MRS R0, SPSR
BIC R0, R0, #0x1f /* 將R0的低5位清零,其它位不變 */
ORR R0, R0, #SYS32Mode
MSR SPSR_c, R0
}
break;
case 0x81: /* 任務切換到使用者模式 */
__asm
{
MRS R0, SPSR
BIC R0, R0, #0x1f
ORR R0, R0, #USR32Mode
MSR SPSR_c, R0
}
break;
case 0x82: /* 任務是ARM程式碼 */
if (Regs[0] <= OS_LOWEST_PRIO)
{
ptcb = OSTCBPrioTbl[Regs[0]];/*ptcb指向當前任務堆疊的首
地址,也就是當前任務堆疊結構的OsEnterSum
if (ptcb != NULL)
{
ptcb -> OSTCBStkPtr[1] &= ~(1 << 5) /*OSTCNStkPtr指向任務堆疊的棧頂,所以OSTCBStkPtr[1]指向任務堆疊的SPSR暫存器,通過改變SPSR相應位切換處理器模式
}
}
break;
case 0x83: /* 任務是THUMB程式碼 */
if (Regs[0] <= OS_LOWEST_PRIO)
{
ptcb = OSTCBPrioTbl[Regs[0]];
if (ptcb != NULL)
{
ptcb -> OSTCBStkPtr[1] |= (1 << 5);
}
}
break;
default:
break;
}
3. 巨集OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()
這裡需要對關中斷函式和開中斷函式作些解釋。與所有的實時核心一樣,μC/OS-Ⅱ需要先禁止中斷再訪問程式碼的臨界段,並且在訪問完畢後重新允許中斷。這就使得μC/OS-Ⅱ能夠保護臨界段程式碼免受多工或中斷服務破壞。實現OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()這2個巨集的定義有3種方法,本移植採用的是第三種方法,即在OS_CPU.H檔案中使OS_CRITICAL_MEHTOD等於3,這種方法是利用某些編譯器提供的擴充套件功能,使用者可以得到當前處理器狀態字的值,並將其儲存在C函式的區域性變數之中,這個變數可以用於恢復PSW,而本ARM核心關中斷和開中斷時,是通過改變程式狀態暫存器CPSR中的相應控制位實現。由於使用了軟體中斷,程式狀態暫存器 CPSR儲存到程式狀態儲存暫存器 SPSR中,軟體中斷退出時會將SPSR恢復到CPSR中。所以程式只要改變程式狀態儲存暫存器SPSR中相應的控制位就可以了。
4. OSStartHighRdy
uC/OS-II啟動多工環境的函式是OSStart()。使用者在呼叫OSStart()之前,必須已經建立了一個或更多工。OSStart()最終呼叫函式OSStartHighRdy()執行多工啟動前優先順序最高的任務,OSStartHighRdy()的程式碼如下。
void OSStartHighRdy(void)
{
_OSStartHighRdy();
}
通過函式_OSStartHighRdy()進入軟中斷SoftwareInterrupt,最終呼叫__OSStartHighRdy。__OSStartHighRdy程式碼如下所示
__OSStartHighRdy
MSR CPSR_c, #(NoInt | SYS32Mode);/*關中斷,切換到系統模式
/*告訴uC/OS-II自身已經執行,將1儲存到OSRunning*/
LDR R4, =OSRunning /*將OSRunning的地址載入到R4,
R4裡存放的是OSRunning的地址
MOV R5, #1
STRB R5, [R4] /*將R5的值儲存到R4指定的地址中,
只儲存1位元組資料
BL OSTaskSwHook ;呼叫鉤子函式
LDR R6, =OSTCBHighRdy /*將OSTCBHighRdy的地址載入到R6
LDR R6, [R6] /*載入R6指定地址上的資料(字),放入R6中
B OSIntCtxSw_1
5. 移植增加的特定函式
根據ARM核心的特點和移植目標,為此增加了兩個處理器模式轉換函式(ChangTo-SYSMode()、ChangeToUSRMode())和兩個任務初始指令集設定函式(TaskIsARM()、TaskIsTHUMBle())。他們都是通過中斷指令SWI轉換到系統模式,通過軟體中斷服務程式實現的。
ChangeToSYSMode()把當前任務轉換到系統模式,ChangeToUSRMode()把當前任務轉換到使用者模式。她們改變程式狀態儲存暫存器SPSR的相應位段,而程式狀態儲存暫存器會在軟體中斷退出時複製到程式狀態暫存器CPSR,任務的處理器模式就改變了。
本移植增加了兩個函式TaskIsARM()和TaskIsTHUMB()用於改變任務建立時預設的指令集。函式TaskIsARM()用於宣告指定優先順序的任務的第一條指令是ARM指令集中的指令,而函式TaskIsTHUMB()用於宣告指定優先順序的任務的第一條指令是THUMB指令集中的指令,她們都有唯一的引數,即需要改變的任務的優先順序。值得注意的是,這兩個函式必須在相應的任務建立後但還沒執行時呼叫。這樣,如果在低優先順序的任務中建立高優先順序的任務就十分危險了。此時,解決的方法有三種:
(1) 高優先順序任務使用預設的指令集;
(2) 改變函式OSTaskCreateHook()使任務預設不是出於就緒狀態,建立任務後呼叫函式OSTaskResume()來使任務進入就緒狀態。
(3) 建立任務時禁止任務切換,呼叫函式TaskIsARM()或TaskIsTHUMB()後再允許任務切換。
6. …Hook()函式
uC/OS-II有很多由使用者編寫的…Hook()函式,它在移植中全為空函式,使用者就可以按照uC/OS-II的要求修改它。