经典教程:嵌入式实时操作系统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中实现任务堆栈初始化以及所有钩子函数(可以不写)。