(1)UCOSII移植到不同的處理器上,所謂的移植就是將一個實時的內(nèi)核能在其他的微處理器或者微控制器上運行。
為了方便移植,UCOSII的大部分的代碼都是C語言寫的,因為不同機器的匯編代碼是不一樣的。這是由于UCOSII在設計的時候已經(jīng)充分考慮到了可移植性這一點。
但是仍然有一部分的代碼是需要用C語言和匯編語言寫一些與處理器有關的代碼。
(2)要使用UCOSII正常的運行,處理器必須滿足以下的條件
2.1、處理器的C編譯器能產(chǎn)生可重入型的代碼
原因:如果不行的話,那么就不能在任務之間隨意的切換,因為當你切換到別的任務的時候,該任務在這個函數(shù)的數(shù)據(jù)就會被破壞。
2.2、處理器支持中斷,并能產(chǎn)生定時中斷
2.3、用C語言就可以開關中斷
原因:連中斷都沒有的話,是不可能進行任務切換的。
2.4、處理器能夠支持一定數(shù)量的數(shù)據(jù)存儲硬件堆棧,也就是棧
2.5、處理器有將堆棧指針以及其他的CPU的寄存器的內(nèi)容讀出,并存儲到堆?;蛘邇?nèi)存中去的指令。
原因:因為我們在任務切換的時候,需要將當前的CPU指針保存到剛剛執(zhí)行的任務當中。然后切換到優(yōu)先級更高的任務當中。
其實我們移植UCOSII額時候,大部分的代碼是基于底層進行編寫的,所以我們不需要進行移植。有一些是對這個UCOSII進行的配置的,所以我們不需要進行移植。
我們唯一需要關注的三個文件就是下面的三個文件。
OS_CPU.h
OS_CPU_A.ASM
OS_CPU_C.c
一般我們在開發(fā)的時候,我們會把所有的頭文件定義在同一個頭文件當中,因為這樣我們只需要包含一個頭文件就可以了,不會重復的包含多個頭文件。
需要移植的幾個文件:
INCLUDES.H
是一個主頭文件,出現(xiàn)在每個.c 文件的第一行。
使得每個.c的文件中無需分別考慮它實際上需要哪些頭文件,使用INCLUDE.H唯一的缺點就是,它可能包含一些與當前的編譯的.c文件不相干的頭文件。
OS_CPU.H
包含了用#define 語句定義的,與處理器相關的常數(shù),宏以及類型。
OS_CPU.H的大體結(jié)構(gòu)如程序清單所列。
這里面使用typedef用的比#define更加的好,因為#define僅僅相當于字符串的拷貝,但是typedef相當于命名了一個別名。
- /* 數(shù)據(jù)類型 */
- typedef unsigned char BOOLEAN;
- typedef unsigned int OS_STK; //堆棧入口的寬度為16位
- typedef unsigned short OS_CPU_SR; //定義CPU狀態(tài)寄存器的寬度為16位
- /* 與處理器有關的代碼 */
- #define OS_ENTER_CRITICAL() //進入臨界區(qū)的代碼
- #define OS_EXIT_CRITICAL() //跳出臨界區(qū)的代碼
- #define OS_STK_GHOWTH 1 定義堆棧的方向:1=向下遞減,0=向上遞增
- #define OS_TASK_SW() ?? //定義軟件任務切換的函數(shù)。
最關鍵的移植文件:CPU的文件
移植文件3:OS_CPU_C.c
UCOSII的移植范例要求用戶編寫10個簡單的C函數(shù):
OSTaskStkInit();
OSTaskCreateHook();
OSTaskDelHook();
OSTaskSwHook();
OSTaskIdleHook();
OSTaskStatHook();
OSTimeTickHook();
OSInitHookBegin();
OSInitHookend();
OSTCBInitHook();
PS:唯一必要的函數(shù)是OSTaskStkinit();其余的9個函數(shù)必須聲明,但是并不一定要包含任何代碼,這些函數(shù)的原型放在本章的末尾。
(1)因此堆??雌饋砭拖裰袛鄤偘l(fā)生的一樣,所有的寄存器都保存在堆棧中,OSTaskStkInit()的示例性代碼:
- OS_STK * OSTaskStkInit(void (*task)(void * pd),
- void * pada,
- OS_STK *ptos,
- INT16U opt);
- {
- pada = pada;
- 模擬ISR向量;
- //按照預先設計的寄存器值初始化堆棧結(jié)構(gòu);
- //不斷的在堆棧中相應的位置填入你要傳遞的參數(shù)
- //返回棧頂指針給調(diào)用該函數(shù)的函數(shù)
- //在這里假定堆棧是從上往下遞減的,下面討論同樣適用于以相反方向從下到上遞增的堆棧結(jié)構(gòu)。
- }
(2)OSTaskCreateHook():
每當添加任務的時候,OS_TCBInit()函數(shù)都會調(diào)用OSTaskCreateHook()函數(shù),該函數(shù)允許擴展UCOSII的功能,當UCOSII設置完任務控制塊OS_TCB初始化的絕大部分的工作后,但是在任務控制塊被鏈接到相應的任務鏈中之前,以及在該任務就緒運行之前,UCOSII會調(diào)用OSTaskCreateHook(),該函數(shù)被調(diào)用的時候中斷是打開的。
鉤子函數(shù)就是為了檢查相應的操作有沒有成功的。
(3)OSTaskIdleHook()
很多微處理器都允許執(zhí)行相應的指令,將CPU置于低功耗模式。而當接收到中斷信號的時候,CPU就會退出低功耗模式,OSTaskIdle()函數(shù)可調(diào)用OSTaskIdleHook()函數(shù),實現(xiàn)CPU的這種低功耗的模式:
PS:其實這里真的設計的非常的巧妙,因為你沒有任何的任務進行調(diào)度的時候,應該是沒有什么執(zhí)行的,但是我們UCOSII系統(tǒng),規(guī)定當沒有任何的任務需要強占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個簡單的匯編語言函數(shù):
OSStartHighRdy(); //使得最高優(yōu)先級的任務運行的函數(shù)
OSCtxSw(); //任務的切換的函數(shù)
OSIntCtxSw();
OSTickISR();
如果編譯器支持插入行匯編代碼就可以將所有的與處理器相關的代碼放置到OS_CPU_C.c里面種,就不需要適用匯編文件了。
(1)OSStartHighRdy()
OSStart()函數(shù)調(diào)用OSStartHighRdy()來使得就緒太任務中最高優(yōu)先級的任務開始運行,這個函數(shù)的示例性的代碼
- void OSStartHighRdy()
- {
- 調(diào)用用戶定義的OSTaskSwHook();
- OSRunning = TRUE;
- //得到將要恢復運行的任務的堆棧指針。
- stack pointer = OSTcbHighRdy->OSTCBStkPtr;
- //從新的堆棧中恢復處理器的所有的寄存器,就是把剛剛切換的堆棧保存到別的地方當中
- //執(zhí)行中斷返回,然后跳轉(zhuǎn)PC指針到別的地方中去。
- }
(2)OSCtxSw():
任務級的切換是通過執(zhí)行軟中斷指令,或者依據(jù)處理器的不同,執(zhí)行TPAP陷阱指令執(zhí)行的。中斷服務子程序,陷阱或者異常處理的向量的地址必須指向OSCtxSw();
- void OSCtxSw()
- {
- 保存處理器寄存器;
- 在當前的任務的任務控制塊中保存當前任務的堆棧指針;
- OSTCBCur->OSTCBStkPtr = stack pointer;
- OSTaskSwHook();
- OSTCBCur = OSTCBHighRdy;
- OSPrioCur = OSPrioHighRdy;
- //得到將要重新開始運行的任務的堆棧指針:
- stack pointer = OSTCBHighRdy->OSTCBstkPtr;
- //從新的任務堆棧中恢復所有的寄存器的值;
- //執(zhí)行中斷返回的指令。
- }
(3)OSTickISR()
UCOSII要求用戶提供一個周期性的時鐘源,來實現(xiàn)時間延遲和超時功能,時鐘節(jié)拍應該每秒發(fā)生10或者100次每秒,為了完成任務,可以使用硬件定時器,也可以從交流電中獲得50~60Hz的時鐘頻率。
必須在開啟多任務后,即調(diào)用OSStart()后,啟動時鐘節(jié)拍中斷,但是由于OSStart()函數(shù)不會返回。不能在還沒有運行第一個任務的時候,啟動時鐘節(jié)拍中斷。會導致程序跑飛。
在沒有執(zhí)行OSStart()之前不能打開時鐘節(jié)拍中斷。千萬不能在這里開中斷。
因為UCOSII此時仍然處于未知的狀態(tài),所以一旦跳入中斷,就會跑飛。
定時器允許用戶被掛起一定的時間:
- void OSTickISR(void)
- {
- //保存處理器的寄存器
- //調(diào)用OSIntEnter或者直接給OSIntNesting加1
- if(OSIntNesting ==1)
- {
- OSTCBCur->OSTCBStkPtr = Stack Pointer;
- }
- //給產(chǎn)生中斷的設備清中斷
- //重新允許中斷
- OSTimeTick(); //硬件的產(chǎn)生中斷的原理
- OSIntExit(); //中斷退出
- //恢復處理器寄存器
- //執(zhí)行中斷返回指令
- }
OSIntCtxSw()
- void OSintCtxSw(void)
- {
- //調(diào)整堆棧指針
- OSintExit();
- OSINTCtxSw();
- }