//BY 简单的元清
//部分内容引用了其他博主的文章,对这些博主表示感谢,时间关系就不一一指出了。
//如有转载,请说明,谢谢 /* ********************************************************************************************************* * uC/OS-II * The Real-Time Kernel * CORE FUNCTIONS * * (c) Copyright 1992-1998, Jean J. Labrosse, Plantation, FL * All Rights Reserved * * V2.00 * * File : OS_CORE.C * By : Jean J. Labrosse ********************************************************************************************************* */ #ifndef OS_MASTER_FILE #define OS_GLOBALS #include "software\includes.h" #endif /* ********************************************************************************************************* * LOCAL GLOBAL VARIABLES ********************************************************************************************************* */ static INT8U OSIntExitY; /* Variable used by 'OSIntExit' to prevent using locals */ static OS_STK OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE]; /* Idle task stack */ #if OS_TASK_STAT_EN static OS_STK OSTaskStatStk[OS_TASK_STAT_STK_SIZE]; /* Statistics task stack */ #endif static OS_TCB OSTCBTbl[OS_MAX_TASKS + OS_N_SYS_TASKS]; /* Table of TCBs */ /*$PAGE*/ /* ********************************************************************************************************* * MAPPING TABLE TO MAP BIT POSITION TO BIT MASK * * Note: Index into table is desired bit position, 0..7 * Indexed value corresponds to bit mask ********************************************************************************************************* */ //ucosii 中确定优先级的机制如下: //首先有一个类型为INT8U 的OSRdyGrp,我们把其中的8位叫D0,D1,D2,D3,D4,D5,D6,D7. 例如OSRdyGrp=0x03;说明在D0,D1两组中有任务就绪,那么我们知道了属于那一组,也需要知道在组里面的具体位置,所以我们还有 //一个类型为INT8U 的数组OSRdyTbl[]。其中OSRdyTbl[0]的8位叫a0,a1,a2,a3,a4,a5,a6,a7,OSRdyTbl[1]的8位叫b0,b1,b2,b3,b4,b5,b6,b7.....,如果OSRdyTbl[0]=0x03,说明在D0组的a0,a1两个位置有任务就绪。 //而优先级和OSRdyGrp、OSRdyTbl[]的对应关系为 //X = OSUnMapTbl[OSRdyGrp]; //Y = OSUnMapTbl[OSRdyTbl[X]]; //最高优先级为X*8+Y ( prio = (y << 3) + x;) //prio最大的值为63,也就是0011 1111 ,最高的两位没有用到,其中D6,D5,D4 三位表示所在OSRdyGrp组中的位置,D3,D2,D1 三位表示所在具体组的位置。 //例如prio=30,二进制为0001 1110 ,表示 OSRdyGrp组中D3组为1,OSRdyTbl[3]中d6为1 //举一个例子 //OSRdyGrp = 0x011; //0b00010001 //OSRdyTbl[0] = 0x0a; //0b00001010 //OSRdyTbl[4] = 0x01; //0b00000001 //计算出存在的几个优先级为;0*8+1=1,0*8+3=3,4*8+0=32 //这个表表明是属于那一位 INT8U const OSMapTbl[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; /* ********************************************************************************************************* * PRIORITY RESOLUTION TABLE * * Note: Index into table is bit pattern to resolve highest priority * Indexed value corresponds to highest priority bit position (i.e. 0..7) ********************************************************************************************************* */ //这个表其实就是从00-FF时最低位为1的位置。 //为什么是最低位,因为优先级的值越小优先级越高 //例如 //0u==0x00 ==00000000b 最低位为1的位数为 bit0==0u (其实为空,空的情况默认为0,不影响计算) //1u==0x01 ==00000001b 最低位为1的位数为 bit0==0u //2u==0x02 ==00000010b 最低位为1的位数为 bit1==1u //3u==0x03 ==00000011b 最低位为1的位数为 bit0==0u (有两个为1的位,bit0最小,所以最低位为1的位置是0) //4u==0x04 ==00000100b 最低位为1的位数为 bit2==2u //5u==0x05 ==00000101b 最低位为1的位数为 bit0==0u //6u==0x06 ==00000110b 最低位为1的位数为 bit1==1u( //7u==0x07 ==00000111b 最低位为1的位数为 bit0==0u //8u==0x08 ==00001000b 最低位为1的位数为 bit3==3u(最低位为1的位置是3) //9u==0x09 ==00001001b 最低位为1的位数为 bit0==0u //Au==0x0A ==00001010b 最低位为1的位数为 bit1==1u //Bu==0x0B ==00001011b 最低位为1的位数为 bit0==0u INT8U const OSUnMapTbl[] = { 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 }; //为什么搞这个表,这么复杂,是因为这样做可以加快查找优先级的时间,同时查询的时间是可知的,如果用循环语句,那就蒙B了,你也不知道它查完这个东西要多久,实时操作系统的实时性体现在这里。 /*$PAGE*/ /* ********************************************************************************************************* * MAKE TASK READY TO RUN BASED ON EVENT OCCURING * * Description: This function is called by other uC/OS-II services and is used to ready a task that was * waiting for an event to occur. * * Arguments : pevent is a pointer to the event control block corresponding to the event. * * msg is a pointer to a message. This pointer is used by message oriented services * such as MAILBOXEs and QUEUEs. The pointer is not used when called by other * service functions. * * msk is a mask that is used to clear the status byte of the TCB. For example, * OSSemPost() will pass OS_STAT_SEM, OSMboxPost() will pass OS_STAT_MBOX etc. * * Returns : none * * Note : This function is INTERNAL to uC/OS-II and your application should not call it. ********************************************************************************************************* */ #if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN || OS_SEM_EN /****************************************************************/ //当发生了某个事件,该事件等待任务列表中的最高优先级任务置于就绪态 void OSEventTaskRdy (OS_EVENT *pevent, void *msg, INT8U msk) reentrant { OS_TCB *ptcb; INT8U x; INT8U y; INT8U bitx; INT8U bity; INT8U prio; y = OSUnMapTbl[pevent->OSEventGrp]; /* Find highest prio. task waiting for message */ //查询D6,D5,D4所在的组 //然后找出最低位为1的位置 bity = OSMapTbl[y]; //把对应的位置转化为在组中位置 x = OSUnMapTbl[pevent->OSEventTbl[y]]; // //查询D3,D2,D1所在的位置 ////然后找出最低位为1的位置 bitx = OSMapTbl[x]; //把对应的位置转化为在组中的位置 prio = (INT8U)((y << 3) + x); /* Find priority of task getting the msg */ //根据所在的组和在组中的具体位置推算出对应的优先级 //prio=y*8+x if ((pevent->OSEventTbl[y] &= ~bitx) == 0) { /* Remove this task from the waiting list */ //bitx 表示D3,D2,D1中对应在pevent->OSEventTbl[y]这个组中的位置 //pevent->OSEventTbl[y]的8个位和bitx取反之后做与,来判断是否在等待。 pevent->OSEventGrp &= ~bity; //对应的组中这一位的值取反,即取消这一位的等待状态 } ptcb = OSTCBPrioTbl[prio]; /* Point to this task's OS_TCB */ //把优先级信息告诉ptcb ptcb->OSTCBDly = 0; /* Prevent OSTimeTick() from readying task */ //不等待 ptcb->OSTCBEventPtr = (OS_EVENT *)0; /* Unlink ECB from this task */ //不连接ECB #if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN ptcb->OSTCBMsg = msg; /* Send message directly to waiting task */ //直接发送信息给等待任务 #else msg = msg; /* Prevent compiler warning if not used */ //防止编译器报错 #endif ptcb->OSTCBStat &= ~msk; /* Clear bit associated with event type */ //清除事件类型 if (ptcb->OSTCBStat == OS_STAT_RDY) { /* See if task is ready (could be susp'd) */ OSRdyGrp |= bity; /* Put task in the ready to run list */ OSRdyTbl[y] |= bitx; //如果任务就绪了,添加到就绪列表 } } #endif /*$PAGE*/ /* ********************************************************************************************************* * MAKE TASK WAIT FOR EVENT TO OCCUR * * Description: This function is called by other uC/OS-II services to suspend a task because an event has * not occurred. * * Arguments : pevent is a pointer to the event control block for which the task will be waiting for. * * Returns : none * * Note : This function is INTERNAL to uC/OS-II and your application should not call it. ********************************************************************************************************* */ #if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN || OS_SEM_EN //使一个任务进入等待某事件发生状态 void OSEventTaskWait (OS_EVENT *pevent) reentrant { OSTCBCur->OSTCBEventPtr = pevent; /* Store pointer to event control block in TCB */ //把这个任务的指针指向TCB任务块 if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) { /* Task no longer ready */ OSRdyGrp &= ~OSTCBCur->OSTCBBitY; //从就绪列表中删除这个任务 //代码参照上面的OSEventTaskRdy函数自己分析。 } pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX; /* Put task in waiting list */ pevent->OSEventGrp |= OSTCBCur->OSTCBBitY; //在等待列表中添加这个任务 } #endif /*$PAGE*/ /* ********************************************************************************************************* * MAKE TASK READY TO RUN BASED ON EVENT TIMEOUT * * Description: This function is called by other uC/OS-II services to make a task ready to run because a * timeout occurred. * * Arguments : pevent is a pointer to the event control block which is readying a task. * * Returns : none * * Note : This function is INTERNAL to uC/OS-II and your application should not call it. ********************************************************************************************************* */ #if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN || OS_SEM_EN //由于等待超时而将任务置为就绪态 void OSEventTO (OS_EVENT *pevent) reentrant { if ((pevent->OSEventTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) { pevent->OSEventGrp &= ~OSTCBCur->OSTCBBitY; } //从就绪列表中删除这个任务 OSTCBCur->OSTCBStat = OS_STAT_RDY; /* Set status to ready */ //把状态设置为就绪 OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; /* No longer waiting for event */ //不需要等待事件 } #endif /*$PAGE*/ /* ********************************************************************************************************* * INITIALIZE EVENT CONTROL BLOCK'S WAIT LIST * * Description: This function is called by other uC/OS-II services to initialize the event wait list. * * Arguments : pevent is a pointer to the event control block allocated to the event. * * Returns : none * * Note : This function is INTERNAL to uC/OS-II and your application should not call it. ********************************************************************************************************* */ #if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN || OS_SEM_EN // 初始化ECB块的等待任务列表 void OSEventWaitListInit (OS_EVENT *pevent) reentrant { INT8U i; pevent->OSEventGrp = 0x00; /* No task waiting on event */ //直接把OSEventGrp=0x00 for (i = 0; i < OS_EVENT_TBL_SIZE; i++) { pevent->OSEventTbl[i] = 0x00; } //OSEventTbl[0]-OSEventTbl[OS_EVENT_TBL_SIZE]全部等于0x00 } #endif /*$PAGE*/ /* ********************************************************************************************************* * INITIALIZATION * * Description: This function is used to initialize the internals of uC/OS-II and MUST be called prior to * creating any uC/OS-II object and, prior to calling OSStart(). * * Arguments : none * * Returns : none ********************************************************************************************************* */ //它在OS应用中的main()函数中首先被调用,是OS运行的第一个函数,它完成各初始变量的初始化。 void OSInit (void) reentrant { INT16U i; OSTime = 0L; /* Clear the 32-bit system clock */ //清空化时间戳 OSIntNesting = 0; /* Clear the interrupt nesting counter */ //清空中断嵌套标志 //OSIntNesting是作为调度器是否可进行调度的标志,以保证调度器不会在中断服务程序中进行任务调度度器加锁器 OSLockNesting = 0; /* Clear the scheduling lock counter */ //清空调度锁 //SLockNesting是用来表示嵌套层数的 #if OS_TASK_CREATE_EN || OS_TASK_CREATE_EXT_EN || OS_TASK_DEL_EN OSTaskCtr = 0; /* Clear the number of tasks */ //清除任务的数量 // #endif OSRunning = FALSE; /* Indicate that multitasking not started */ //清除任务开始标志位 OSIdleCtr = 0L; /* Clear the 32-bit idle counter */ //清除空闲计数 #if OS_TASK_STAT_EN && OS_TASK_CREATE_EXT_EN OSIdleCtrRun = 0L; OSIdleCtrMax = 0L; OSStatRdy = FALSE; /* Statistic task is not ready */ //和空闲任务有关的三个变量,初始化的时间全部清空 #endif OSCtxSwCtr = 0; /* Clear the context switch counter */ //清空切换计数 //任务和任务之间的切换次数通过OSCtxSwCtr统计 OSRdyGrp = 0; //清空就绪列表组 /* Clear the ready list */ for (i = 0; i < OS_RDY_TBL_SIZE; i++) { OSRdyTbl[i] = 0; } // 清空各个组中的元素 //以下是对TCB任务的初始化,很简单 ,自己看看就懂 OSPrioCur = 0; OSPrioHighRdy = 0; OSTCBHighRdy = (OS_TCB *)0; /* TCB Initialization */ OSTCBCur = (OS_TCB *)0; OSTCBList = (OS_TCB *)0; for (i = 0; i < (OS_LOWEST_PRIO + 1); i++) { /* Clear the priority table */ OSTCBPrioTbl[i] = (OS_TCB *)0; } for (i = 0; i < (OS_MAX_TASKS + OS_N_SYS_TASKS - 1); i++) { /* Init. list of free TCBs */ OSTCBTbl[i].OSTCBNext = &OSTCBTbl[i + 1]; } OSTCBTbl[OS_MAX_TASKS + OS_N_SYS_TASKS - 1].OSTCBNext = (OS_TCB *)0; /* Last OS_TCB */ OSTCBFreeList = &OSTCBTbl[0]; #if OS_MAX_EVENTS >= 2 for (i = 0; i < (OS_MAX_EVENTS - 1); i++) { /* Init. list of free EVENT control blocks */ OSEventTbl[i].OSEventPtr = (OS_EVENT *)&OSEventTbl[i + 1]; } OSEventTbl[OS_MAX_EVENTS - 1].OSEventPtr = (OS_EVENT *)0; OSEventFreeList = &OSEventTbl[0]; #endif //以上是对TCB任务的初始化,很简单,一看就懂 #if OS_Q_EN && (OS_MAX_QS >= 2) OSQInit(); //初始化队列 /* Initialize the message queue structures */ // #endif #if OS_MEM_EN && OS_MAX_MEM_PART >= 2 OSMemInit(); //初始化内存 /* Initialize the memory manager */ #endif //下面代码主要是根据栈的方向是向上还是向下,分别创建了两个任务:空闲任务和OSTaskStat任务 //同时OS_TASK_CREATE_EXT_EN如果使能,创建任务时可以配置的参数更多,自己看看很容易懂,累,不一一分析了。 #if OS_STK_GROWTH == 1 //选择栈的方向 #if OS_TASK_CREATE_EXT_EN OSTaskCreateExt(OSTaskIdle, //创建了空闲任务 (void *)0, /* No arguments passed to OSTaskIdle() */ &OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1], /* Set Top-Of-Stack */ OS_IDLE_PRIO, /* Lowest priority level */ OS_TASK_IDLE_ID, &OSTaskIdleStk[0], /* Set Bottom-Of-Stack */ OS_TASK_IDLE_STK_SIZE, (void *)0, /* No TCB extension */ OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);/* Enable stack checking + clear stack */ #else OSTaskCreate(OSTaskIdle, (void *)0, &OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1], OS_IDLE_PRIO); #endif #else #if OS_TASK_CREATE_EXT_EN OSTaskCreateExt(OSTaskIdle, (void *)0, /* No arguments passed to OSTaskIdle() */ &OSTaskIdleStk[0], /* Set Top-Of-Stack */ OS_IDLE_PRIO, /* Lowest priority level */ OS_TASK_IDLE_ID, &OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1], /* Set Bottom-Of-Stack */ OS_TASK_IDLE_STK_SIZE, (void *)0, /* No TCB extension */ OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);/* Enable stack checking + clear stack */ #else OSTaskCreate(OSTaskIdle, (void *)0, &OSTaskIdleStk[0], OS_IDLE_PRIO); #endif #endif #if OS_TASK_STAT_EN #if OS_TASK_CREATE_EXT_EN #if OS_STK_GROWTH == 1 OSTaskCreateExt(OSTaskStat, //创建了OSTaskStat任务 (void *)0, /* No args passed to OSTaskStat() */ &OSTaskStatStk[OS_TASK_STAT_STK_SIZE - 1],/* Set Top-Of-Stack */ OS_STAT_PRIO, /* One higher than the idle task */ OS_TASK_STAT_ID, &OSTaskStatStk[0], /* Set Bottom-Of-Stack */ OS_TASK_STAT_STK_SIZE, (void *)0, /* No TCB extension */ OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); /* Enable stack checking + clear */ #else OSTaskCreateExt(OSTaskStat, (void *)0, /* No args passed to OSTaskStat() */ &OSTaskStatStk[0], /* Set Top-Of-Stack */ OS_STAT_PRIO, /* One higher than the idle task */ OS_TASK_STAT_ID, &OSTaskStatStk[OS_TASK_STAT_STK_SIZE - 1],/* Set Bottom-Of-Stack */ OS_TASK_STAT_STK_SIZE, (void *)0, /* No TCB extension */ OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); /* Enable stack checking + clear */ #endif #else #if OS_STK_GROWTH == 1 OSTaskCreate(OSTaskStat, (void *)0, /* No args passed to OSTaskStat() */ &OSTaskStatStk[OS_TASK_STAT_STK_SIZE - 1], /* Set Top-Of-Stack */ OS_STAT_PRIO); /* One higher than the idle task */ #else OSTaskCreate(OSTaskStat, (void *)0, /* No args passed to OSTaskStat() */ &OSTaskStatStk[0], /* Set Top-Of-Stack */ OS_STAT_PRIO); /* One higher than the idle task */ #endif #endif #endif } /*$PAGE*/ /* ********************************************************************************************************* * ENTER ISR * * Description: This function is used to notify uC/OS-II that you are about to service an interrupt * service routine (ISR). This allows uC/OS-II to keep track of interrupt nesting and thus * only perform rescheduling at the last nested ISR. * * Arguments : none * * Returns : none * * Notes : 1) Your ISR can directly increment OSIntNesting without calling this function because * OSIntNesting has been declared 'global'. You MUST, however, be sure that the increment * is performed 'indivisibly' by your processor to ensure proper access to this critical * resource. * 2) You MUST still call OSIntExit() even though you increment OSIntNesting directly. * 3) You MUST invoke OSIntEnter() and OSIntExit() in pair. In other words, for every call * to OSIntEnter() at the beginning of the ISR you MUST have a call to OSIntExit() at the * end of the ISR. ********************************************************************************************************* */ //在uC/OS-II中,通常在进入中断时需要使用OSIntEnter() ;退出中断前使用OSIntExit(); // 这个函数的作用是对全局变量OSIntNesting增1,OSIntNesting为中断嵌套深度。 void OSIntEnter (void) reentrant { OS_ENTER_CRITICAL(); //进入临界段 OSIntNesting++; /* Increment ISR nesting level */ //OSIntNesting为中断嵌套深度+1 OS_EXIT_CRITICAL(); //退出临界段 } /*$PAGE*/ /* ********************************************************************************************************* * EXIT ISR * * Description: This function is used to notify uC/OS-II that you have completed serviving an ISR. When * the last nested ISR has completed, uC/OS-II will call the scheduler to determine whether * a new, high-priority task, is ready to run. * * Arguments : none * * Returns : none * * Notes : 1) You MUST invoke OSIntEnter() and OSIntExit() in pair. In other words, for every call * to OSIntEnter() at the beginning of the ISR you MUST have a call to OSIntExit() at the * end of the ISR. * 2) Rescheduling is prevented when the scheduler is locked (see OSSchedLock()) ********************************************************************************************************* */ //函数的前面部分对OSIntNesting减1,刚好与OSIntEnter() 相对应;后面部分则进行任务调度。 void OSIntExit (void) reentrant { OS_ENTER_CRITICAL(); if ((--OSIntNesting | OSLockNesting) == 0) { /* Reschedule only if all ISRs completed & not locked */ OSIntExitY = OSUnMapTbl[OSRdyGrp]; OSPrioHighRdy = (INT8U)((OSIntExitY << 3) + OSUnMapTbl[OSRdyTbl[OSIntExitY]]); if (OSPrioHighRdy != OSPrioCur) { /* No context switch if current task is highest ready */ OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; OSCtxSwCtr++; /* Keep track of the number of context switches */ OSIntCtxSw(); /* Perform interrupt level context switch */ } } OS_EXIT_CRITICAL(); } /*$PAGE*/ /* ********************************************************************************************************* * SCHEDULER * * Description: This function is called by other uC/OS-II services to determine whether a new, high * priority task has been made ready to run. This function is invoked by TASK level code * and is not used to reschedule tasks from ISRs (see OSIntExit() for ISR rescheduling). * * Arguments : none * * Returns : none * * Notes : 1) This function is INTERNAL to uC/OS-II and your application should not call it. * 2) Rescheduling is prevented when the scheduler is locked (see OSSchedLock()) ********************************************************************************************************* */ //任务调度函数 void OSSched (void) reentrant { INT8U y; OS_ENTER_CRITICAL(); //进入临界段 if ((OSLockNesting | OSIntNesting) == 0) { /* Task scheduling must be enabled and not ISR level */ //判断没有上锁也没有中断发生 y = OSUnMapTbl[OSRdyGrp]; /* Get pointer to highest priority task ready to run */ OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]); //查找任务优先级最高的任务 if (OSPrioHighRdy != OSPrioCur) { /* No context switch if current task is highest ready */ //如果任务优先级最高的任务不是当前任务 OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; //把优先级最高的任务赋值给OSTCBHighRdy OSCtxSwCtr++; /* Increment context switch counter */ //任务切换此书加一 OS_TASK_SW(); /* Perform a context switch */ //开始进行任务切换 } } OS_EXIT_CRITICAL(); //退出临界段 } /*$PAGE*/ /* ********************************************************************************************************* * PREVENT SCHEDULING * * Description: This function is used to prevent rescheduling to take place. This allows your application * to prevent context switches until you are ready to permit context switching. * * Arguments : none * * Returns : none * * Notes : 1) You MUST invoke OSSchedLock() and OSSchedUnlock() in pair. In other words, for every * call to OSSchedLock() you MUST have a call to OSSchedUnlock(). ********************************************************************************************************* */ #if OS_SCHED_LOCK_EN > 0 //这个函数又叫上锁函数,如果在一个任务里面调用了上锁函数,那么OSSched()这个任务切换函数就不会执行也就是说不会进任务调度。 void OSSchedLock (void) reentrant { if (OSRunning == TRUE) { /* Make sure multitasking is running */ OS_ENTER_CRITICAL(); OSLockNesting++; /* Increment lock nesting level */ OS_EXIT_CRITICAL(); } } #endif /*$PAGE*/ /* ********************************************************************************************************* * ENABLE SCHEDULING * * Description: This function is used to re-allow rescheduling. * * Arguments : none * * Returns : none * * Notes : 1) You MUST invoke OSSchedLock() and OSSchedUnlock() in pair. In other words, for every * call to OSSchedLock() you MUST have a call to OSSchedUnlock(). ********************************************************************************************************* */ #if OS_SCHED_LOCK_EN > 0 //解锁函数 void OSSchedUnlock (void) reentrant { if (OSRunning == TRUE) { /* Make sure multitasking is running */ //判断任务是不是在执行 OS_ENTER_CRITICAL(); //进入临界段 if (OSLockNesting > 0) { /* Do not decrement if already 0 */ //把上锁的次数-1 OSLockNesting--; /* Decrement lock nesting level */ if ((OSLockNesting | OSIntNesting) == 0) { /* See if scheduling re-enabled and not an ISR */ OS_EXIT_CRITICAL(); //如果没有锁了也没有中断 OSSched(); /* See if a higher priority task is ready */ //进行一次调度 } else { OS_EXIT_CRITICAL(); //退出临界段 } } else { OS_EXIT_CRITICAL(); //退出临界段 } } } #endif /*$PAGE*/ /* ********************************************************************************************************* * START MULTITASKING * * Description: This function is used to start the multitasking process which lets uC/OS-II manages the * task that you have created. Before you can call OSStart(), you MUST have called OSInit() * and you MUST have created at least one task. * * Arguments : none * * Returns : none * * Note : OSStartHighRdy() MUST: * a) Call OSTaskSwHook() then, * b) Set OSRunning to TRUE. ********************************************************************************************************* */ //功能是运行优先级最高的就绪任务,在调用OSStart()之前,用户必须先调用OSInit(),并且已经至少创建了一个任务. void OSStart (void) reentrant { INT8U y; INT8U x; if (OSRunning == FALSE) { //如果OSRunning不在运行 y = OSUnMapTbl[OSRdyGrp]; /* Find highest priority's task priority number */ x = OSUnMapTbl[OSRdyTbl[y]]; OSPrioHighRdy = (INT8U)((y << 3) + x); //上面三行代码是为了找到优先级最高的任务 OSPrioCur = OSPrioHighRdy; //把优先级最高的任务赋值给OSPrioCur OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; /* Point to highest priority task ready to run */ //把优先级最高的任务赋值给OSTCBHighRdy OSTCBCur = OSTCBHighRdy; //最高优先级的任务赋值给当前OSTCBCur OSStartHighRdy(); /* Execute target specific code to start task */ //运行最高优先级的任务 } } /*$PAGE*/ /* ********************************************************************************************************* * STATISTICS INITIALIZATION * * Description: This function is called by your application to establish CPU usage by first determining * how high a 32-bit counter would count to in 1 second if no other tasks were to execute * during that time. CPU usage is then determined by a low priority task which keeps track * of this 32-bit counter every second but this time, with other tasks running. CPU usage is * determined by: * * OSIdleCtr * CPU Usage (%) = 100 * (1 - ------------) * OSIdleCtrMax * * Arguments : none * * Returns : none ********************************************************************************************************* */ #if OS_TASK_STAT_EN //OSStatInit()要在建立的第一个,并且只有一个任务的时候调用,所以会经常见到下面的结构: void OSStatInit (void) reentrant { OSTimeDly(2); /* Synchronize with clock tick */ OS_ENTER_CRITICAL(); OSIdleCtr = 0L; /* Clear idle counter */ OS_EXIT_CRITICAL(); OSTimeDly(OS_TICKS_PER_SEC); /* Determine MAX. idle counter value for 1 second */ OS_ENTER_CRITICAL(); OSIdleCtrMax = OSIdleCtr; /* Store maximum idle counter count in 1 second */ OSStatRdy = TRUE; //这里只有OSStatRdy变为TRUE,下面的任务才能进行进行 OS_EXIT_CRITICAL(); } #endif /*$PAGE*/ /* ********************************************************************************************************* * IDLE TASK * * Description: This task is internal to uC/OS-II and executes whenever no other higher priority tasks * executes because they are waiting for event(s) to occur. * * Arguments : none * * Returns : none ********************************************************************************************************* */ //上面创建了空闲任务,OSIdleCtr一直在++ void OSTaskIdle (void *ppdata) reentrant { ppdata = ppdata; /* Prevent compiler warning for not using 'pdata' */ for (;;) { OS_ENTER_CRITICAL(); OSIdleCtr++; OS_EXIT_CRITICAL(); } } /*$PAGE*/ /* ********************************************************************************************************* * STATISTICS TASK * * Description: This task is internal to uC/OS-II and is used to compute some statistics about the * multitasking environment. Specifically, OSTaskStat() computes the CPU usage. * CPU usage is determined by: * * OSIdleCtr * OSCPUUsage = 100 * (1 - ------------) (units are in %) * OSIdleCtrMax * * Arguments : pdata this pointer is not used at this time. * * Returns : none * * Notes : 1) This task runs at a priority level higher than the idle task. In fact, it runs at the * next higher priority, OS_IDLE_PRIO-1. * 2) You can disable this task by setting the configuration #define OS_TASK_STAT_EN to 0. * 3) We delay for 5 seconds in the beginning to allow the system to reach steady state and * have all other tasks created before we do statistics. You MUST have at least a delay * of 2 seconds to allow for the system to establish the maximum value for the idle * counter. ********************************************************************************************************* */ #if OS_TASK_STAT_EN //在上面我们创建了一个OSTaskStat任务,这里是OSTaskStat任务的具体实现代码。 void OSTaskStat (void *ppdata) reentrant { INT32U run; INT8S usage; ppdata = ppdata; /* Prevent compiler warning for not using 'pdata' */ //防止编译器抽风 while (OSStatRdy == FALSE) { OSTimeDly(2 * OS_TICKS_PER_SEC); /* Wait until statistic task is ready */ } //如果任务没有准备好,就等待延时 for (;;) { OS_ENTER_CRITICAL(); //进入临界段 OSIdleCtrRun = OSIdleCtr; /* Obtain the of the idle counter for the past second */ //空闲计数的值,在OSTaskIdle任务中会不断+1 run = OSIdleCtr; //空闲计数的值,在OSTaskIdle任务中会不断+1 OSIdleCtr = 0L; /* Reset the idle counter for the next second */ //把OSIdleCtr变为0,一秒清空一次 OS_EXIT_CRITICAL(); //退出临界段 if (OSIdleCtrMax > 0L) { usage = (INT8S)(100L - 100L * run / OSIdleCtrMax); //计算使用率 if (usage > 100) { OSCPUUsage = 100; } else if (usage < 0) { OSCPUUsage = 0; } else { OSCPUUsage = usage; } } else { OSCPUUsage = 0; } OSTaskStatHook(); /* Invoke user definable hook */ //如果编写了钩子函数,运行钩子函数 OSTimeDly(OS_TICKS_PER_SEC); /* Accumulate OSIdleCtr for the next second */ //延时一下 } } #endif /*$PAGE*/ /* ********************************************************************************************************* * INITIALIZE TCB * * Description: This function is internal to uC/OS-II and is used to initialize a Task Control Block when * a task is created (see OSTaskCreate() and OSTaskCreateExt()). * * Arguments : prio is the priority of the task being created * * ptos is a pointer to the task's top-of-stack assuming that the CPU registers * have been placed on the stack. Note that the top-of-stack corresponds to a * 'high' memory location is OS_STK_GROWTH is set to 1 and a 'low' memory * location if OS_STK_GROWTH is set to 0. Note that stack growth is CPU * specific. * * pbos is a pointer to the bottom of stack. A NULL pointer is passed if called by * 'OSTaskCreate()'. * * id is the task's ID (0..65535) * * stk_size is the size of the stack (in 'stack units'). If the stack units are INT8Us * then, 'stk_size' contains the number of bytes for the stack. If the stack * units are INT32Us then, the stack contains '4 * stk_size' bytes. The stack * units are established by the #define constant OS_STK which is CPU * specific. 'stk_size' is 0 if called by 'OSTaskCreate()'. * * pext is a pointer to a user supplied memory area that is used to extend the task * control block. This allows you to store the contents of floating-point * registers, MMU registers or anything else you could find useful during a * context switch. You can even assign a name to each task and store this name * in this TCB extension. A NULL pointer is passed if called by OSTaskCreate(). * * opt options as passed to 'OSTaskCreateExt()' or, * 0 if called from 'OSTaskCreate()'. * * Returns : OS_NO_ERR if the call was successful * OS_NO_MORE_TCB if there are no more free TCBs to be allocated and thus, the task cannot * be created. * * Note : This function is INTERNAL to uC/OS-II and your application should not call it. ********************************************************************************************************* */ INT8U OSTCBInit (INT8U prio, OS_STK *ptos, OS_STK *pbos, INT16U id, INT16U stk_size, void *pext, INT16U opt) reentrant { OS_TCB *ptcb; OS_ENTER_CRITICAL(); //进入临界段 ptcb = OSTCBFreeList; /* Get a free TCB from the free TCB list */ //指针指向一个空的TCB块 if (ptcb != (OS_TCB *)0) { //以下为对TCB块进行的各种赋值操作,太多了,不写了。自己看。一看就懂 OSTCBFreeList = ptcb->OSTCBNext; /* Update pointer to free TCB list */ OS_EXIT_CRITICAL(); ptcb->OSTCBStkPtr = ptos; /* Load Stack pointer in TCB */ ptcb->OSTCBPrio = (INT8U)prio; /* Load task priority into TCB */ ptcb->OSTCBStat = OS_STAT_RDY; /* Task is ready to run */ ptcb->OSTCBDly = 0; /* Task is not delayed */ #if OS_TASK_CREATE_EXT_EN ptcb->OSTCBExtPtr = pext; /* Store pointer to TCB extension */ ptcb->OSTCBStkSize = stk_size; /* Store stack size */ ptcb->OSTCBStkBottom = pbos; /* Store pointer to bottom of stack */ ptcb->OSTCBOpt = opt; /* Store task options */ ptcb->OSTCBId = id; /* Store task ID */ #else pext = pext; /* Prevent compiler warning if not used */ stk_size = stk_size; pbos = pbos; opt = opt; id = id; #endif #if OS_TASK_DEL_EN ptcb->OSTCBDelReq = OS_NO_ERR; #endifzh ptcb->OSTCBY = prio >> 3; /* Pre-compute X, Y, BitX and BitY */ ptcb->OSTCBBitY = OSMapTbl[ptcb->OSTCBY]; ptcb->OSTCBX = prio & 0x07; ptcb->OSTCBBitX = OSMapTbl[ptcb->OSTCBX]; #if OS_MBOX_EN || (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_SEM_EN ptcb->OSTCBEventPtr = (OS_EVENT *)0; /* Task is not pending on an event */ #endif #if OS_MBOX_EN || (OS_Q_EN && (OS_MAX_QS >= 2)) ptcb->OSTCBMsg = (void *)0; /* No message received */ #endif 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; //以上为对TCB块进行的各种赋值操作,太多了,不写了。自己看。一看就懂 OS_EXIT_CRITICAL(); //退出临界段 return (OS_NO_ERR); } else { OS_EXIT_CRITICAL(); //退出临界段 return (OS_NO_MORE_TCB); } } /*$PAGE*/ /* ********************************************************************************************************* * PROCESS SYSTEM TICK * * Description: This function is used to signal to uC/OS-II the occurrence of a 'system tick' (also known * as a 'clock tick'). This function should be called by the ticker ISR but, can also be * called by a high priority task. * * Arguments : none * * Returns : none ********************************************************************************************************* */ //这个节拍服务函数是在OSTickISR函数中调用的,目的是在时钟节拍到来时, //检查每个任务的任务控制块中的. //OSTCBDly-1后是否为0,如果是,那么表明这个任务刚才是挂起的状态,此时应改变为就绪态 void OSTimeTick (void) reentrant { OS_TCB *ptcb; //**OSTimeTickHook(); /* Call user definable hook */ ptcb = OSTCBList; /* Point at first TCB in TCB list */ //指针指向第一个TCB块 while (ptcb->OSTCBPrio != OS_IDLE_PRIO) { /* Go through all TCBs in TCB list */ //判断优先级是不是空闲任务的优先级 OS_ENTER_CRITICAL(); //进入临界段 if (ptcb->OSTCBDly != 0) { /* Delayed or waiting for event with TO */ //判断ptcb->OSTCBDly是否等于0 if (--ptcb->OSTCBDly == 0) { /* Decrement nbr of ticks to end of delay */ //OSTCBDly-1后是否为0,如果是,那么表明这个任务刚才是挂起的状态,此时应改变为就绪态 if (!(ptcb->OSTCBStat & OS_STAT_SUSPEND)) { /* Is task suspended? */ OSRdyGrp |= ptcb->OSTCBBitY; /* No, Make task Rdy to Run (timed out)*/ OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; //如果状态是挂起,把这个任务添加到就绪列表 } else { /* Yes, Leave 1 tick to prevent ... */ ptcb->OSTCBDly = 1; /* ... loosing the task when the ... */ //接着等 } /* ... suspension is removed. */ } } ptcb = ptcb->OSTCBNext; /* Point at next TCB in TCB list */ //指向下一个TCB块 OS_EXIT_CRITICAL(); //退出临界段 } OS_ENTER_CRITICAL(); /* Update the 32-bit tick counter */ //进入临界段 OSTime++; //OSTime+1 OS_EXIT_CRITICAL(); //退出临界段 } /*$PAGE*/ /* ********************************************************************************************************* * GET VERSION * * Description: This function is used to return the version number of uC/OS-II. The returned value * corresponds to uC/OS-II's version number multiplied by 100. In other words, version 2.00 * would be returned as 200. * * Arguments : none * * Returns : the version number of uC/OS-II multiplied by 100. ********************************************************************************************************* */ #if OS_VERSION_EN //ucos 版本信息 INT16U OSVersion (void) reentrant { return (OS_VERSION); } #endif