ucos-iii时钟节拍

这回说下系统中很重要的时钟节拍
在系统初始化的时候就用下面这个函数建了一个时钟任务。
/************************************************************************************************************************

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;
    }
}
*************************************************************************************************************************
 

你可能感兴趣的:(实时操作系统)