從零開始學習UCOSII操作系統13--系統移植理論篇

從零開始學習UCOSII操作系統13--系統移植理論篇

1、什麼是系統移植?

(1)UCOSII移植到不同的處理器上,所謂的移植就是將一個實時的內核能在其他的微處理器或者微控制器上運行。

爲了方便移植,UCOSII的大部分的代碼都是C語言寫的,因爲不同機器的彙編代碼是不一樣的。這是由於UCOSII在設計的時候已經充分考慮到了可移植性這一點。

但是仍然有一部分的代碼是需要用C語言和彙編語言寫一些與處理器有關的代碼。

(2)要使用UCOSII正常的運行,處理器必須滿足以下的條件
2.1、處理器的C編譯器能產生可重入型的代碼

原因:如果不行的話,那麼就不能在任務之間隨意的切換,因爲當你切換到別的任務的時候,該任務在這個函數的數據就會被破壞。

2.2、處理器支持中斷,並能產生定時中斷

2.3、用C語言就可以開關中斷

原因:連中斷都沒有的話,是不可能進行任務切換的。

2.4、處理器能夠支持一定數量的數據存儲硬件堆棧,也就是棧

2.5、處理器有將堆棧指針以及其他的CPU的寄存器的內容讀出,並存儲到堆棧或者內存中去的指令。

原因:因爲我們在任務切換的時候,需要將當前的CPU指針保存到剛剛執行的任務當中。然後切換到優先級更高的任務當中。

2、移植UCOSII實際上需要移植什麼文件?

其實我們移植UCOSII額時候,大部分的代碼是基於底層進行編寫的,所以我們不需要進行移植。有一些是對這個UCOSII進行的配置的,所以我們不需要進行移植。

我們唯一需要關注的三個文件就是下面的三個文件。
OS_CPU.h
OS_CPU_A.ASM
OS_CPU_C.c

一般我們在開發的時候,我們會把所有的頭文件定義在同一個頭文件當中,因爲這樣我們只需要包含一個頭文件就可以了,不會重複的包含多個頭文件。

需要移植的幾個文件:
INCLUDES.H
是一個主頭文件,出現在每個.c 文件的第一行。

使得每個.c的文件中無需分別考慮它實際上需要哪些頭文件,使用INCLUDE.H唯一的缺點就是,它可能包含一些與當前的編譯的.c文件不相干的頭文件。

OS_CPU.H
包含了用#define 語句定義的,與處理器相關的常數,宏以及類型。
OS_CPU.H的大體結構如程序清單所列。

這裏面使用typedef用的比#define更加的好,因爲#define僅僅相當於字符串的拷貝,但是typedef相當於命名了一個別名。

/*        數據類型       */

typedef unsigned char BOOLEAN;

typedef unsigned int OS_STK;   //堆棧入口的寬度爲16位
typedef unsigned short OS_CPU_SR; //定義CPU狀態寄存器的寬度爲16位

/*         與處理器有關的代碼    */
#define OS_ENTER_CRITICAL()     //進入臨界區的代碼
#define OS_EXIT_CRITICAL()      //跳出臨界區的代碼

#define OS_STK_GHOWTH   1  定義堆棧的方向:1=向下遞減,0=向上遞增

#define OS_TASK_SW()  ??  //定義軟件任務切換的函數。

最關鍵的移植文件:CPU的文件
移植文件3:OS_CPU_C.c
UCOSII的移植範例要求用戶編寫10個簡單的C函數:

OSTaskStkInit();
OSTaskCreateHook();
OSTaskDelHook();
OSTaskSwHook();
OSTaskIdleHook();
OSTaskStatHook();
OSTimeTickHook();
OSInitHookBegin();
OSInitHookend();
OSTCBInitHook();

PS:唯一必要的函數是OSTaskStkinit();其餘的9個函數必須聲明,但是並不一定要包含任何代碼,這些函數的原型放在本章的末尾。

(1)因此堆棧看起來就像中斷剛發生的一樣,所有的寄存器都保存在堆棧中,OSTaskStkInit()的示例性代碼:

OS_STK * OSTaskStkInit(void (*task)(void * pd),
                        void * pada,
                        OS_STK  *ptos,
                        INT16U  opt);
{
    pada = pada;
    模擬ISR向量;

    //按照預先設計的寄存器值初始化堆棧結構;
    //不斷的在堆棧中相應的位置填入你要傳遞的參數
    //返回棧頂指針給調用該函數的函數

    //在這裏假定堆棧是從上往下遞減的,下面討論同樣適用於以相反方向從下到上遞增的堆棧結構。
}

(2)OSTaskCreateHook():
每當添加任務的時候,OS_TCBInit()函數都會調用OSTaskCreateHook()函數,該函數允許擴展UCOSII的功能,當UCOSII設置完任務控制塊OS_TCB初始化的絕大部分的工作後,但是在任務控制塊被鏈接到相應的任務鏈中之前,以及在該任務就緒運行之前,UCOSII會調用OSTaskCreateHook(),該函數被調用的時候中斷是打開的。

鉤子函數就是爲了檢查相應的操作有沒有成功的。

(3)OSTaskIdleHook()
很多微處理器都允許執行相應的指令,將CPU置於低功耗模式。而當接收到中斷信號的時候,CPU就會退出低功耗模式,OSTaskIdle()函數可調用OSTaskIdleHook()函數,實現CPU的這種低功耗的模式:

PS:其實這裏真的設計的非常的巧妙,因爲你沒有任何的任務進行調度的時候,應該是沒有什麼執行的,但是我們UCOSII系統,規定當沒有任何的任務需要強佔CPU的時候,我們應該讓其進入低功耗的模式,真的設計的很好。

void OS_TaskIdle(void *pdata)
{
    pdata = pdata;
    for(;;)
    {
        OS_ENTER_CRITICAL();
        OSIdleCtr++;
        OS_EXIT_CRITICAL();
        OSTaskIdleHook();
    }
}

void OSTaskIdleHook(void)
{
    asm("STOP");
    //收到中斷並完成中斷服務。
}

OS_CPU_A.ASm
UCOSII的移植實例就是要求用戶編寫4個簡單的彙編語言函數:
OSStartHighRdy(); //使得最高優先級的任務運行的函數
OSCtxSw(); //任務的切換的函數
OSIntCtxSw();
OSTickISR();

如果編譯器支持插入行彙編代碼就可以將所有的與處理器相關的代碼放置到OS_CPU_C.c裏面種,就不需要適用彙編文件了。

(1)OSStartHighRdy()

OSStart()函數調用OSStartHighRdy()來使得就緒太任務中最高優先級的任務開始運行,這個函數的示例性的代碼

void OSStartHighRdy()
{
    調用用戶定義的OSTaskSwHook();
    OSRunning = TRUE;
    //得到將要恢復運行的任務的堆棧指針。
    stack pointer = OSTcbHighRdy->OSTCBStkPtr;
    //從新的堆棧中恢復處理器的所有的寄存器,就是把剛剛切換的堆棧保存到別的地方當中
    //執行中斷返回,然後跳轉PC指針到別的地方中去。

}

(2)OSCtxSw():

任務級的切換是通過執行軟中斷指令,或者依據處理器的不同,執行TPAP陷阱指令執行的。中斷服務子程序,陷阱或者異常處理的向量的地址必須指向OSCtxSw();

void OSCtxSw()
{
    保存處理器寄存器;
    在當前的任務的任務控制塊中保存當前任務的堆棧指針;
    OSTCBCur->OSTCBStkPtr = stack pointer;
    OSTaskSwHook();
    OSTCBCur = OSTCBHighRdy;
    OSPrioCur = OSPrioHighRdy;

    //得到將要重新開始運行的任務的堆棧指針:
    stack pointer = OSTCBHighRdy->OSTCBstkPtr;
    //從新的任務堆棧中恢復所有的寄存器的值;
    //執行中斷返回的指令。
}

(3)OSTickISR()
UCOSII要求用戶提供一個週期性的時鐘源,來實現時間延遲和超時功能,時鐘節拍應該每秒發生10或者100次每秒,爲了完成任務,可以使用硬件定時器,也可以從交流電中獲得50~60Hz的時鐘頻率。

必須在開啓多任務後,即調用OSStart()後,啓動時鐘節拍中斷,但是由於OSStart()函數不會返回。不能在還沒有運行第一個任務的時候,啓動時鐘節拍中斷。會導致程序跑飛。

在沒有執行OSStart()之前不能打開時鐘節拍中斷。千萬不能在這裏開中斷。

因爲UCOSII此時仍然處於未知的狀態,所以一旦跳入中斷,就會跑飛。

定時器允許用戶被掛起一定的時間:

void OSTickISR(void)
{
    //保存處理器的寄存器
    //調用OSIntEnter或者直接給OSIntNesting加1
    if(OSIntNesting ==1)
    {
        OSTCBCur->OSTCBStkPtr = Stack Pointer;
    }
    //給產生中斷的設備清中斷
    //重新允許中斷
    OSTimeTick();   //硬件的產生中斷的原理
    OSIntExit();    //中斷退出
    //恢復處理器寄存器
    //執行中斷返回指令
}

OSIntCtxSw()

void OSintCtxSw(void)
{
    //調整堆棧指針
    OSintExit();
    OSINTCtxSw();
}
發佈了89 篇原創文章 · 獲贊 86 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章