根据之前用到zigbee的协议栈的时候的学习的经验,对于操作系统(当然zigbee协议栈不是一个操作系统,但是有点类似)的掌握,先应该从整体上有个大的框架上的了解.至少要知道整个系统是怎么运行的,这个时候不需要了解的很深,只需要大概的了解一下流程即可。
那么整个系统的运行当然是从main函数开始执行的,先看看main函数中的语句:
void main (void)
{
INT8U err;
PC_DispClrScr(DISP_FGND_WHITE + DISP_BGND_BLACK); /* Clear the screen */
OSInit(); /* Initialize uC/OS-II */
PC_DOSSaveReturn(); /* Save environment to return to DOS */
PC_VectSet(uCOS, OSCtxSw); /* Install uC/OS-II's context switch vector */
RandomSem = OSSemCreate(1); /* Random number semaphore */
OSTaskCreate(TaskStart, (void *)0, &TaskStartStk[TASK_STK_SIZE - 1], 0);
OSTaskNameSet(0,"Task0",&err);
OSStart(); /* Start multitasking */
}
我们暂时可以讲PC_DispClrScr()函数屏蔽掉,暂时不管他,OSInit()函数就开始进入操作系统的部分了,顾名思义,这种命名方式提示了我们这是对操作系统的初始化,后面的注释部分也给了相应的说明.函数暂时粘贴如下:
void OSInit (void)
{
#if OS_VERSION >= 204
OSInitHookBegin(); /* Call port specific initialization code */
#endif
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_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)
OS_FlagInit(); /* Initialize the event flag structures */
#endif
#if (OS_MEM_EN > 0) && (OS_MAX_MEM_PART > 0)
OS_MemInit(); /* Initialize the memory manager */
#endif
#if (OS_Q_EN > 0) && (OS_MAX_QS > 0)
OS_QInit(); /* Initialize the message queue structures */
#endif
OS_InitTaskIdle(); /* Create the Idle Task */
#if OS_TASK_STAT_EN > 0
OS_InitTaskStat(); /* Create the Statistic Task */
#endif
#if OS_VERSION >= 204
OSInitHookEnd(); /* Call port specific init. code */
#endif
}
ucosII发展到目前为止已经有很多的版本,这个初始化函数我是从V2.82版本中复制过来的,因为相比较于比较经典的V2.52的版本,V2.82中的这个版本看起来更让人感觉到清晰明了,从函数中可以看到就是依次对操作系统中的各个部分进行初始化,运用预处理指令的好处是可以实现对操作系统的灵活配置,比如当我们不需要进行内存管理部分时,就可以将OS_MEM_EN变量设置为0,此时由上面的程序中可以看到,在编译器就行编译时就不会对OS_MemInit()函数进行编译,从而在一些资源有限的处理器上减少资源的消耗,能比较方便的实现灵活的移植.对相关功能的配置是在OS_CFG.H头文件中进行的。使用者只要更改其中变量的值就可以实现系统的需求.
PC_DOSSaveReturn()函数由后面的注释中可以看到是返回到DOS界面,可以不看(包括前面一个PC开头的函数在内,这些函数是因为ucosII的开发是在PC机上完成的,作者给出的例子也是基于PC的,但是不妨碍移植到你所用的处理器上,当然对处理器有一些限制性要求).RandomSem = OSSemCreate(1); 实现的功能是产生一个信号量,该信号量的功能在今后你将会明白,暂时不管,因为我们主要看的是宏观上的了解.
OSTaskCreate(TaskStart, (void *)0, &TaskStartStk[TASK_STK_SIZE - 1], 0);
OSTaskNameSet(0,"Task0",&err);
由函数的名称中就可以看到上面的第一个函数是产生一个任务,该任务是在main函数中最后一个函数OSStart()之前必须要进行的,因为ucosii要求在系统start之前必须至少有一个任务。OSTaskNameSet(0,"Task0",&err);函数是对任务命名,暂时不问。
OSStart(); 该函数是main函数中执行的最后一个函数,该函数的运行就标志了整个操作系统运行起来了.
不过貌似看起来很简单,不就是对其进行初始化,在creat task,再start os 吗?是的。
那看看在OSStart中操作系统做了什么呢?
void OSStart (void)
{
INT8U y;
INT8U x;
if (OSRunning == FALSE) {
y = OSUnMapTbl[OSRdyGrp]; /* Find highest priority's task priority number */
x = OSUnMapTbl[OSRdyTbl[y]];
OSPrioHighRdy = (INT8U)((y << 3) + x);
OSPrioCur = OSPrioHighRdy;
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; /* Point to highest priority task ready to run */
OSTCBCur = OSTCBHighRdy;
OSStartHighRdy(); /* Execute target specific code to start task */
}
}
由于ucosII总是根据任务的优先级来选择该执行那个任务的,任务的prio数值越小,则优先级越高,所以最高的优先级是0,最低的优先级根据ucosII的版本不同而不同,V2.52中是63,V2.8中是255,但是无论是哪一个版本,最低的优先级都会被空闲任务占据(MCU总是要有任务执行的嘛!),很容易理解,空闲任务就是不执行任务的任务(当然用户可以在里面添加代码,比如让MCU进入低功耗模式),即空闲任务的prio等于系统允许的最大任务数,此时还有一个任务,就是统计任务,该任务在OS_CFG.h中如果配置为允许的话,就会产生一个优先级比空闲任务第一个的统计任务,该任务功能就是统计CPU的使用情况。在操作系统第一次start之后,就要对现在已经拥有的任务中找到优先级最高的任务就行执行,其实我们已经看到,在OSStart执行之前我们已经新建了一个任务,
OSTaskCreate(TaskStart, (void *)0, &TaskStartStk[TASK_STK_SIZE - 1], 0); 其中第一个参数就是新建的任务,其实是一个函数的首地址,最后一个参数就是任务的优先级,可以看到TaskStart的优先级prio = 0,即最高优先级。
而在OSStart()函数中要完成的功能就是找到这个任务,即当前优先级最高的任务。具体有下列的语句实现:
y = OSUnMapTbl[OSRdyGrp]; /* Find highest priority's task priority number */
x = OSUnMapTbl[OSRdyTbl[y]];
OSPrioHighRdy = (INT8U)((y << 3) + x);
OSPrioCur = OSPrioHighRdy;
在后面会陆续讲到这些语句。
至此,main函数就执行完了。系统开始运行了,可能大家仍然会迷茫究竟是怎么运行的,但是毕竟一嘴吃不了一个胖子,咱们得一步一步好好的理解,从而不会在后面产生混淆的地方,到时候就不好纠正过来了。
到目前为止,main中还有一个函数没有讲,为了保证完整性,
void TaskStart (void *pdata)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
char s[100];
INT16S key;
(void)s; //prevent the warning
pdata = pdata; /* Prevent compiler warning */
TaskStartDispInit(); /* Initialize the display */
OS_ENTER_CRITICAL();
PC_VectSet(0x08, OSTickISR); /* Install uC/OS-II's clock tick ISR */
PC_SetTickRate(OS_TICKS_PER_SEC); /* Reprogram tick rate */
OS_EXIT_CRITICAL();
OSStatInit(); /* Initialize uC/OS-II's statistics */
OSView_Init();
TaskStartCreateTasks(); /* Create all the application tasks */
for (;;) {
TaskStartDisp(); /* Update the display */
if (PC_GetKey(&key) == OS_TRUE) { /* See if key has been pressed */
if (key == 0x1B) { /* Yes, see if it's the ESCAPE key */
PC_DOSReturn(); /* Return to DOS */
}
}
OSCtxSwCtr = 0; /* Clear context switch counter */
OSTimeDlyHMSM(0, 0, 1, 0); /* Wait one second */
}
}
这个函数是用户自己写的,作者给出的是一个例子,主要实现的功能就是中间的一个函数
TaskStartCreateTasks(); 该函数就是生成任务,具体代码不在贴出,因为跟操作系统无关,不然就偏离了主题。
敬请期待下一节