從單片機開發,嵌入式開發中 μC/OS 升級到 μC/OS-II
本章描述如何從μC/OS 升級到 μC/OS-II。如果已經將μC/OS移植到了某類單片機開發上,移植μC/OS-II所要做的工作應當非常有限。在多數情況下,用戶能夠在1個小時之內完成這項工作。如果用戶熟悉μC/OS的移植,可隔過本章前一部分直接參閱10.05節。
10.
目錄和文件
用戶首先會注意到的是目錄的結構,主目錄不再叫 \SOFTWARE\uCOS。而是叫 \SOFTWARE\uCOS-II。所有的μC/OS-II文件都應放在用戶硬盤的\SOFTWARE\uCOS-II 目錄下。面向不同的微處理器或微處理器的源代碼一定是在以下兩個或三個文件中: OS_CPU.H, OS_CPU_C.C,或許還有OS_CPU_A.ASM.。匯編語言文件是可有可無的,因為有些C編譯程序允許使用在線匯編代碼,用戶可以將這些匯編代碼直接寫在 OS_CPU_C.C.中。
與微處理器有關的特殊代碼,即與移植有關的代碼,在 μC/OS 中是放在用微處理器名字命名的文件中的,例如,Intel 80x86的實模式(Real Mode),在大模式下編譯(Large Modle)時,文件名為Ix86L.H, Ix86L_C.C, 和Ix86L_A.ASM.。
表 L10.1在μC/OS-II中重新命名的文件.
|
\SOFTWARE\uCOS\Ix86L | \SOFTWARE\uCOS-II\Ix86L |
Ix86L.H | OS_CPU.H |
Ix86L_A.ASM | OS_CPU_A.ASM |
Ix86L_C.C | OS_CPU_C.C |
升級可以從這里開始:首先將μC/OS目錄下的舊文件復制到μC/OS-II 的相應目錄下,并改用新的文件名,這比重新建立一些新文件要容易許多。表10.2給出來幾個與移植有關的新舊文件名命名法的例子。
表 L10.2對不同微處理器從μC/OS到μC/OS-II,要重新命名的文件.
|
\SOFTWARE\uCOS\I80251 | \SOFTWARE\uCOS-II\I80251 |
I80251.H | OS_CPU.H |
I80251.C | OS_CPU_C.C |
\SOFTWARE\uCOS\M680x0 | \SOFTWARE\uCOS-II\M680x0 |
M680x0.H | OS_CPU.H |
M680x0.C | OS_CPU_C.C |
\SOFTWARE\uCOS\M68HC11 | \SOFTWARE\uCOS-II\M68HC11 |
M68HC11.H | OS_CPU.H |
M68HC11.C | OS_CPU_C.C |
\SOFTWARE\uCOS\Z80 | \SOFTWARE\uCOS-II\Z80 |
Z80.H | OS_CPU.H |
Z80_A.ASM | OS_CPU_A.ASM |
Z80_C.C | OS_CPU_C.C |
10.
INCLUDES.H
用戶應用程序中的INCLUDES.H 文件要修改。以80x86 實模式,在大模式下編譯為例,用戶要做如下修改:
? 變目錄名 μC/OS 為 μC/OS-II
? 變文件名 IX86L.H 為 OS_CPU.H
? 變文件名UCOS.H 為 uCOS_II.H
新舊文件如程序清單 L10.1和 L10.2所示
10.
OS_CPU.H
OS_CPU.H 文件中有與微處理器類型及相應硬件有關的常數定義、宏定義和類型定義。
10.0.
與編譯有關的數據類型s
為了實現 μC/OS-II,用戶應定義6個新的數據類型:INT8U、INT8S、INT16U、NT16S、INT32U、和INT32S。這些數據類型有分別表示有符號和無符號8位、16位、32位整數。在μC/OS中相應的數據類型分別定義為:UBYTE、BYTE、UWORD、WORD、ULONG和LONG。用戶所要做的僅僅是復制μC/OS中數類型并修改原來的UBYTE為INT8U,將BYTE為INT8S,將UWORD修改為INT16U等等,如程序清單 L10.3所示。
程序清單 L10.1 μC/OS 中的 INCLUDES.H. |
/* |
*************************************************************** |
* INCLUDES.H |
*************************************************************** |
*/ |
|
#include <STDIO.H> |
#include <STRING.H> |
#include <CTYPE.H> |
#include <STDLIB.H> |
#include <CONIO.H> |
#include <DOS.H> |
|
#include "\SOFTWARE\UCOS\IX86L\IX86L.H" |
#include "OS_CFG.H" |
#include "\SOFTWARE\UCOS\SOURCE\UCOS.H" |
程序清單 L10.2 μC/OS-II 中的 INCLUDES.H. |
/* |
*************************************************************** |
* INCLUDES.H |
*************************************************************** |
*/ |
|
#include <STDIO.H> |
#include <STRING.H> |
#include <CTYPE.H> |
#include <STDLIB.H> |
#include <CONIO.H> |
#include <DOS.H> |
|
#include "\SOFTWARE\uCOS-II\IX86L\OS_CPU.H" |
#include "OS_CFG.H" |
#include "\SOFTWARE\uCOS-II\SOURCE\uCOS_II.H" |
程序清單 L10.3μC/OS到μC/OS-II 數據類型的修改. |
/* uC/OS data types: */ |
typedef unsigned char UBYTE; /* Unsigned 8 bit quantity */ |
typedef signed char BYTE; /* Signed 8 bit quantity */ |
typedef unsigned int UWORD; /* Unsigned 16 bit quantity */ |
typedef signed int WORD; /* Signed 16 bit quantity */ |
typedef unsigned long ULONG; /* Unsigned 32 bit quantity */ |
typedef signed long LONG; /* Signed 32 bit quantity */ |
|
/* uC/OS-II data types */ |
typedef unsigned char INT8U; /* Unsigned 8 bit quantity */ |
typedef signed char INT8S; /* Signed 8 bit quantity */ |
typedef unsigned int INT16U; /* Unsigned 16 bit quantity */ |
typedef signed int INT16S; /* Signed 16 bit quantity */ |
typedef unsigned long INT32U; /* Unsigned 32 bit quantity */ |
typedef signed long INT32S; /* Signed 32 bit quantity */ |
在μC/OS中,任務棧定義為類型OS_STK_TYPE,而在μC/OS-II中任務棧要定義類型OS_STK.,為了免于修改所有應用程序的文件,可以在OS_CPU.H中建立兩個數據類型,以Intel 80x86 為例,如程序清單 L10.4所示。
程序清單 L10.4 μC/OS 和 μC/OS-II任務棧的數據類型 |
#define OS_STK_TYPE UWORD /* 在 uC/OS 中 */ |
#define OS_STK INT16U /* 在 uC/OS-II 中 */ |
10.0.
OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()
μC/OS-II和μC/OS一樣,分別定義兩個宏來開中斷和關中斷:OS_ENTER_CRITICAL()和 OS_EXIT_CRITICAL()。在μC/OS向μC/OS-II升級的時候,用戶不必動這兩個宏。.
10.0.
OS_STK_GROWTH
大多數微處理器和微處理器的棧都是由存儲器高地址向低地址操作的,然而有些微處理器的工作方式正好相反。μC/OS-II設計成通過定義一個常數OS_STK_GROWTH來處理不同微處理器棧操作的取向:
對棧操作由低地址向高地址增長,設OS_STK_GROWTH 為 0
對棧操作由高地址向低地址遞減,設OS_STK_GROWTH 為 1
有些新的常數定義(#define constants )在μC/OS中是沒有的,故要加到OS_CPU.H中去。
10.0.
OS_TASK_SW()
OS_TASK_SW()是一個宏,從μC/OS升級到μC/OS-II時,這個宏不需要改動。當μC/OS-II從低優先級的任務向高優先級的任務切換時要用到這個宏,OS_TASK_SW()的調用總是出現在任務級代碼中。
10.0.
OS_FAR
因為Intel 80x86的結構特點,在μC/OS中使用過OS_FAR 。這個定義語句(#define )在μC/OS-II 中去掉了,因為這條定義使移植變得不方便。結果是對于Intel 80x86,如果用戶定義在大模式下編譯時,所有存儲器屬性都將為遠程(FAR).
在μC/OS-II中,任務返回值類型定義如程序清單L10.5所示。用戶可以重新編輯所有OS_FAR的文件,或者在μC/OS-II中將OS_FAR定義為空,去掉OS_FAR,以實現向μC/OS-II的升級。
程序清單 L10.5 在 μC/OS 中任務函數的定義 |
void OS_FAR task (void *pdata) |
{ |
pdata = pdata; |
while (1) { |
. |
. |
} |
} |
10.
OS_CPU_A.ASM
移植μC/OS 和μC/OS-II 需要用戶用匯編語言寫4個相當簡單的函數。
OSStartHighRdy()
OSCtxSw()
OSIntCtxSw()
OSTickISR()
10.0.
OSStartHighRdy()
在μC/OS-II中,OSStartHighRdy ()要調用OSSTaskSwHook()。OSTaskSwHook()這個函數在μC/OS中沒有。用戶將最高優先級任務的棧指針裝入CPU之前要先調用OSTaskSwHook()。還有, OSStartHighRdy要在調用OSTaskSwHook()之后立即將OSRunning設為1。程序清單L10.6 給出OSStartHighRdy()的示意代碼。.μC/OS只有其中最后三步。
程序清單 L10.6 OSStartHighRdy()的示意代碼 |
OSStartHighRdy: |
Call OSTaskSwHook(); 調用OSTaskSwHook(); |
Set OSRunning to 1; 置 OSRunning 為 1; |
Load the processor stack pointer with OSTCBHighRdy->OSTCBStkPtr; 將 OSTCBHighRdy->OSTCBStkPtr 裝入處理器的棧指針; |
POP all the processor registers from the stack; 從棧中彈出所有寄存器的值; |
Execute a Return from Interrupt instruction; 執行中斷返回指令; |
|
10.0.
OSCtxSw()
在μC/OS-II中,任務切換要增作兩件事,首先,將當前任務棧指針保存到當前任務控制塊TCB后要立即調用OSTaskSwHook()。其次,在裝載新任務的棧指針之前必須將OSPrioCur設為OSPrioHighRdy 。OSCtxSw()的示意代碼如程序清單L10.7所示。μC/OS-II加上了步驟 L10.7(1)和(2)。
程序清單 L10.7 OSCtxSw()的示意代碼 |
OSCtxSw: |
PUSH processor registers onto the current task’s stack; 所有處理器寄存器的值推入當前任務棧; |
Save the stack pointer at OSTCBCur->OSTCBStkPtr; |
Call OSTaskSwHook(); 1) |
OSTCBCur = OSTCBHighRdy; |
OSPrioCur = OSPrioHighRdy; (2) |
Load the processor stack pointer with OSTCBHighRdy->OSTCBStkPtr; 將 OSTCBHighRdy->OSTCBStkPtr 裝入處理器的棧指針; |
POP all the processor registers from the stack; 從棧中彈出所有寄存器的值; |
Execute a Return from Interrupt instruction; |
10.0.
OSIntCtxSw()
如同上述函數一樣,在μC/OS-II.中,OSCtxSw()也增加了兩件事。首先,將當前任務的棧指針保存到當前任務的控制塊TCB后要立即調用OSTaskSwHook()。其次,在裝載新任務的棧指針之前必須將OSPrioCur 設為OSPrioHighRdy。程序清單L10.8給出OSIntCtxSw()的示意代碼。μC/OS-II.中增加了L10.8(1)和 (2)。
程序清單 L10.8 OSIntCtxSw()的示意代碼 |
OSIntCtxSw(): |
Adjust the stack pointer to remove call to OSIntExit(), locals in OSIntExit() |
and the call to OSIntCtxSw(); 調整由于調用上述子程序引起的棧指針值的變化; |
Save the stack pointer at OSTCBCur->OSTCBStkPtr; 保存棧指針到OSTCBCur->OSTCBStkPtr; |
Call OSTaskSwHook(); 調用OSTaskSwHook();(1) |
OSTCBCur = OSTCBHighRdy; |
OSPrioCur = OSPrioHighRdy; (2) |
Load the processor stack pointer with OSTCBHighRdy->OSTCBStkPtr; 將 OSTCBHighRdy->OSTCBStkPtr 裝入處理器的棧指針; |
POP all the processor registers from the stack; 從棧中彈出所有寄存器的值; |
Execute a Return from Interrupt instruction; 執行中斷返回指令; |
10.0.
OSTickISR()
在μC/OS-II和μC/OS 中,這個函數的代碼是一樣,無須改變。
10.
OS_CPU_C.C
移植 μC/OS-II 需要用C語言寫6個非常簡單的函數:
OSTaskStkInit()
OSTaskCreateHook()
OSTaskDelHook()
OSTaskSwHook()
OSTaskStatHook()
OSTimeTickHook()
其中只有一個函數OSTaskStkInit()是必不可少的。其它5個只需定義,而不包括任何代碼。
10.0.
OSTaskStkInit()
在μC/OS中,OSTaskCreate()被認為是與使用的微處理器類型有關的函數。實際上這個函數中只有一部分內容是依賴于微處理器類型的。在μC/OS-II中,與使用的微處理器類型有關的那一部分已經從函數OSTaskCreate() 中抽出來了,放在一個叫作OSTaskStkInit()的函數中。
OSTaskStkInit()只負責設定任務的棧,使之看起來好像中斷剛剛發生過,所有的CPU寄存器都被推入堆棧。作為提供給用戶的例子,程序清單L10.9給出Intel 80x86實模式,在大模式下編譯的 μC/OS的OSTaskCreate()函數的代碼。程序清單L10.10是同類微微處理器的μC/OS-II的OSTaskStkInit()函數的代碼。比較這兩段代碼,可以看出:從 [L10.9(1)] OS_EXIT_CRIITICAL()到[L10.9(2)]調用OSTaskStkInit()都抽出來并移到了OSTaskStkInit()中。
程序清單L10.9 μC/OS 中的 OSTaskCreate() |
UBYTE OSTaskCreate(void (*task)(void *pd), void *pdata, void *pstk, UBYTE p) |
{ |
UWORD OS_FAR *stk; |
UBYTE err; |
|
|
OS_ENTER_CRITICAL(); |
if (OSTCBPrioTbl[p] == (OS_TCB *)0) { |
OSTCBPrioTbl[p] = (OS_TCB *)1; |
OS_EXIT_CRITICAL(); (1) |
stk = (UWORD OS_FAR *)pstk; |
*--stk = (UWORD)FP_OFF(pdata); |
*--stk = (UWORD)FP_SEG(task); |
*--stk = (UWORD)FP_OFF(task); |
*--stk = (UWORD)0x0202; |
*--stk = (UWORD)FP_SEG(task); |
*--stk = (UWORD)FP_OFF(task); |
*--stk = (UWORD)0x0000; |
*--stk = (UWORD)0x0000; |
*--stk = (UWORD)0x0000; |
*--stk = (UWORD)0x0000; |
*--stk = (UWORD)0x0000; |
*--stk = (UWORD)0x0000; |
*--stk = (UWORD)0x0000; |
*--stk = (UWORD)0x0000; |
*--stk = (UWORD)0x0000; |
*--stk = _DS; |
err = OSTCBInit(p, (void far *)stk); (2) |
if (err == OS_NO_ERR) { |
if (OSRunning) { |
OSSched(); |
} |
} else { |
OSTCBPrioTbl[p] = (OS_TCB *)0; |
} |
return (err); |
} else { |
OS_EXIT_CRITICAL(); |
return (OS_PRIO_EXIST); |
} |
} |
程序清單 L10.10 μC/OS-II中的OSTaskStkInit() |
void *OSTaskStkInit (void (*task)(void *pd), void *pdata, void *ptos, INT16U opt) |
{ |
INT16U *stk; |
|
|
opt = opt; |
stk = (INT16U *)ptos; |
*stk-- = (INT16U)FP_SEG(pdata); |
*stk-- = (INT16U)FP_OFF(pdata); |
*stk-- = (INT16U)FP_SEG(task); |
*stk-- = (INT16U)FP_OFF(task); |
*stk-- = (INT16U)0x0202; |
*stk-- = (INT16U)FP_SEG(task); |
*stk-- = (INT16U)FP_OFF(task); |
*stk-- = (INT16U)0xAAAA; |
*stk-- = (INT16U)0xCCCC; |
*stk-- = (INT16U)0xDDDD; |
*stk-- = (INT16U)0xBBBB; |
*stk-- = (INT16U)0x0000; |
*stk-- = (INT16U)0x1111; |
*stk-- = (INT16U)0x2222; |
*stk-- = (INT16U)0x3333; |
*stk-- = (INT16U)0x4444; |
*stk = _DS; |
return ((void *)stk); |
} |
10.0.
OSTaskCreateHook()
OSTaskCreateHook()在μC/OS中沒有,如程序清單L10.11所示,在由.μC/OS 向μC/OS-II升級時,定義一個空函數就可以了。注意其中的賦值語句,如果不把Ptcb賦給Ptcb,有些編譯器會產生一個警告錯誤,說定義的Ptcb變量沒有用到。
程序清單10.11 μC/OS-II 中的OSTaskCreateHook() |
#if OS_CPU_HOOKS_EN |
OSTaskCreateHook(OS_TCB *ptcb) |
{ |
ptcb = ptcb; |
} |
#endif |
用戶還應該使用條件編譯管理指令來處理這個函數。只有在OS_CFG.H 文件中將OS_CPU_HOOKS _EN設為1時,OSTaskCreateHook()的代碼才會生成。這樣做的好處是允許用戶移植時可在不同文件中定義鉤子函數。
10.0.
OSTaskDelHook()
OSTaskDelHook() 這個函數在μC/OS中沒有,如程序清單10.12所示,從μC/OS 到μC/OS-II,只要簡單地定義一個空函數就可以了。注意,如果不用賦值語句將ptcb賦值為ptcb,有些編譯程序可能會產生一些警告信息,指出定義的ptcb變量沒有用到。
程序清單 L10.12 μC/OS-II中的OSTaskDelHook(). |
#if OS_CPU_HOOKS_EN |
OSTaskDelHook(OS_TCB *ptcb) |
{ |
ptcb = ptcb; |
} |
#endif |
也還是要用條件編譯管理指令來處理這個函數。只有把OS_CFG.H. 文件中的OS_CPU_HOOKS_EN 設為1,OSTaskDelHook()的代碼才能生成。這樣做的好處是允許用戶移植時在不同的文件中定義鉤子函數。
10.0.
OSTaskSwHook()
OSTaskSwHook() 在μC/OS 中也不存在。從μC/OS向μC/OS-II升級時,只要簡單地定義一個空函數就可以了,如程序清單L10.13所示。
程序清單 L10.13 μC/OS-II中的OSTaskSwHook()函數 |
#if OS_CPU_HOOKS_EN |
OSTaskSwHook(void) |
{ |
} |
#endif |
也還是要用編譯管理指令來處理這個函數。只有把OS_CFG.H 文件中的OS_CPU_HOOKS_EN設為1,OSTaskSwHook() 的代碼才能生成。.
10.0.
OSTaskStatHook()
OSTaskStatHook()在μC/OS中不存在,從μC/OS向μC/OS-II升級時,只要簡單地定義一個空函數就可以了,如程序清單L10.14所示。
也還是要用編譯管理指令來處理這個函數。只有把OS_CFG.H 文件中的OS_CPU_HOOKS_EN設為1,OSTaskSwHook() 的代碼才能生成。
程序清單 L10.14 μC/OS-II中的OSTaskStatHook()函數 |
#if OS_CPU_HOOKS_EN |
OSTaskStatHook(void) |
{ |
} |
#endif |
10.0.
OSTimeTickHook()
OSTimeTickHook()在μC/OS中不存在,從μC/OS向μC/OS-II升級時,只要簡單地定義一個空函數就可以了,如程序清單L10.15所示。
也還是要用編譯管理指令來處理這個函數。只有把OS_CFG.H 文件中的OS_CPU_HOOKS_EN設為1,OSTimeTickHook()的代碼才能生成。
.
程序清單 L10.15 μC/OS-II中的OSTimeTickHook() |
#if OS_CPU_HOOKS_EN |
OSTimeTickHook(void) |
{ |
} |
#endif |
10.
總結
表T10.3總結了從μC/OS向μC/OS-II.升級需要改變得地方。其中processor_name.?是μC/OS中移植范例程序的文件名。
表 T10.3 升級 μC/OS到 μC/OS-I要修改的地方
|
μC/OS | μC/OS-II |
Processor_name.H | OS_CPU.H |
數據類型: UBYTE BYTE UWORD WORD ULONG LONG | 數據類型: INT8U INT8S INT16U INT16S INT32U INT32S |
|
|
OS_STK_TYPE | OS_STK |
OS_ENTER_CRITICAL() | 不變 |
OS_EXIT_CRITICAL() | 不變 |
— | 增加了 OS_STK_GROWTH |
OS_TASK_SW() | 不變 |
OS_FAR | 定義OS_FAR 為空,或刪除所有的 OS_FAR |
Processor_name.ASM | OS_CPU_A.ASM |
OSStartHighRdy() | 增加了調用 OSTaskSwHook(); 置 OSRunning 為 1 (8 bits) |
OSCtxSw() | 增加了調用 OSTaskSwHook(); 拷貝OSPrioHighRdy 到 OSPrioCur (8 bits) |
OSIntCtxSw() | 增加了調用OSTaskSwHook(); 拷貝 OSPrioHighRdy 到 OSPrioCur (8 bits) |
OSTickISR() | 不變 |
Processor_name.C | OS_CPU_C.C |
OSTaskCreate() | 抽出棧初始部分,放在函數 OSTaskStkInit() 中 |
— | 增加了空函數 OSTaskCreateHook() |
— | 增加了空函數 OSTaskDelHook() |
— | 增加了空函數 OSTaskSwHook() |
— | 增加了空函數 OSTaskStatHook() |
— | 增加了空函數 OSTimeTickHook() |