0-1write MC/OS __Basics4

July 24, 2021 10:04:51 Saturday

9, Implementation time base list

What is a time base list

The time base list is time-dependent. Tasks in delay and tasks with timeout restrictions on waiting events will be removed from the ready list and then inserted into the time base list.
The time base list is updated in OSTimeTIck. If the delay time of the task ends or the timeout expires, the task will be ready, removed from the time base list and inserted into the ready list.

1. Implementation time base list

1.1. Defining time base list variables

/* Time base list size, in os_cfg_app.h definition */
#define  OS_CFG_TICK_WHEEL_SIZE           17u

/* In os_cfg_app.c definition */
/* Time base list */
(1)(2)
OS_TICK_SPOKE  OSCfg_TickWheel[OS_CFG_TICK_WHEEL_SIZE];
/* Time base list size */
OS_OBJ_QTY const OSCfg_TickWheelSize = (OS_OBJ_QTY  )OS_CFG_TICK_WHEEL_SIZE;


/* Declare in os.h */
/* Time base list */
extern  OS_TICK_SPOKE  OSCfg_TickWheel[];
/* Time base list size */
extern  OS_OBJ_QTY    const OSCfg_TickWheelSize;


/* Tick Counter, defined in os.h */
OS_EXT            OS_TICK                OSTickCtr;(3)
typedefstruct  os_tick_spoke       OS_TICK_SPOKE;(1)

struct  os_tick_spoke {
    OS_TCB              *FirstPtr;(2)
    OS_OBJ_QTY           NbrEntries;(3)
    OS_OBJ_QTY           NbrEntriesMax;(4)
};
//Time base list oscfg_ Each member of tickwheel [] contains a one-way linked list, and the TCB S inserted into the linked list will be arranged in ascending order according to the delay time.
FirstPtr Used to point to the first node of this one-way linked list.
NbrEntries Indicates how many nodes the one-way linked list currently has.
NbrEntriesMax Record the maximum number of nodes in the one-way linked list. It will be refreshed when adding nodes and not when deleting nodes.

1.2. Modify task control block TCB

Time base list oscfg_ Each member of tickwheel [] contains a one-way linked list. The TCBS inserted into the linked list will be arranged in ascending order according to the delay time. In order to connect TCBS in series from small to large according to the delay time, several members need to be added to the TCB

struct os_tcb {
    CPU_STK         *StkPtr;
    CPU_STK_SIZE    StkSize;

    /* Number of task delay cycles */
    OS_TICK         TaskDelayTicks;

    /* Task priority */
    OS_PRIO         Prio;

    /* Next pointer to the ready list bidirectional linked list */
    OS_TCB          *NextPtr;
    /* The previous pointer of the two-way linked list of the ready list */
    OS_TCB          *PrevPtr;

    /* Time base list related fields */
    OS_TCB          *TickNextPtr;//Point to the next TCB node in the linked list.
    OS_TCB          *TickPrevPtr;//Point to the previous TCB node in the linked list
    OS_TICK_SPOKE   *TickSpokePtr;//Each TCB inserted into the linked list contains a field TickSpokePtr, which is used to refer back to the root of the linked list.

    OS_TICK         TickCtrMatch;//It is equal to the value of the time base counter OSTickCtr plus the value of tickmain. When the value of TickCtrMatch is equal to the value of OSTickCtr, it indicates that the wait expires and TCB will be deleted from the linked list.
    OS_TICK         TickRemain;//It is used to set how many clock cycles a task needs to wait. The value will decrease with each clock cycle.
};

1.3. Implement time base list related functions

  • OS_TickListInit()

/* Initializes the data field of the time base list */
void  OS_TickListInit (void)   //Initialize the time base list, that is, the global variable oscfg_ All data fields of tickwheel [] are initialized to 0
{
    OS_TICK_SPOKE_IX   i;
    OS_TICK_SPOKE     *p_spoke;

    for (i = 0u; i < OSCfg_TickWheelSize; i++) {
        p_spoke                = (OS_TICK_SPOKE *)&OSCfg_TickWheel[i];
        p_spoke->FirstPtr      = (OS_TCB        *)0;
        p_spoke->NbrEntries    = (OS_OBJ_QTY     )0u;
        p_spoke->NbrEntriesMax = (OS_OBJ_QTY     )0u;
    }
}
  • OS_TickListInsert()

/* Insert a task into the time base list and arrange it in ascending order according to the delay time */
void  OS_TickListInsert (OS_TCB *p_tcb,OS_TICK time)   //Insert a task TCB into the time base list
{
    OS_TICK_SPOKE_IX   spoke;
    OS_TICK_SPOKE     *p_spoke;
    OS_TCB            *p_tcb0;
    OS_TCB            *p_tcb1;

    p_tcb->TickCtrMatch = OSTickCtr + time;(1)
    p_tcb->TickRemain   = time;(2)

    spoke   = (OS_TICK_SPOKE_IX)(p_tcb->TickCtrMatch % OSCfg_TickWheelSize);(3)
    p_spoke = &OSCfg_TickWheel[spoke];(4)

    /* Insert oscfg_ First node of tickwheel [spoke] */
    if (p_spoke->NbrEntries == (OS_OBJ_QTY)0u) (5)
    {
        p_tcb->TickNextPtr   = (OS_TCB   *)0;
        p_tcb->TickPrevPtr   = (OS_TCB   *)0;
        p_spoke->FirstPtr    =  p_tcb;
        p_spoke->NbrEntries  = (OS_OBJ_QTY)1u;
    }
    /* If the inserted node is not the first node, it is arranged in ascending order according to the size of tickmain */
    else (6)
    {
        /* Gets the first node pointer */
        p_tcb1 = p_spoke->FirstPtr;
        while (p_tcb1 != (OS_TCB *)0)
        {
            /* Calculate the remaining time of the comparison node */
            p_tcb1->TickRemain = p_tcb1->TickCtrMatch - OSTickCtr;

            /* Insert after comparison node */
            if (p_tcb->TickRemain > p_tcb1->TickRemain)
            {
                if (p_tcb1->TickNextPtr != (OS_TCB *)0)
                {
                    /* Find the next comparison node */
                    p_tcb1 =  p_tcb1->TickNextPtr;
                }
                else
                {  /* Insert at last node */
                    p_tcb->TickNextPtr   = (OS_TCB *)0;
                    p_tcb->TickPrevPtr   =  p_tcb1;
                    p_tcb1->TickNextPtr  =  p_tcb;
                    p_tcb1               = (OS_TCB *)0;(7)
                }
            }
            /* Insert in front of comparison node */
            else
            {
                /* Insert at first node */
                if (p_tcb1->TickPrevPtr == (OS_TCB *)0) {
                    p_tcb->TickPrevPtr   = (OS_TCB *)0;
                    p_tcb->TickNextPtr   =  p_tcb1;
                    p_tcb1->TickPrevPtr  =  p_tcb;
                    p_spoke->FirstPtr    =  p_tcb;
                }
                else
                {
                    /* Insert between two nodes */
                    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;
                }
                /* Jump out of while loop */
                p_tcb1 = (OS_TCB *)0;(8)
            }
        }

        /* Node successfully inserted */
        p_spoke->NbrEntries++;(9)
    }

    /* Refresh the value of NbrEntriesMax */
    if (p_spoke->NbrEntriesMax < p_spoke->NbrEntries) //Refresh the value of nbrentiesmax, which is used to record the maximum number of nodes in the current linked list. It is refreshed only when adding nodes and when deleting nodes
    {
        p_spoke->NbrEntriesMax = p_spoke->NbrEntries;//Is not refreshed.
    }

    /* TickSpokePtr in task TCB refers back to the root node */
    p_tcb->TickSpokePtr = p_spoke;(11)
}
//(3) Oscfg: the size of the time base list by the task's TickCtrMatch_ Tickwheelsize performs the remainder operation, and the resulting value, spoke, is used as the time base list oscfg_ Index of tickwheel []. As long as it is the task's TickCtrMatch to oscfg_ If the value of tick wheelsize after the remainder is equal to the value of spoke, the TCB of the task will be inserted into oscfg_ In the one-way linked list under tickwheel [spoke], the nodes are arranged in ascending order according to the TickCtrMatch value of the task. For example, there are three TCBS in the time base list, the time base list oscfg_ Size of tickwheel [] OSCfg_TickWheelSize is equal to 12, and the current time base counter OSTickCtr is 10. There are three tasks that need to delay TickTemain=1, TickTemain=23 and TickTemain=25 clock cycles respectively. The TickRemain of the three tasks plus OSTickCtr can obtain that their TickCtrMatch is equal to 11, 23 and 35 respectively. The TickCtrMatch of the three tasks is equal to oscfg_ The value of tick wheelsize after the remainder operation is equal to 11, so the TCBS of these three tasks will be inserted into oscfg_ For the same linked list under tickwheel [11], the node order is arranged in ascending order according to the value of TickCtrMatch.
  • OS_TickListRemove()

/* Remove a task from the time base list */
void  OS_TickListRemove (OS_TCB  *p_tcb)  //Deletes a specified TCB node from the time base list
{
    OS_TICK_SPOKE  *p_spoke;
    OS_TCB         *p_tcb1;
    OS_TCB         *p_tcb2;

    /* Get the root pointer of the linked list where the task TCB is located */
    p_spoke = p_tcb->TickSpokePtr;(1)

    /* Make sure the task is in the linked list */
    if (p_spoke != (OS_TICK_SPOKE *)0)
    {
        /* Reset remaining time */
        p_tcb->TickRemain = (OS_TICK)0u;

        /* It is just the first node to be removed */
        if (p_spoke->FirstPtr == p_tcb) (2)
        {
            /* Update the first node. The original first node needs to be removed */
            p_tcb1            = (OS_TCB *)p_tcb->TickNextPtr;
            p_spoke->FirstPtr = p_tcb1;
            if (p_tcb1 != (OS_TCB *)0)
            {
                p_tcb1->TickPrevPtr = (OS_TCB *)0;
            }
        }
        /* Not the first node to remove */(3)
        else
        {
            /* Save pointers to the front and back nodes of the node to be removed */
            p_tcb1              = p_tcb->TickPrevPtr;
            p_tcb2              = p_tcb->TickNextPtr;

            /* Node removal connects the two nodes before and after the node */
            p_tcb1->TickNextPtr = p_tcb2;
            if (p_tcb2 != (OS_TCB *)0)
            {
                p_tcb2->TickPrevPtr = p_tcb1;
            }
        }

        /* Reset the field members related to the time base list in the task TCB */(4)
        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;

        /* Node minus 1 */
        p_spoke->NbrEntries--;(5)
    }
}
  • OS_TickListUpdate()

void  OS_TickListUpdate (void)         //When each SysTick cycle arrives, OSTimeTick() is called to update the time base counter OSTickCtr and scan whether the task delay in the time base list expires
{
    OS_TICK_SPOKE_IX   spoke;
    OS_TICK_SPOKE     *p_spoke;
    OS_TCB            *p_tcb;
    OS_TCB            *p_tcb_next;
    CPU_BOOLEAN        done;

    CPU_SR_ALLOC();

    /* Enter critical section */
    OS_CRITICAL_ENTER();

    /* Time base counter++ */
    OSTickCtr++;(1)

    spoke    = (OS_TICK_SPOKE_IX)(OSTickCtr % OSCfg_TickWheelSize);(2)
    p_spoke  = &OSCfg_TickWheel[spoke];

    p_tcb    = p_spoke->FirstPtr;
    done     = DEF_FALSE;

    while (done == DEF_FALSE)
    {
        if (p_tcb != (OS_TCB *)0) (3)
        {
            p_tcb_next = p_tcb->TickNextPtr;

            p_tcb->TickRemain = p_tcb->TickCtrMatch - OSTickCtr;//If the linked list is not empty, decrease the tickmain of the first node.

            /* Node delay time expires */
            if (OSTickCtr == p_tcb->TickCtrMatch)//Judge whether the delay time of the first node is up. If it expires, make the task ready, that is, delete the task from the time base list and insert it into the ready list. These two steps are determined by the function OS_TaskRdy()
            {
                /* Make the task ready */
                OS_TaskRdy(p_tcb);
            }
            else (6)
            {
                /* If the delay period of the first node is not expired, exit the while loop
                Because the linked list is arranged in ascending order, if the delay period of the first node is not full, the following node must not be full */
                done = DEF_TRUE;
            }

            /* If the delay of the first node expires, continue to traverse the linked list to see if there are tasks whose delay expires
            If so, put it in place */
            p_tcb = p_tcb_next;(7)
        }
        else
        {
            done  = DEF_TRUE;(8)
        }
    }

    /* Exit critical section */
    OS_CRITICAL_EXIT();
}
//Calculate the index of the time base list to be scanned, and scan only one linked list at a time. There may be multiple linked lists in the time base list. Why only scan one of them? Because when the task inserts the time base list, the inserted index value is spoke_insert is to oscfg through / / TickCtrMatch_ The tick wheelsize is obtained from the remainder, and the index value that needs to be scanned now is spoke_ Update is to update oscfg through OSTickCtr_ The remainder of tickwheelsize shows that the value of TickCtrMatch is equal to OSTickCt plus TickRemain. Only / / after TickRemain clock cycles, spoke_ Only the value of update can be equal to stroke_ insert.  If the calculated stroke_ Update is less than poke_ Insert, and oscfg_ The tasks of the linked list under tickwheel [spoke_update] have not expired. Then / / the following tasks must not have expired. There is no need to continue scanning.

//For example, there are three TCBS in the time base list, the time base list oscfg_ Size of tickwheel [] OSCfg_TickWheelSize is equal to 12, and the current time base counter OSTickCtr is 7. Three tasks need to delay TickTemain=16, TickTemain=28 and / / TickTemain=40 clock cycles respectively. The TickRemain of the three tasks plus OSTickCtr can obtain that their TickCtrMatch is equal to 23, 35 and 47 respectively. The TickCtrMatch of the three tasks is equal to oscfg_ The value of tick wheelsize after the remainder operation is 11, so the TCBS of these three tasks will be inserted into oscfg_ For the same linked list under tickwheel [11], the node order is arranged in ascending order according to the value of TickCtrMatch. When the next SysTick clock cycle arrives, the OS will be called_ Ticklistupdate(), which / / when OSTickCtr is added with one, it is equal to 8 for oscfg_ The tickwheelsize (equal to 12) is summed to obtain the index value spoke to scan for updates_ Update, etc. 8, oscfg_ Scan the linked list under tickwheel [8]. You can get three TCBS from the time base list. / / you know that there are no nodes under the index 8, you can exit directly. The three TCBS just inserted are in oscfg_ The linked list under tickwheel [11] does not need to be scanned at all, because the time has just passed one clock cycle, which is far from reaching the delay time they need.
void  OS_TaskRdy (OS_TCB  *p_tcb)
{
    /* Delete from time base list */
    OS_TickListRemove(p_tcb);

    /* Insert ready list */
    OS_RdyListInsert(p_tcb);
}

2. Modify OSTimeDly() function

void  OSTimeDly(OS_TICK dly)
{
    CPU_SR_ALLOC();

    /* Enter critical zone */
    OS_CRITICAL_ENTER();
#if 0
    /* Set delay time */
    OSTCBCurPtr->TaskDelayTicks = dly;

    /* Remove from ready list */
    //OS_RdyListRemove(OSTCBCurPtr);
    OS_PrioRemove(OSTCBCurPtr->Prio);
#endif

    /* Insert time base list */
    OS_TickListInsert(OSTCBCurPtr, dly);

    /* Remove from ready list */
    OS_RdyListRemove(OSTCBCurPtr);

    /* Exit critical zone */
    OS_CRITICAL_EXIT();

    /* task scheduling  */
    OSSched();
}

3. Modify OSTimeTick() function

void  OSTimeTick (void)
{
#if 0
    unsigned int i;
    CPU_SR_ALLOC();

    /* Enter critical zone */
    OS_CRITICAL_ENTER();

    for (i=0; i<OS_CFG_PRIO_MAX; i++)
    {
        if (OSRdyList[i].HeadPtr->TaskDelayTicks > 0)
        {
            OSRdyList[i].HeadPtr->TaskDelayTicks --;
            if (OSRdyList[i].HeadPtr->TaskDelayTicks == 0)
            {
                /* A value of 0 indicates that the delay time is up and the task is ready */
                //OS_RdyListInsert (OSRdyList[i].HeadPtr);
                OS_PrioInsert(i);
            }
        }
    }

    /* Exit critical zone */
    OS_CRITICAL_EXIT();

#endif

    /* Update time base list */
    OS_TickListUpdate();

    /* task scheduling  */
    OSSched();
}

10, Implementation time slice

In this code example, the OS should support the function of multiple tasks under the same priority. These tasks can be assigned different time slices. When the task time slice is used up, the task will move from the head to the tail of the linked list, so that the next task can share the time slice, so as to cycle.

1. Implementation time slice

1.1. Modify task TCB

struct os_tcb {                        //In order to realize the time slice function, we need to add two time slice related variables to the task control block TCB first
    CPU_STK         *StkPtr;
    CPU_STK_SIZE    StkSize;

    /* Number of task delay cycles */
    OS_TICK         TaskDelayTicks;

    /* Task priority */
    OS_PRIO         Prio;

    /* Next pointer to the ready list bidirectional linked list */
    OS_TCB          *NextPtr;
    /* The previous pointer of the two-way linked list of the ready list */
    OS_TCB          *PrevPtr;

    /*Time base list related fields*/
    OS_TCB          *TickNextPtr;
    OS_TCB          *TickPrevPtr;
    OS_TICK_SPOKE   *TickSpokePtr;

    OS_TICK         TickCtrMatch;
    OS_TICK         TickRemain;

    /* Time slice related fields */
    OS_TICK              TimeQuanta;//(1) Indicates how many time slices a task needs, and the unit is the system clock cycle Tick.
    OS_TICK              TimeQuantaCtr;//(2) Indicates how many time slices are left in the task. Each time the system clock cycle arrives, TimeQuantaCtr will be reduced by one. When TimeQuantaCtr is equal to zero, it means that the time slice is used up. The TCB of the task will / / move from the head to the tail of the ready list list, so that the next task can share the time slice.
};

1.2. Implement time slice scheduling function


As shown in the figure, in a ready linked list, there are three tasks ready, of which there are two tasks under priority 2, both of which are assigned two time slices. If the time slice of task 3 has been used up, it is located at the end of the linked list, and if there is still one time slice of task 2, it is located at the head of the linked list. When the next clock cycle comes, the time slice of task 2 will be consumed, the corresponding TimeQuantaCtr will decrease to 0, the TCB of task 2 will be moved to the end of the linked list, and task 3 will become the head of the linked list. Then reset the time slice counter of task 3, the value of TimeQuantaCtr is 2, and enjoy the time slice again.

#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0U / / time slice is an optional function. Is it selected by the OS_CFG_SCHED_ROUND_ROBIN_EN control
void OS_SchedRoundRobin(OS_RDY_LIST  *p_rdy_list)
{
    OS_TCB   *p_tcb;
    CPU_SR_ALLOC();

    /*  Enter critical section */
    CPU_CRITICAL_ENTER();

    p_tcb = p_rdy_list->HeadPtr;//Get the first node of the linked list.

    /* If the TCB node is empty, exit */
    if (p_tcb == (OS_TCB *)0) //
    {
        CPU_CRITICAL_EXIT();
        return;
    }

    /* If it is an idle task, also exit */
    if (p_tcb == &OSIdleTaskTCB) {//(4)
        CPU_CRITICAL_EXIT();
        return;
    }

    /* Time slice self subtraction */
    if (p_tcb->TimeQuantaCtr > (OS_TICK)0) {//(5) If it is not an idle task, the time slice counter TimeQuantaCtr is decremented by one.
        p_tcb->TimeQuantaCtr--;
    }

    /* If the time slice is not used up, exit */
    if (p_tcb->TimeQuantaCtr > (OS_TICK)0) {//(6)
        CPU_CRITICAL_EXIT();
        return;
    }

    /* If there is only one task at the current priority, exit */
    if (p_rdy_list->NbrEntries < (OS_OBJ_QTY)2) {//(7)
        CPU_CRITICAL_EXIT();
        return;
    }

    /* When the time slice is exhausted, put the task to the last node of the linked list */
    OS_RdyListMoveHeadToTail(p_rdy_list);//(8)

    /* Retrieve task node */
    p_tcb = p_rdy_list->HeadPtr;//(9)
    /* Overloads the default time slice count value */
    p_tcb->TimeQuantaCtr = p_tcb->TimeQuanta;

    /* Exit critical section */
    CPU_CRITICAL_EXIT();
}
#endif/* OS_CFG_SCHED_ROUND_ROBIN_EN > 0u */

2. Modify OSTimeTick() function

The unit of the time slice of the task is updated at the time of the arrival of every system clock cycle, and the time slice scheduling function is called by the time base processing function OSTimeTick().

void  OSTimeTick (void)
{
    /* Update time base list */
    OS_TickListUpdate();

#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
    /* Time slice scheduling */
    OS_SchedRoundRobin(&OSRdyList[OSPrioCur]);
#endif

    /* task scheduling  */
    OSSched();
}

3. Modify OSTaskCreate() function

The time slice of the task is specified when the function is created

void OSTaskCreate (OS_TCB        *p_tcb,
                OS_TASK_PTR   p_task,
                void          *p_arg,
                OS_PRIO       prio,
                CPU_STK       *p_stk_base,
                CPU_STK_SIZE  stk_size,
                OS_TICK       time_quanta,//(1)
                OS_ERR        *p_err)
{
    CPU_STK       *p_sp;
    CPU_SR_ALLOC();

    /* Initialize TCB as default */
    OS_TaskInitTCB(p_tcb);

    /* Initialization stack */
    p_sp = OSTaskStkInit( p_task,
                        p_arg,
                        p_stk_base,
                        stk_size );

    p_tcb->Prio = prio;

    p_tcb->StkPtr = p_sp;
    p_tcb->StkSize = stk_size;

    /* Time slice correlation initialization */
    p_tcb->TimeQuanta    = time_quanta;//(2) Initialize the time slice variable TimeQuanta in the TCB field of the task. This variable indicates the maximum time slice that the task can enjoy. Once initialized, this value will not change unless it is considered to be modified.
#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
    p_tcb->TimeQuantaCtr = time_quanta;//(3) The value of the initialization time slice counter TimeQuantaCtr is equal to TimeQuanta. The value will decrease every system clock cycle. If the value is 0, it means that the time slice is consumed.
#endif

    /* Enter critical section */
    OS_CRITICAL_ENTER();

    /* Add task to ready list */
    OS_PrioInsert(p_tcb->Prio);
    OS_RdyListInsertTail(p_tcb);

    /* Exit critical section */
    OS_CRITICAL_EXIT();

    *p_err = OS_ERR_NONE;
}

4. Modify OS_IdleTaskInit() function

Because on OS_IdleTaskInit() function creates an idle task, so the function also needs to be modified. Just add a formal parameter of a time slice in the idle task creation function. We assign the time slice as 0, because there is only one task of the idle task under the priority of the idle task, and there are no other tasks

void  OS_IdleTaskInit(OS_ERR  *p_err)
{
    /* Initialize idle task counters */
    OSIdleTaskCtr = (OS_IDLE_CTR)0;

    /* Create idle task */
    OSTaskCreate( (OS_TCB     *)&OSIdleTaskTCB,
                (OS_TASK_PTR )OS_IdleTask,
                (void       *)0,
                (OS_PRIO)(OS_CFG_PRIO_MAX - 1u),
                (CPU_STK    *)OSCfg_IdleTaskStkBasePtr,
                (CPU_STK_SIZE)OSCfg_IdleTaskStkSize,
                (OS_TICK       )0,
                (OS_ERR     *)p_err );
}

5. main() function

int main(void)
{
    OS_ERR err;


    /* CPU Initialization: 1. Initialization timestamp */
    CPU_Init();

    /* Close interrupt */
    CPU_IntDis();

    /* Configure systick to interrupt once every 10ms */
    OS_CPU_SysTickInit (10);

    /* Initialize related global variables */
    OSInit(&err);

    /* Create task */
    OSTaskCreate( (OS_TCB       *)&Task1TCB,
                (OS_TASK_PTR   )Task1,
                (void         *)0,
                (OS_PRIO       )1,(1)
                (CPU_STK      *)&Task1Stk[0],
                (CPU_STK_SIZE  )TASK1_STK_SIZE,
                (OS_TICK       )0,(1)
                (OS_ERR       *)&err );

    OSTaskCreate( (OS_TCB       *)&Task2TCB,
                (OS_TASK_PTR   )Task2,
                (void         *)0,
                (OS_PRIO       )2,(2)
                (CPU_STK      *)&Task2Stk[0],
                (CPU_STK_SIZE  )TASK2_STK_SIZE,
                (OS_TICK       )1,(2)
                (OS_ERR       *)&err );

    OSTaskCreate( (OS_TCB       *)&Task3TCB,
                (OS_TASK_PTR   )Task3,
                (void         *)0,
                (OS_PRIO       )2,(2)
                (CPU_STK      *)&Task3Stk[0],
                (CPU_STK_SIZE  )TASK3_STK_SIZE,
                (OS_TICK       )1,(2)
                (OS_ERR       *)&err );

    /* Start the OS and will not return */
    OSStart(&err);
}

void Task1( void *p_arg )
{
    for ( ;; ) {
        flag1 = 1;
        OSTimeDly(2);
        flag1 = 0;
        OSTimeDly(2);
    }
}

void Task2( void *p_arg )
{
    for ( ;; ) {
        flag2 = 1;
        //OSTimeDly(1);(3)
        delay(0xff);
        flag2 = 0;
        //OSTimeDly(1);
        delay(0xff);
    }
}

void Task3( void *p_arg )
{
    for ( ;; ) {
        flag3 = 1;
        //OSTimeDly(1);(3)
        delay(0xff);
        flag3 = 0;
        //OSTimeDly(1);
        delay(0xff);
    }
}
//(1) : priority of task 1 is 1 and time slice is 0. The time slice function is only required when there are multiple tasks under the same priority.
//(2) : Task 2 and task 3 have the same priority, both of which are 2, and the same time slice is allocated, and the time slice can be different.
//(3) : because tasks 2 and 3 have the same priority, they are assigned the same time slice or different time slices, and the blocking delay can be replaced by software delay. Whether it is blocking delay or software delay, the delay time must be less than the time slice, because tasks with the same priority cannot exceed the time slice at most.

Code experiment phenomenon


As can be seen from the figure, in the two time slices where flag1 of task 1 is set to 1 and 0, tasks 2 and 3 run once respectively, and the running time is one time slice. In this time slice, the flag variables of tasks 2 and 3 flip many times, that is, the task runs many times.

Posted by devil_online on Tue, 30 Nov 2021 05:16:03 -0800