这回说下系统中很重要的时钟节拍
在系统初始化的时候就用下面这个函数建了一个时钟任务。
/************************************************************************************************************************
void OS_TickTaskInit (OS_ERR *p_err)
{
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
OSTickCtr = (OS_TICK)0u; /* Clear the tick counter */
OSTickTaskTimeMax = (CPU_TS)0u;
OS_TickListInit(); /* 初始化一下时钟列表,一般延时的任务就在这里管理,下面看下它的内容*/
/* ---------------- CREATE THE TICK TASK ---------------- */
if (OSCfg_TickTaskPrio >= (OS_CFG_PRIO_MAX - 1u)) { /* Only one task at the 'Idle Task' priority */
*p_err = OS_ERR_PRIO_INVALID;
return;
}
/*下面是建时钟节拍任务*/
OSTaskCreate((OS_TCB *)&OSTickTaskTCB,
(CPU_CHAR *)((void *)"uC/OS-III Tick Task"),
(OS_TASK_PTR )OS_TickTask,
(void *)0,
(OS_PRIO )OSCfg_TickTaskPrio,
(CPU_STK *)OSCfg_TickTaskStkBasePtr,
(CPU_STK_SIZE)OSCfg_TickTaskStkLimit,
(CPU_STK_SIZE)OSCfg_TickTaskStkSize,
(OS_MSG_QTY )0u,
(OS_TICK )0u,
(void *)0,
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
(OS_ERR *)p_err);
}
void OS_TickListInit (void)
{
OS_TICK_SPOKE_IX i;
OS_TICK_SPOKE *p_spoke;
/*如果一个任务休息了,那它会从就绪列中退出,然后用OS_TICK_SPOKE来管理*/
for (i = 0u; i < OSCfg_TickWheelSize; i++) {
p_spoke = (OS_TICK_SPOKE *)&OSCfg_TickWheel[i];/*OSCfg_TickWheel[]在这我也不知道怎么说,先放下,往下看,会说的*/
p_spoke->FirstPtr = (OS_TCB *)0;
p_spoke->NbrEntries = (OS_OBJ_QTY )0u;
p_spoke->NbrEntriesMax = (OS_OBJ_QTY )0u;
}
}
这时我也要说下OS_TICK_SPOKE结构体
struct os_tick_spoke {
OS_TCB *FirstPtr; /* 这里怎么说呢比如挂OSCfg_TickWheel[1]任务可能有很多,但这里的
FirstPtr只指第一个任务,其他任务是用任务控制块中的成员TickNextPtr,TickPrevPtr来连接的*/
OS_OBJ_QTY NbrEntries; /* 还是用OSCfg_TickWheel[1]举例,挂要这个spoke上的任务数 */
OS_OBJ_QTY NbrEntriesMax; /* 这上的最多可以是多少个任务,(这说的不一定对) */
};
OSCfg_TickWheel对于这个大家先别急,等我说到OSTimeDly说,这节会说的。
*************************************************************************************************************************/
/************************************************************************************************************************
这个任务是在上面的函数中建立的。
void OS_TickTask (void *p_arg)
{
OS_ERR err;
CPU_TS ts;
p_arg = p_arg; /* Prevent compiler warning */
while (DEF_ON) {
(void)OSTaskSemPend((OS_TICK )0,/*这里是要等一个信号量,如果有的话就可以往下干活了,不然就会等这个信号,直到得到这个信号,这个我不会在这说,说到信号量时说*/
(OS_OPT )OS_OPT_PEND_BLOCKING,
(CPU_TS *)&ts,
(OS_ERR *)&err); /* Wait for signal from tick interrupt */
if (err == OS_ERR_NONE) {
if (OSRunning == OS_STATE_OS_RUNNING) {
OS_TickListUpdate(); /* Update all tasks waiting for time */
}
}
}
}
OSTimeTick这个是在定时中断中被调用的,用来给上面这个任务发送信号
我们要自写出一个定时中断,大概1秒进入定时中断几次是由我们定的,同时设置OSCfg_TickRate_Hz,也就是说,1秒,我们会给OS_TickTask
发送OSCfg_TickRate_Hz次信号。也说明这个任务1秒可以调用OS_TickListUpdate多少次。
我们不太会说,总之以上就是我想说的关于时钟节拍。
void OSTimeTick (void)/*在中断中调用*/
{
OS_ERR err;
#if OS_CFG_ISR_POST_DEFERRED_EN > 0u
CPU_TS ts;
#endif
OSTimeTickHook(); /* Call user definable hook */
#if OS_CFG_ISR_POST_DEFERRED_EN > 0u
ts = OS_TS_GET(); /* Get timestamp */
OS_IntQPost((OS_OBJ_TYPE) OS_OBJ_TYPE_TICK, /* 如果是到这的话,说明不立刻发送信号 */
(void *)&OSRdyList[OSPrioCur],
(void *) 0,
(OS_MSG_SIZE) 0u,
(OS_FLAGS ) 0u,
(OS_OPT ) 0u,
(CPU_TS ) ts,
(OS_ERR *)&err);
#else
(void)OSTaskSemPost((OS_TCB *)&OSTickTaskTCB, /* Signal tick task */
(OS_OPT ) OS_OPT_POST_NONE,
(OS_ERR *)&err);
#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
OS_SchedRoundRobin(&OSRdyList[OSPrioCur]);
#endif
#if OS_CFG_TMR_EN > 0u
OSTmrUpdateCtr--;/*这是用于软定时器的先不说了*/
if (OSTmrUpdateCtr == (OS_CTR)0u) {
OSTmrUpdateCtr = OSTmrUpdateCnt;
OSTaskSemPost((OS_TCB *)&OSTmrTaskTCB, /* Signal timer task */
(OS_OPT ) OS_OPT_POST_NONE,
(OS_ERR *)&err);
}
#endif
#endif
}
*************************************************************************************************************************/
/************************************************************************************************************************
好了说下任务的延时OSTimeDly,我们看这个函数的入口参数就知道,一个任务只能延时自己。
void OSTimeDly (OS_TICK dly,
OS_OPT opt,
OS_ERR *p_err)
{
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
if (OSIntNestingCtr > (OS_NESTING_CTR)0u) { /* Not allowed to call from an ISR */
*p_err = OS_ERR_TIME_DLY_ISR;
return;
}
#endif
if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0u) { /* Can't delay when the scheduler is locked */
*p_err = OS_ERR_SCHED_LOCKED;
return;
}
switch (opt) {
case OS_OPT_TIME_DLY:
case OS_OPT_TIME_TIMEOUT:
case OS_OPT_TIME_PERIODIC:
if (dly == (OS_TICK)0u) { /* 0 means no delay! */
*p_err = OS_ERR_TIME_ZERO_DLY;
return;
}
break;
case OS_OPT_TIME_MATCH:
break;
default:
*p_err = OS_ERR_OPT_INVALID;
return;
}
OS_CRITICAL_ENTER();
OSTCBCurPtr->TaskState = OS_TASK_STATE_DLY;/*当前任务的状态变化*/
OS_TickListInsert(OSTCBCurPtr,
dly,
opt,
p_err);
if (*p_err != OS_ERR_NONE) {
OS_CRITICAL_EXIT_NO_SCHED();
return;
}
OS_RdyListRemove(OSTCBCurPtr); /* Remove current task from ready list */
OS_CRITICAL_EXIT_NO_SCHED();
OSSched(); /* Find next task to run! */
*p_err = OS_ERR_NONE;
}
OS_TickListInsert把当前任务插入到OSCfg_TickWheel[]中。
OS_TICK=INT32 DEF_OCTET_NBR_BITS=8
#define OS_TICK_TH_RDY (OS_TICK)(DEF_BIT_FIELD(((sizeof(OS_TICK) * DEF_OCTET_NBR_BITS) / 2u), \16
((sizeof(OS_TICK) * DEF_OCTET_NBR_BITS) / 2u))) 16
DEF_INT_CPU_NBR_BITS=32
#define DEF_BIT_FIELD(bit_field, bit_shift) ((((bit_field) >= DEF_INT_CPU_NBR_BITS) ? (DEF_INT_CPU_U_MAX_VAL) \
: (DEF_BIT(bit_field) - 1uL)) \
<< (bit_shift))
void OS_TickListInsert (OS_TCB *p_tcb,
OS_TICK time,
OS_OPT opt,
OS_ERR *p_err)
{
OS_TICK tick_delta;
OS_TICK tick_next;
OS_TICK_SPOKE *p_spoke;
OS_TCB *p_tcb0;
OS_TCB *p_tcb1;
OS_TICK_SPOKE_IX spoke;
if (opt == OS_OPT_TIME_MATCH) { /* 这个选项是说,让你休息到什么时间点 */
tick_delta = time - OSTickCtr - 1u;
if (tick_delta > OS_TICK_TH_RDY) { /*这说的是,如果你设定的时间点,比当前的时间点小,说明现在,已经经过了那个时间点
,那么就不延时了,但也不绝对这样,不然咱就是想让它休息一会,它就不休息了怎么办。OS_TICK_TH_RDY是我们设置的,
上面的设置方法是我找到的别人做的,自己理解一下吧。
如果小的不是很多就算了,但要是小的很多就让它休息,因为OSTickCtr溢出之后能很快到达time那就会让
任务休息。根据我上面选的几个数可知OS_TICK_TH_RDY=0xffff0000,如果当前时间是0xffff000f,而
我们设置的时间为0xffff0005,那就不会让任务休息了,但要是0x00000005,这样是会让任务休息的*/
p_tcb->TickCtrMatch = (OS_TICK )0u;
p_tcb->TickRemain = (OS_TICK )0u;
p_tcb->TickSpokePtr = (OS_TICK_SPOKE *)0;
*p_err = OS_ERR_TIME_ZERO_DLY; /* ... do NOT delay. */
return;
}
p_tcb->TickCtrMatch = time;
p_tcb->TickRemain = tick_delta + 1u;
} else if (time > (OS_TICK)0u) {
if (opt == OS_OPT_TIME_PERIODIC) { /* OS_OPT_TIME_PERIODIC这说的是给一个固定时间,在这上时间里,你做事后,剩下的时间都让任务休息
比如写了一个任务,这个任务中用了这个函数,选项也是这个,时间设的是10,一开始p_tcb->TickCtrPrev=0,
那么,这个任务工作到了3时,运行到这个函数,这个函数会让他休息7.这时p_tcb->TickCtrPrev=10,如果下次
工作到了15,那么只会让任务休息5,这时p_tcb->TickCtrPrev=20*/
tick_next = p_tcb->TickCtrPrev + time;
tick_delta = tick_next - OSTickCtr - 1u;
if (tick_delta < time) { /* If next periodic delay did NOT already occur, ... */
p_tcb->TickCtrMatch = tick_next; /* ... set next periodic delay; ... */
} else {
p_tcb->TickCtrMatch = OSTickCtr + time; /* ... else reset periodic delay. */
}
p_tcb->TickRemain = p_tcb->TickCtrMatch - OSTickCtr;
p_tcb->TickCtrPrev = p_tcb->TickCtrMatch;
} else { /*OS_OPT_TIME_DLY还有这种,就是让任务休息time时间 */
p_tcb->TickCtrMatch = OSTickCtr + time;
p_tcb->TickRemain = time;
}
/*综上所述,根据我们的选项设置了p_tcb->TickCtrMatch,任务休息,然后当OSTickCtr到达这个值时,就会再次变成就绪的,p_tcb->TickRemain是还剩下
多长的休息时间。p_tcb->TickCtrPrev,是要OS_OPT_TIME_PERIODIC这个选项时用的,它和p_tcb->TickCtrMatch一起控制任务休息时间。p_tcb->TickCtrPrev是
上次休息到的时间,p_tcb->TickCtrMatch是这次可以休息到的时间,一个任务从p_tcb->TickCtrPrev开始工作,到一某个时间点休息,休息到p_tcb->TickCtrMatch。
然后p_tcb->TickCtrMatch会成为下次的工作的时间起点。*/
} else { /* Zero time delay; ... */
p_tcb->TickCtrMatch = (OS_TICK )0u;
p_tcb->TickRemain = (OS_TICK )0u;
p_tcb->TickSpokePtr = (OS_TICK_SPOKE *)0;
*p_err = OS_ERR_TIME_ZERO_DLY; /* ... do NOT delay. */
return;
}
/*然后用p_tcb->TickCtrMatch%OSCfg_TickWheelSize得出一个余数赋给spoke,然后取出OSCfg_TickWheel[spoke]来管理这个任务,
说实话,我觉得这种处理方真的很不错,咱们如果设定OSCfg_TickWheelSize为8,任何一个数去除这个数余只有0,1,2,3,4,5,6,7这几种情况,
然后就也说OSCfg_TickWheel[8]中的一个spoke来管理这个任务,如果有很多任务休息,这些任务可能不在同一个上,当系统查是不是有任务
到时间了,可以就绪了,那么只要查看OSCfg_TickWheel[OSTickCtr%OSCfg_TickWheelSize]就行了,这样就可以少查看几个任务(这你明白为什么了吗?)*/
spoke = (OS_TICK_SPOKE_IX)(p_tcb->TickCtrMatch % OSCfg_TickWheelSize);
p_spoke = &OSCfg_TickWheel[spoke];
if (p_spoke->NbrEntries == (OS_OBJ_QTY)0u) { /* 如果是挂在这个spoke上的第一个任务 */
p_tcb->TickNextPtr = (OS_TCB *)0;
p_tcb->TickPrevPtr = (OS_TCB *)0;
p_spoke->FirstPtr = p_tcb;
p_spoke->NbrEntries = (OS_OBJ_QTY)1u;
} else {
p_tcb1 = p_spoke->FirstPtr; /* 如果不是,就把这个任务插到这个链中, */
while (p_tcb1 != (OS_TCB *)0) {
p_tcb1->TickRemain = p_tcb1->TickCtrMatch- OSTickCtr; /* 看这几行,我们发现,当前任务要插在,一个合适的位置,也就是当前任务插在那个位置后,
一定是这个任务前面的任务所需的TickRemain 比它所需的小,而它后面的一定比它大 。这样做的原因是
当查看这个spoke上哪个任务可以进入就绪状况所用的时间最小(这我想我说的明白吧?) */
if (p_tcb->TickRemain > p_tcb1->TickRemain) { /* Do we need to insert AFTER current TCB in list? */
if (p_tcb1->TickNextPtr != (OS_TCB *)0) { /* Yes, are we pointing at the last TCB in the list? */
p_tcb1 = p_tcb1->TickNextPtr; /* No, Point to next TCB in the list */
} else {
p_tcb->TickNextPtr = (OS_TCB *)0;
p_tcb->TickPrevPtr = p_tcb1;
p_tcb1->TickNextPtr = p_tcb; /* Yes, TCB to add is now new last entry in the list */
p_tcb1 = (OS_TCB *)0; /* Break loop */
}
} else { /* Insert before the current TCB */
if (p_tcb1->TickPrevPtr == (OS_TCB *)0) { /* Are we inserting before the first TCB? */
p_tcb->TickPrevPtr = (OS_TCB *)0;
p_tcb->TickNextPtr = p_tcb1;
p_tcb1->TickPrevPtr = p_tcb;
p_spoke->FirstPtr = p_tcb;
} else { /* Insert in between 2 TCBs already in the list */
p_tcb0 = p_tcb1->TickPrevPtr;
p_tcb->TickPrevPtr = p_tcb0;
p_tcb->TickNextPtr = p_tcb1;
p_tcb0->TickNextPtr = p_tcb;
p_tcb1->TickPrevPtr = p_tcb;
}
p_tcb1 = (OS_TCB *)0; /* Break loop */
}
}
p_spoke->NbrEntries++;
}
if (p_spoke->NbrEntriesMax < p_spoke->NbrEntries) { /* Keep track of maximum # of entries in each spoke */
p_spoke->NbrEntriesMax = p_spoke->NbrEntries;
}
p_tcb->TickSpokePtr = p_spoke; /* Link back to tick spoke */
*p_err = OS_ERR_NONE;
}
画个图来看一个
spoke
+-------------+________
| FirstPtr | |
+-------------+ | tcb1 tcb2 tcb2
|NbrEntries=3 |
|---->+-----------+ +-----------+ +-----------+
+-------------+ 0 <-----|TickPrevPtr| <-----|TickPrevPtr| <-----|TickPrevPtr|
|NbrEntriesMax| +-----------+ +-----------+ +-----------+
+-------------+ |TickNextPtr|-----> |TickNextPtr|-----> |TickNextPtr|-----> 0
+-----------+ +-----------+ +-----------+
*************************************************************************************************************************/
/************************************************************************************************************************
我们在看一下OS_Ticktask任务做的事OS_TickListUpdate();就是看看当前时间没有没任务延时结束,只看OSCfg_TickWheel[OSTickCtr % OSCfg_TickWheelSize]上的任务,
而且先看第一个如果第一个延时结束才会向后看,因为后面的延时时间都是大于等于前面的,前面的不就绪,后面的也不可能就绪,这样做就很高效,实时性就好
void OS_TickListUpdate (void)
{
CPU_BOOLEAN done;
OS_TICK_SPOKE *p_spoke;
OS_TCB *p_tcb;
OS_TCB *p_tcb_next;
OS_TICK_SPOKE_IX spoke;
CPU_TS ts_start;
CPU_TS ts_end;
CPU_SR_ALLOC();
OS_CRITICAL_ENTER();
ts_start = OS_TS_GET();
OSTickCtr++; /* 每来一次定时中断这个数就加1 */
spoke = (OS_TICK_SPOKE_IX)(OSTickCtr % OSCfg_TickWheelSize);/*刚刚说了,一次只看一个spoke上的任务。看哪个呢?就由这个余数确定*/
p_spoke = &OSCfg_TickWheel[spoke];
p_tcb = p_spoke->FirstPtr;
done = DEF_FALSE;
while (done == DEF_FALSE) {
if (p_tcb != (OS_TCB *)0) {
p_tcb_next = p_tcb->TickNextPtr; /* Point to next TCB to update */
switch (p_tcb->TaskState) {
case OS_TASK_STATE_RDY:
case OS_TASK_STATE_PEND:
case OS_TASK_STATE_SUSPENDED:
case OS_TASK_STATE_PEND_SUSPENDED:
break;
case OS_TASK_STATE_DLY:
p_tcb->TickRemain = p_tcb->TickCtrMatch /* Compute time remaining of current TCB */
- OSTickCtr;
if (OSTickCtr == p_tcb->TickCtrMatch) { /* Process each TCB that expires */
p_tcb->TaskState = OS_TASK_STATE_RDY;
OS_TaskRdy(p_tcb); /* Make task ready to run */
} else {
done = DEF_TRUE; /*上面我说过,在一个spoke上的任务,TickRemain小的在前面,
如果最前面的任务都没就绪,后面的也不会就绪,所以就不向下看了*/
}
break;
case OS_TASK_STATE_PEND_TIMEOUT:
p_tcb->TickRemain = p_tcb->TickCtrMatch /* 这个是在等事件时用的,一般我们不会让任务无限等那个事件,有时间限制的,时间到了,也会就绪 */
- OSTickCtr;
if (OSTickCtr == p_tcb->TickCtrMatch) { /* Process each TCB that expires */
#if (OS_MSG_EN > 0u)
p_tcb->MsgPtr = (void *)0;
p_tcb->MsgSize = (OS_MSG_SIZE)0u;
#endif
p_tcb->TS = OS_TS_GET();
OS_PendListRemove(p_tcb); /* Remove from wait list */
OS_TaskRdy(p_tcb);
p_tcb->TaskState = OS_TASK_STATE_RDY;
p_tcb->PendStatus = OS_STATUS_PEND_TIMEOUT; /* 这是说明我们没等到我们想要的事件 */
p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /* 因为超时也就不等了 */
} else {
done = DEF_TRUE; /* Don't find a match, we're done! */
}
break;
case OS_TASK_STATE_DLY_SUSPENDED:/*这个位置我想说的是SUSPENDED,说的是待机状态(我也不知道可不可以这么说就是闲着没事),
以上都是延时结束,就会进入就绪,有了这个状态就是延时结束也不会就绪*/
p_tcb->TickRemain = p_tcb->TickCtrMatch /* Compute time remaining of current TCB */
- OSTickCtr;
if (OSTickCtr == p_tcb->TickCtrMatch) { /*OSTickCtr等于这个任务的 TickCtrMatch结束延时 */
p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;
OS_TickListRemove(p_tcb); /* 你看,虽然从这个链上拿下来了,但也没调用进入就绪状态那个函数,而且不就绪也不是因为延时或等事件*/
} else {
done = DEF_TRUE; /* 这个地方我不说了,上面说过了 */
}
break;
case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
p_tcb->TickRemain = p_tcb->TickCtrMatch /* Compute time remaining of current TCB */
- OSTickCtr;
if (OSTickCtr == p_tcb->TickCtrMatch) { /* Process each TCB that expires */
#if (OS_MSG_EN > 0u)
p_tcb->MsgPtr = (void *)0;
p_tcb->MsgSize = (OS_MSG_SIZE)0u;
#endif
p_tcb->TS = OS_TS_GET();
OS_PendListRemove(p_tcb); /* Remove from wait list */
OS_TickListRemove(p_tcb); /* Remove from current wheel spoke */
p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;
p_tcb->PendStatus = OS_STATUS_PEND_TIMEOUT; /*PendStatus就那么几个状态,等到事件了OK, 中止也就是不用这个事件了,删除事件,最后就是这个等待超时了 */
p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /* Indicate no longer pending */
} else {
done = DEF_TRUE; /* Don't find a match, we're done! */
}
break;
default:
break;
}
p_tcb = p_tcb_next;
} else {
done = DEF_TRUE;
}
}
ts_end = OS_TS_GET() - ts_start; /* 看这个函数执行了多少时间,也是验证实时性的,时间越短越好 */
if (ts_end > OSTickTaskTimeMax) {
OSTickTaskTimeMax = ts_end;
}
OS_CRITICAL_EXIT();
}
*************************************************************************************************************************/
/************************************************************************************************************************
下面的函数是将一个处于delay的任务解除,看下限制条件,我们知道,这个函数只解除处于OS_TASK_STATE_DLY状态
和OS_TASK_STATE_DLY_SUSPENDED的任务,任务处于前面的状态,解除delay同时还会使任务就绪,后者只是解除delay
还有,最后还会执行任务的调度,因为解除delay状态并就绪的任务可能优先级比当前运行的任务高。
void OSTimeDlyResume (OS_TCB *p_tcb,
OS_ERR *p_err)
{
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
if (OSIntNestingCtr > (OS_NESTING_CTR)0u) { /* Not allowed to call from an ISR */
*p_err = OS_ERR_TIME_DLY_RESUME_ISR;
return;
}
#endif
#if OS_CFG_ARG_CHK_EN > 0u
if (p_tcb == (OS_TCB *)0) { /* Not possible for the running task to be delayed! */
*p_err = OS_ERR_TASK_NOT_DLY;
return;
}
#endif
CPU_CRITICAL_ENTER();
switch (p_tcb->TaskState) {
case OS_TASK_STATE_RDY: /* Cannot Abort delay if task is ready */
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_TASK_NOT_DLY;
break;
case OS_TASK_STATE_DLY:
OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();
p_tcb->TaskState = OS_TASK_STATE_RDY;
OS_TickListRemove(p_tcb); /* Remove task from tick list */
OS_RdyListInsert(p_tcb); /* Add to ready list */
OS_CRITICAL_EXIT_NO_SCHED();
*p_err = OS_ERR_NONE;
break;
case OS_TASK_STATE_PEND:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_TASK_NOT_DLY;
break;
case OS_TASK_STATE_PEND_TIMEOUT:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_TASK_NOT_DLY;
break;
case OS_TASK_STATE_SUSPENDED:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_TASK_NOT_DLY;
break;
case OS_TASK_STATE_DLY_SUSPENDED:
OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();
p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;
OS_TickListRemove(p_tcb); /* Remove task from tick list */
OS_CRITICAL_EXIT_NO_SCHED();
*p_err = OS_ERR_TASK_SUSPENDED;
break;
case OS_TASK_STATE_PEND_SUSPENDED:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_TASK_NOT_DLY;
break;
case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_TASK_NOT_DLY;
break;
default:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_STATE_INVALID;
break;
}
OSSched();
}
*************************************************************************************************************************/
/************************************************************************************************************************
将任务从管理它的spoke上卸下,这个任务不在指向这个spoke,这个spoke也不指向这个任务。这个任务不再指向这个spoke上的其他任务,
这个spoke上的其他任务也不指向它。
void OS_TickListRemove (OS_TCB *p_tcb)
{
OS_TICK_SPOKE *p_spoke;
OS_TCB *p_tcb1;
OS_TCB *p_tcb2;
p_spoke = p_tcb->TickSpokePtr;
if (p_spoke != (OS_TICK_SPOKE *)0) { /* Confirm that task is in tick list */
p_tcb->TickRemain = (OS_TICK)0u;
if (p_spoke->FirstPtr == p_tcb) { /* Is timer to remove at the beginning of list? */
p_tcb1 = (OS_TCB *)p_tcb->TickNextPtr; /* Yes */
p_spoke->FirstPtr = p_tcb1; /*其实,这我想说,一般延时时间到了的,多是在这解除,因为spoke上的任务,一定是就前面的时间先到*/
if (p_tcb1 != (OS_TCB *)0) {
p_tcb1->TickPrevPtr = (void *)0;
}
} else {
p_tcb1 = p_tcb->TickPrevPtr; /* No, remove timer from somewhere in the list */
p_tcb2 = p_tcb->TickNextPtr;
p_tcb1->TickNextPtr = p_tcb2;
if (p_tcb2 != (OS_TCB *)0) { /*如果是调用了OSTimeDlyResume,才有可能执行到这,任务可能是在中间某位置解除*/
p_tcb2->TickPrevPtr = p_tcb1;
}
}
p_tcb->TickNextPtr = (OS_TCB *)0;
p_tcb->TickPrevPtr = (OS_TCB *)0;
p_tcb->TickSpokePtr = (OS_TICK_SPOKE *)0;
p_tcb->TickCtrMatch = (OS_TICK )0u;
p_spoke->NbrEntries--;
}
}
*************************************************************************************************************************/
/************************************************************************************************************************
字面上我们也可以看出来,如果解除延时的任务优先级和这个任务的相等,就把它放在这个优先级链上的最后,至于为什么我也不太明白,
可能是不想干扰到现在正在工作的任务吧。但我们大至可以猜到,这个刚被解除的任务可能原来是在当前任务之前的。
对于不等于当前任务优级的情况,其实可以这么看,如果是比当前任务优先级高,那么,当前任务能执行,说明比当前任务优先级高的
任务都处于阻塞的状态,而这个刚被解除的任务,所在的就绪链一定没别的任务,所以可以插到最前面,但也有可能是这样,被解除的任务
比当前的优先级低。比如,当前任务先运行一会,delay了,一个低优先级的运行一会也delay了,但马上那个任务就绪了,而低的还没有,
我们用这个就绪的任务解除了那个低优先级的,这也是有可能的,但这也说明,那个低优先级的任务在自己的任务链中原来也是在前面的,
因为我们们知道一个优先级链中,一定是执行第一个任务,但有没有可能那低优先级的任务不是在最前面的也是有可能的
比如优先级3上有一个任务A,优先级4上有两个任务B,C。A运行了一会,delay=5。B运行一会,然后delay=6,C运行一会,delay=10,
过一会,A动了,然后再一会B也就绪了,但A优先级高,所以还是A是当前任务,然后A将C的延时状态解除,看下面代码C一定会放在B前。
不过正常来说不会随便出现这种情况,而且,一般我们认为一个任务休息后应尽快工作,所以是放在优先级链前。
void OS_RdyListInsert (OS_TCB *p_tcb)
{
OS_PrioInsert(p_tcb->Prio);
if (p_tcb->Prio == OSPrioCur) { /* Are we readying a task at the same prio? */
OS_RdyListInsertTail(p_tcb); /* Yes, insert readied task at the end of the list */
} else {
OS_RdyListInsertHead(p_tcb); /* No, insert readied task at the beginning of the list */
}
}
只看这一个函数吧。比较简单。我也不说了,但为了逻辑连贯,我还是拿出来放在这。
void OS_RdyListInsertTail (OS_TCB *p_tcb)
{
OS_RDY_LIST *p_rdy_list;
OS_TCB *p_tcb2;
p_rdy_list = &OSRdyList[p_tcb->Prio];
if (p_rdy_list->NbrEntries == (OS_OBJ_QTY)0) { /* CASE 0: Insert when there are no entries */
p_rdy_list->NbrEntries = (OS_OBJ_QTY)1; /* This is the first entry */
p_tcb->NextPtr = (OS_TCB *)0; /* No other OS_TCBs in the list */
p_tcb->PrevPtr = (OS_TCB *)0;
p_rdy_list->HeadPtr = p_tcb; /* Both list pointers point to this OS_TCB */
p_rdy_list->TailPtr = p_tcb;
} else { /* CASE 1: Insert AFTER the current tail of list */
p_rdy_list->NbrEntries++; /* One more OS_TCB in the list */
p_tcb->NextPtr = (OS_TCB *)0; /* Adjust new OS_TCBs links */
p_tcb2 = p_rdy_list->TailPtr;
p_tcb->PrevPtr = p_tcb2;
p_tcb2->NextPtr = p_tcb; /* Adjust old tail of list's links */
p_rdy_list->TailPtr = p_tcb;
}
}
*************************************************************************************************************************