經典教程:嵌入式實時操作系統uc/os-ii Jean J.Labrosse著 邵貝貝等譯
1. 從主函數開始
int main (void)
{
init();//一些硬件的初始化
InstallInterruptHandler((unsigned int) OSTickISR, (unsigned int) 0x18);//Timer 1 Interrupt Level 8 指定定時器中斷中斷號
InstallInterruptHandler((unsigned int) OSCtxSw, (unsigned int) 0x80);//Context Switch 指定上下文切換陷阱
InitialiseSystemTimer(ACTIVE_TICK_TIMER);
OSInit(); /* Initialize uC/OS-II*/
OSTaskCreate(FirstTestTask, (void *)0, &FirstTestTaskStk[TEST_STK_SIZE - 1], APP_TASK_ONE_PRIO);//創造任務1,給出任務1的地址、堆棧大小和優先級
OSTaskCreate(SecondTestTask, (void *)0, &SecondTestTaskStk[TEST_STK_SIZE - 1], APP_TASK_TWO_PRIO);
OSStart(); /* Start multitasking */
return 0;
}
其中重要的有三個函數OSInit() OSTskCreate() OSStart():
1.1.OSInit()
void OSInit (void)
{
OSInitHookBegin(); /* Call port specific initialization code */
OS_InitMisc(); /* Initialize miscellaneous variables */
OS_InitRdyList(); /* Initialize the Ready List */
OS_InitTCBList(); /* Initialize the free list of OS_TCBs */
OS_InitEventList(); /* Initialize the free list of OS_EVENTs */
#if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u)
OS_FlagInit(); /* Initialize the event flag structures */
#endif
#if (OS_MEM_EN > 0u) && (OS_MAX_MEM_PART > 0u)
OS_MemInit(); /* Initialize the memory manager */
#endif
#if (OS_Q_EN > 0u) && (OS_MAX_QS > 0u)
OS_QInit(); /* Initialize the message queue structures */
#endif
OS_InitTaskIdle(); /* Create the Idle Task */
#if OS_TASK_STAT_EN > 0u
OS_InitTaskStat(); /* Create the Statistic Task */
#endif
#if OS_TMR_EN > 0u
OSTmr_Init(); /* Initialize the Timer Manager */
#endif
OSInitHookEnd(); /* Call port specific init. code */
#if OS_DEBUG_EN > 0u
OSDebugInit();
#endif
}
其中hook爲鉤子函數,是作者留給開發人員的發揮空間,可以在這裏完成一些開發人員想做的事情,比如初始化自己的硬件。條件編譯“開關”都在ucos_ii.h中,可以在這裏對內核進行“剪裁”,決定保留什麼功能,去除什麼功能。
在OSInit中最重要的是三個部分:就緒表、任務塊、事件塊。
1.1.1就緒表
每個任務有自己的優先級0~63。處於就緒態的任務會在就緒表的相應位置1。
如果一個任務的優先級爲8=00001000,即OSRdyGrp=YYY=001,OSRdyTbl[001]=XXX=000
每個任務的優先級都會分成兩部分分別記錄在OSRdyGrp和OSRdyTbl中。
當就緒表中的值是一定的,則有且僅有唯一一個最小值(優先級最高的任務),如當第7/22/30位置一,則最小值爲7。
在就緒表中找到優先級最高的任務:
y = OSUnMapTbl[OSRdyGrp];
x = OSUnMapTbl[OSRdyTbl[y]];
prio = (y<<3) + x;
OSUnMapTbl爲:
若某時刻OSRdyGrp爲0x68,OSRdyTbl爲0xE4。則
3 = OSUnMapTbl[0x68];
2 = OSUnMapTbl[0xE4];
26 = (3<<3) + 2;//最高優先級任務爲26
1.1.2任務控制塊
以下爲任務控制塊精簡版本:
typedef struct os_tcb {
OS_STK *OSTCBStkPtr; /* Pointer to current top of stack */
struct os_tcb *OSTCBNext; /* Pointer to next TCB in the TCB list */
struct os_tcb *OSTCBPrev; /* Pointer to previous TCB in the TCB list */
INT32U OSTCBDly; /* Nbr ticks to delay task or, timeout waiting for event */
INT8U OSTCBStat; /* Task status */
INT8U OSTCBStatPend; /* Task PEND status */
INT8U OSTCBPrio; /* Task priority (0 == highest) */
INT8U OSTCBX; /* Bit position in group corresponding to task priority */
INT8U OSTCBY; /* Index into ready table corresponding to task priority */
OS_PRIO OSTCBBitX; /* Bit mask to access bit position in ready table */
OS_PRIO OSTCBBitY; /* Bit mask to access bit position in ready group */
} OS_TCB;
一旦任務建立,一個任務控制塊就被賦值。當任務的CPU使用權被剝奪,uc/os-ii用它來保存該任務的狀態。OS_TCB全部駐留在RAM中。
在ucos初始化時,所有任務控制塊OS_TCB都被連接成單向空任務鏈表,然後OSTCBFreeList的值調整爲指向鏈表下一個空的任務控制塊。一旦任務被刪除,任務控制塊就還給空任務鏈表。
任務鏈表初始化:
static void OS_InitTCBList (void)
{
INT8U ix;
INT8U ix_next;
OS_TCB *ptcb1;
OS_TCB *ptcb2;
OS_MemClr((INT8U *)&OSTCBTbl[0], sizeof(OSTCBTbl)); /* Clear all the TCBs */
OS_MemClr((INT8U *)&OSTCBPrioTbl[0], sizeof(OSTCBPrioTbl)); /* Clear the priority table */
for (ix = 0u; ix < (OS_MAX_TASKS + OS_N_SYS_TASKS - 1u); ix++) { /* Init. list of free TCBs */
ix_next = ix + 1u;
ptcb1 = &OSTCBTbl[ix];
ptcb2 = &OSTCBTbl[ix_next];
ptcb1->OSTCBNext = ptcb2;
#if OS_TASK_NAME_EN > 0u
ptcb1->OSTCBTaskName = (INT8U *)(void *)"?"; /* Unknown name */
#endif
}
ptcb1 = &OSTCBTbl[ix];
ptcb1->OSTCBNext = (OS_TCB *)0; /* Last OS_TCB */
#if OS_TASK_NAME_EN > 0u
ptcb1->OSTCBTaskName = (INT8U *)(void *)"?"; /* Unknown name */
#endif
OSTCBList = (OS_TCB *)0; /* TCB lists initializations */
OSTCBFreeList = &OSTCBTbl[0];
}
1.1.3事件控制塊
事件控制塊ECB是用於實現以下功能函數的基本數據結構:信號量管理、互斥型信號量管理、消息郵箱管理以及消息隊列管理。
typedef struct os_event {
INT8U OSEventType; /* Type of event control block (see OS_EVENT_TYPE_xxxx) 事件類型*/
void *OSEventPtr; /* Pointer to message or queue structure 等待任務所在的組*/
INT16U OSEventCnt; /* Semaphore Count (not used if other EVENT type) 計數器(當事件是信號量的時候)*/
OS_PRIO OSEventGrp; /* Group corresponding to tasks waiting for event to occur 指向消息或者消息隊列的指針*/
OS_PRIO OSEventTbl[OS_EVENT_TBL_SIZE]; /* List of tasks waiting for event to occur 等待任務列表*/
#if OS_EVENT_NAME_EN > 0u
INT8U *OSEventName;
#endif
} OS_EVENT;
每個信號量、互斥型信號量、消息郵箱以及消息隊列都應分配到一個事件控制塊ECB。1.2.OSTaskCreate()
通過將任務的地址和其他參數如優先級傳遞到此函數中來建立任務。
1.3.OSStart()
void OSStart(void){
if (OSRunning == OS_FALSE) { OS_SchedNew(); /* Find highest priority's task priority number */ OSPrioCur = OSPrioHighRdy; OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; /* Point to highest priority task ready to run */ OSTCBCur = OSTCBHighRdy; OSRunning = 1; OSStartHighRdy(); /* Execute target specific code to start task */ } }在這裏完成最高優先級查找和開始執行最高優先級任務。
兩個用戶任務的定義:
void FirstTestTask (void *pdata) { pdata = pdata; InitiateTimer(ACTIVE_TICK_TIMER); while(1) { get_status_for_ucos();//用戶任務 OSTimeDly(10);//任務延時10個時鐘節拍 } } void SecondTestTask (void *pdata) { pdata = pdata; InitiateTimer(ACTIVE_TICK_TIMER); while(1) { get_vector_for_ucos(); OSTimeDly(15);//任務延時15個時鐘節拍 } }
2. 系統的基本運作
在任務開始運行之後,每遇到任務的“OSTimeDly”,任務就進入等待狀態。在這個函數中會調用OS_Sched()函數進行任務調度,重新尋找就緒列表中的最高優先級任務,並調用OS_TASK_SW()函數,出發陷阱,開始任務切換。
3.系統移植
與處理器相關的代碼只有OS_CPU.H,OS_CPU_A.ASM,OS_CPU_C.C。
OS_CPU.H中包含臨界區的實現方式。
OS_CPU_A.ASM中實現OSStartHighRdy()使就緒態任務中優先級最高的任務開始運行,OSCtxSw()任務切換,OSIntCtxSw()和OSTickISR()提供節拍定時中斷。
OS_CPU_C.C中實現任務堆棧初始化以及所有鉤子函數(可以不寫)。