移植好后,应该多看内核源码了!我比较喜欢看里面的实现过程,有些算法很巧妙,“知其然且知其所以然”,不理解原理不能谓之掌握。
任务
首先看她定义的数据结构吧!
任务控制块tcb
在UCOS-II.H中定义了tcb结构:typedef struct os_tcb { /*指向任务堆栈栈顶的指针*/ OS_STK *OSTCBStkPtr; #if OS_TASK_CREATE_EXT_EN > 0 /*指向任务控制块扩展的指针*/ void *OSTCBExtPtr; /*指向任务堆栈栈底的指针*/ OS_STK *OSTCBStkBottom; /*任务堆栈的长度*/ INT32U OSTCBStkSize; /*创建任务时的选择项*/ INT16U OSTCBOpt; /*目前未被使用*/ INT16U OSTCBId; #endif /*指向后一个任务控制块的指针*/ struct os_tcb *OSTCBNext; /*指向前一个任务控制块的指针*/ struct os_tcb *OSTCBPrev; #if OS_EVENT_EN /*指向事件控制块的指针*/ OS_EVENT *OSTCBEventPtr; #endif #if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0) /*指向传递给任务消息的指针*/ void *OSTCBMsg; /* Message received from OSMboxPost() or OSQPost() */ #endif #if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0) #if OS_TASK_DEL_EN > 0 OS_FLAG_NODE *OSTCBFlagNode; /* Pointer to event flag node */ #endif OS_FLAGS OSTCBFlagsRdy; /* Event flags that made task ready to run */ #endif /*任务等待的时限*/ INT16U OSTCBDly; /*任务的当前状态标志*/ INT8U OSTCBStat; BOOLEAN OSTCBPendTO; /* Flag indicating PEND timed out (TRUE == timed out) */ /*任务的优先级别*/ INT8U OSTCBPrio; /* Task priority (0 == highest, 63 == lowest) */ /*用于快速访问就绪表的数据*/ INT8U OSTCBX; /* Bit position in group corresponding to task priority (0..7) */ INT8U OSTCBY; /* Index into ready table corresponding to task priority */ INT8U OSTCBBitX; /* Bit mask to access bit position in ready table */ INT8U OSTCBBitY; /* Bit mask to access bit position in ready group */ #if OS_TASK_DEL_EN > 0 /*请求删除任务时用到的标志*/ INT8U OSTCBDelReq; /* Indicates whether a task needs to delete itself */ #endif #if OS_TASK_PROFILE_EN > 0 INT32U OSTCBCtxSwCtr; /* Number of time the task was switched in */ INT32U OSTCBCyclesTot; /* Total number of clock cycles the task has been running */ INT32U OSTCBCyclesStart; /* Snapshot of cycle counter at start of task resumption */ OS_STK *OSTCBStkBase; /* Pointer to the beginning of the task stack */ INT32U OSTCBStkUsed; /* Number of bytes used from the stack */ #endif #if OS_TASK_NAME_SIZE > 1 char OSTCBTaskName[OS_TASK_NAME_SIZE]; #endif } OS_TCB;
UCOS用两条链表来管理任务控制块
1. 空任务块链表:
是UCOS的全局数据结构,是用来管理TCB的申请和释放,在OSInit()建立并初始化
OSInit() { ……… OS_InitTCBList(); //TCB链表初始化 ……… }
OS_InitTCBList():
TCB创建步骤:建立全局变量OSTCBTb1[]
#define OS_EXT extern OS_EXT OS_TCB OSTCBTbl[OS_MAX_TASKS + OS_N_SYS_TASKS]; OS_EXT OS_TCB *OSTCBFreeList; 然后在OS_InitTCBList()链表初始化中:
OS_TCB *ptcb1; /*创建两个tcb指针*/ OS_TCB *ptcb2; //指向全局变量 ptcb1 = &OSTCBTbl[0]; ptcb2 = &OSTCBTbl[1]; //利用OSTCBNext建立链表: for (i = 0; i < (OS_MAX_TASKS + OS_N_SYS_TASKS - 1); i++) { ptcb1->OSTCBNext = ptcb2; ptcb1++; ptcb2++; } OSTCBList = (OS_TCB *)0; OSTCBFreeList = &OSTCBTbl[0]; //指向链头
2.任务块链表
在OSTaskCreate(...)创建任务时建立
OSTaskCreate(void (*task)(void *pd), void *p_arg, OS_STK *ptos, INT8U prio) { ……… err = OS_TCBInit(prio, psp, (OS_STK *)0, 0, 0, (void *)0, 0); } INT8U OS_TCBInit (INT8U prio, OS_STK *ptos, OS_STK *pbos, INT16U id, INT32U stk_size, void *pext, INT16U opt) { ………… ptcb = OSTCBFreeList; /*获取空任务链表头*/ if (ptcb != (OS_TCB *)0) { OSTCBFreeList = ptcb->OSTCBNext; /*空任务链表更新*/ OS_EXIT_CRITICAL(); ptcb->OSTCBStkPtr = ptos; /* 获取栈*/ ptcb->OSTCBPrio = prio; /* 优先级*/ ptcb->OSTCBStat = OS_STAT_RDY; /* 就绪状态*/ ptcb->OSTCBPendTO = FALSE; /* Clear the Pend timeout flag */ ptcb->OSTCBDly = 0; /* Task is not delayed */ …….. OS_ENTER_CRITICAL(); OSTCBPrioTbl[prio] = ptcb; ptcb->OSTCBNext = OSTCBList; /* Link into TCB chain */ ptcb->OSTCBPrev = (OS_TCB *)0; if (OSTCBList != (OS_TCB *)0) { OSTCBList->OSTCBPrev = ptcb; } OSTCBList = ptcb; OSRdyGrp |= ptcb->OSTCBBitY; /* Make task ready to run */ OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; OSTaskCtr++; /* Increment the #tasks counter */ OS_EXIT_CRITICAL(); return (OS_NO_ERR); }
3.任务堆栈
注意和处理器堆栈不同一个概念
INT8U OSTaskCreate (void (*task)(void *pd), void *p_arg, OS_STK *ptos, INT8U prio) { …….. psp = (OS_STK *)OSTaskStkInit(task, p_arg, ptos, 0); /* Initialize the task's stack*/ }
堆栈类型
typedef unsigned int OS_STK;
创建任务时:
进入OSTaskStkInit函数:
OS_STK *OSTaskStkInit(void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt) { unsigned int *stk; opt = opt; /* 未用到的参数*/ stk = (unsigned int *)ptos; /* 装载任务堆栈指针*/ /* 建立上下文*/ *--stk = (unsigned int) task; /* pc */ *--stk = (unsigned int) 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 */ *--stk = (SVC32MODE|0x0); /* cpsr IRQ, FIQ disable */ return ((void *)stk); }
注意这个是移植时的底层代码!!是将处理器中的寄存器存到任务堆栈,在SVC模式下
4.创建空闲任务和统计任务
这两个任务在UCOS2里是默认创建,其中一个在OSInit中创建,另一个用户可以在创建任务中调用OSStatInit()创建
空闲任务
OSInit中调用OS_InitTaskIdle();在OS_InitTaskIdle函数中创建了空闲任务:
(void)OSTaskCreate(OS_TaskIdle,
(void *)0,
&OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1],
OS_IDLE_PRIO); //优先级别最低
然后看OS_TaskStat这个空任务做了什么:
void OS_TaskIdle (void *parg)
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
cpu_sr = 0;
#endif
parg = parg;
for (;;) {
OS_ENTER_CRITICAL();
OSIdleCtr++; //计数
OS_EXIT_CRITICAL();
OSTaskIdleHook();
}
}
统计任务:
OSInit中调用OS_InitTaskStat();创建了统计任务,了解处理器利用率,优先级比空闲高一级。
(void)OSTaskCreate(OS_TaskStat,
(void *)0,
&OSTaskStatStk[OS_TASK_STAT_STK_SIZE-1], OS_STAT_PRIO);
然后看OS_TaskStat任务,每秒计算一次处理器在单位时间内被使用的时间,公式
OSIdleCtr
OSCPUUsage = 100 * (1 - ----------------------------) (units are in %)
OSIdleCtrMax
通过统计空闲任务来知道目前处理器的占用率。