//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