0-1write MC/OS __Basics3

 2021-07-23 09:40:45 Friday

stay μ In C/OS-III, after a task is created, the TCB of the task will be placed in the ready list, indicating that the task is ready and may be run at any time. The ready list includes a priority table indicating task priority and a TCB bidirectional linked list storing task TCB.

1. Priority table

At the code level, the priority table is an array

```/* Define priority table */
CPU_DATA   OSPrioTbl[OS_PRIO_TBL_SIZE];
```

#define OS_PRIO_TBL_SIZE((OS_CFG_PRIO_MAX - 1u) / (DEF_INT_CPU_NBR_BITS) + 1u)；// OS_ CFG_ PRIO_ Max indicates how many priorities are supported, def_ INT_ CPU_ NBR_ Bits defines how many bits the CPU integer data has

OS_ PRIO_ TBL_ The value of size is 1, that is, the priority table only needs one member to represent 32 priorities. If you want to support 64 priorities, you need two members

How does the priority table relate to the priority of the task?

Each member type in the priority table is 32 bits, which can represent 32 priorities.
If you create a task with priority 3, set bit [31-3] to 1. Bit 31 represents the task with the highest priority, which decreases, and bit 0 represents the lowest priority.

1.1 explanation of priority table function

• OS_PrioInit()

```/* Initialize priority table */
void OS_PrioInit( void )
{
CPU_DATA i;

/* All are initialized to 0 by default */
for ( i=0u; i<OS_PRIO_TBL_SIZE; i++ )
{
OSPrioTbl[i] = (CPU_DATA)0;
}
}
```
• OS_PrioInsert()

```/* Set the corresponding bit in the priority table */
void  OS_PrioInsert (OS_PRIO  prio)
{
CPU_DATA  bit;
CPU_DATA  bit_nbr;
OS_PRIO   ix;

/* Modulo operation to obtain the subscript index of the priority table array */
ix             = prio / DEF_INT_CPU_NBR_BITS;//Locate which member of the priority table array corresponds to the priority of prio. If prio=3, then ix=0;

/* The remainder operation limits the priority to def_ INT_ CPU_ NBR_ Within bits */
bit_nbr        = (CPU_DATA)prio & (DEF_INT_CPU_NBR_BITS - 1u);

/* Gets the position of the corresponding bit of the priority in the priority table */
bit            = 1u;
bit          <<= (DEF_INT_CPU_NBR_BITS - 1u) - bit_nbr;//Assuming that prio is equal to 3, bit is equal to 28

/* Place the priority in the corresponding position 1 in the priority table */
OSPrioTbl[ix] |= bit;
}
```
• OS_PrioRemove()

```/* Clear the corresponding bit in the priority table */
void  OS_PrioRemove (OS_PRIO  prio)
{
CPU_DATA  bit;
CPU_DATA  bit_nbr;
OS_PRIO   ix;

/* Modulo operation to obtain the subscript index of the priority table array */
ix             = prio / DEF_INT_CPU_NBR_BITS;

/* The remainder operation limits the priority to def_ INT_ CPU_ NBR_ Within bits */
bit_nbr        = (CPU_DATA)prio & (DEF_INT_CPU_NBR_BITS - 1u);

/* Gets the position of the corresponding bit of the priority in the priority table */
bit            = 1u;
bit          <<= (DEF_INT_CPU_NBR_BITS - 1u) - bit_nbr;

/* Clear the corresponding bit of priority in the priority table */
OSPrioTbl[ix] &= ~bit;
}
```
• OS_PrioGetHighest()

```/* Get the highest priority */
OS_PRIO  OS_PrioGetHighest (void)
{
CPU_DATA  *p_tbl;
OS_PRIO    prio;

prio  = (OS_PRIO)0;
p_tbl = &OSPrioTbl[0];//Search the entire priority table from scratch until the highest priority is found.

/* An array member with a value other than 0 was found */
while (*p_tbl == (CPU_DATA)0)
{
prio += DEF_INT_CPU_NBR_BITS;
p_tbl++;
}

/* Find the highest priority set in the priority table */
return (prio);
}
```

The way to find the first bit set to 1 in order from high to low is by calculating the leading 0 function_ Cntleadzeros(). Starting from the high order to find 1 is called the calculation leading 0, and starting from the low order to find 1 is called the calculation leading 0.
Call CPU_CntLeadZeros() can calculate that there are three zeros in front of the first bit set to 1 in OSPrioTbl[0], so this 3 is the highest priority we want to find. We don't care how many position 1 are left behind. We just need to find the first 1.
CPU_ The cntleadzeros() function can be implemented by assembly or C. if the processor used supports the leading zero instruction CLZ, it can be implemented by assembly to speed up the instruction operation. If not, it can be implemented by C.
Cortex-M series processors come with CLZ instructions, so the CPU_ The cntleadzeros() function is written by assembly by default.

The TCB of the task ready to run will be placed in the ready list, and the system can schedule the task to run at any time. The ready list is an OS at the code level_ RDY_ List data type array OSRdyList [], the size of the array is determined by the macro OS_CFG_PRIO_MAX determines how many priorities are supported and how many members OSRdyList [] has. The priority of the task corresponds to the index of OSRdyList [] one by one. For example, the TCB of the task of priority 3 will be placed in OSRdyList[3].

```/* Ready list definition */
OS_EXT    OS_RDY_LIST    OSRdyList[OS_CFG_PRIO_MAX];
```
```typedefstruct  os_rdy_list         OS_RDY_LIST;

struct os_rdy_list {
OS_TCB        *TailPtr;
OS_OBJ_QTY    NbrEntries;//Indicates how many tasks there are under the same index in OSRdyList [].
};
//The members of OSRdyList [] correspond to the priority of tasks one by one. Multiple tasks with the same priority will exist under the same index of OSRdyList [] in the form of a two-way linked list. Then HeadPtr is used to point to the head node of the linked list and TailPtr is used to point to the tail node of the linked list. / / the address of the index member under the priority is called the root node of the two-way linked list under the priority, Knowing the address of the root node can find each node under the linked list.
```

2.1 explanation of ready list function

Before implementing the ready list related functions, we need to set the OS in the structure_ Add three members Prio, NextPtr and PrevPtr to TCB, and then define two global variables OSPrioCur and OSPrioHighRdy in os.h

```struct os_tcb {
CPU_STK         *StkPtr;
CPU_STK_SIZE    StkSize;

/* Number of task delay cycles */

OS_PRIO         Prio;

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

/* Defined in os.h */
OS_EXT    OS_PRIO  OSPrioCur;       /* Current priority */
OS_EXT    OS_PRIO  OSPrioHighRdy;   /* Highest priority */
```
• OS_RdyListInit()

```void OS_RdyListInit(void)//Initialize the ready list OSRdyList [] to null
{
OS_PRIO i;
OS_RDY_LIST *p_rdy_list;

/* Loop initialization, all members are initialized to 0 */
for ( i=0u; i<OS_CFG_PRIO_MAX; i++ ) {
p_rdy_list = &OSRdyList[i];
p_rdy_list->NbrEntries = (OS_OBJ_QTY)0;
p_rdy_list->TailPtr = (OS_TCB *)0;
}
}
```

```void  OS_RdyListInsertHead (OS_TCB  *p_tcb)
{
OS_RDY_LIST  *p_rdy_list;
OS_TCB       *p_tcb2;

/* Get the root of the linked list */
p_rdy_list = &OSRdyList[p_tcb->Prio];

/* CASE 0: The linked list is empty */
if (p_rdy_list->NbrEntries == (OS_OBJ_QTY)0) {
p_rdy_list->NbrEntries =  (OS_OBJ_QTY)1;
p_tcb->NextPtr         =  (OS_TCB   *)0;
p_tcb->PrevPtr         =  (OS_TCB   *)0;
p_rdy_list->TailPtr    =  p_tcb;
}
else {
p_rdy_list->NbrEntries++;
p_tcb->PrevPtr         = (OS_TCB    *)0;
p_tcb2->PrevPtr        = p_tcb;
}
}
```
• OS_RdyListInsertTail()

```void  OS_RdyListInsertTail (OS_TCB  *p_tcb)
{
OS_RDY_LIST  *p_rdy_list;
OS_TCB       *p_tcb2;

/* Get the root of the linked list */
p_rdy_list = &OSRdyList[p_tcb->Prio];

/* CASE 0: The linked list is empty */
if (p_rdy_list->NbrEntries == (OS_OBJ_QTY)0) {
p_rdy_list->NbrEntries  = (OS_OBJ_QTY)1;
p_tcb->NextPtr          = (OS_TCB   *)0;
p_tcb->PrevPtr          = (OS_TCB   *)0;
p_rdy_list->TailPtr     = p_tcb;
}
else {
p_rdy_list->NbrEntries++;
p_tcb->NextPtr          = (OS_TCB   *)0;
p_tcb2                  = p_rdy_list->TailPtr;
p_tcb->PrevPtr          = p_tcb2;
p_tcb2->NextPtr         = p_tcb;
p_rdy_list->TailPtr     = p_tcb;
}
}
```
• OS_RdyListInsert()

OS_RdyListInsert() is used to insert the TCB of the task into the ready list. The insertion is divided into two steps. The first step is to insert the corresponding position bits in the priority table according to the priority, which calls the OS_ The second step is to put the TCB of the task into OSRdyList [priority] according to the priority. If the priority is equal to the current priority, insert the tail of the linked list, otherwise insert the head of the linked list

```/* Insert a TCB into the ready linked list */
void  OS_RdyListInsert (OS_TCB  *p_tcb)
{
/* Insert priority into priority table */
OS_PrioInsert(p_tcb->Prio);

if (p_tcb->Prio == OSPrioCur)
{
/* If it is the current priority, insert the tail of the linked list */
OS_RdyListInsertTail(p_tcb);
}
else
{
}
}
```

It is used to move nodes from the head to the tail of the linked list. There are four situations when moving. The first is that the linked list is empty and there is nothing to do; The second is that the linked list has only one node and has nothing to do; The third is that the linked list has only two nodes; The fourth is that the linked list has more than two nodes

```void  OS_RdyListMoveHeadToTail (OS_RDY_LIST  *p_rdy_list)
{
OS_TCB  *p_tcb1;
OS_TCB  *p_tcb2;
OS_TCB  *p_tcb3;

switch (p_rdy_list->NbrEntries) {
case 0:
case 1:
break;

case 2:
p_tcb2              = p_rdy_list->TailPtr;
p_tcb1->PrevPtr     = p_tcb2;
p_tcb1->NextPtr     = (OS_TCB *)0;
p_tcb2->PrevPtr     = (OS_TCB *)0;
p_tcb2->NextPtr     = p_tcb1;
p_rdy_list->TailPtr = p_tcb1;
break;

default:
p_tcb2              = p_rdy_list->TailPtr;
p_tcb3              = p_tcb1->NextPtr;
p_tcb3->PrevPtr     = (OS_TCB *)0;
p_tcb1->NextPtr     = (OS_TCB *)0;
p_tcb1->PrevPtr     = p_tcb2;
p_tcb2->NextPtr     = p_tcb1;
p_rdy_list->TailPtr = p_tcb1;
break;
}
}
```
• OS_RdyListRemove()

It is used to remove a node from the linked list. The removal can be divided into three cases. The first is that the linked list is empty and there is nothing to do; The second is that the linked list has only one node; The third is that the linked list has more than two nodes

```void  OS_RdyListRemove (OS_TCB  *p_tcb)
{
OS_RDY_LIST  *p_rdy_list;
OS_TCB       *p_tcb1;
OS_TCB       *p_tcb2;

p_rdy_list = &OSRdyList[p_tcb->Prio];

/* Save the previous and subsequent nodes of the TCB node to be deleted */
p_tcb1     = p_tcb->PrevPtr;
p_tcb2     = p_tcb->NextPtr;

/* The TCB node to be removed is the first node in the linked list */
if (p_tcb1 == (OS_TCB *)0)
{
/* And there is only one node in the linked list */
if (p_tcb2 == (OS_TCB *)0)
{
/* All root nodes are initialized to 0 */
p_rdy_list->NbrEntries = (OS_OBJ_QTY)0;
p_rdy_list->TailPtr    = (OS_TCB   *)0;

/* Clear the corresponding bit in the priority table */
OS_PrioRemove(p_tcb->Prio);
}
/* There is more than one node in the linked list */
else
{
/* Node minus 1 */
p_rdy_list->NbrEntries--;
p_tcb2->PrevPtr        = (OS_TCB   *)0;
}
}
/* The TCB node to be removed is not the first node in the linked list */
else
{
p_rdy_list->NbrEntries--;
p_tcb1->NextPtr = p_tcb2;

/* If the next node of the node to be deleted is 0, that is, the node to be deleted is the last node */
if (p_tcb2 == (OS_TCB *)0)
{
p_rdy_list->TailPtr = p_tcb1;
}
else
{
p_tcb2->PrevPtr     = p_tcb1;
}
}

/* Reset the PrevPtr and NextPtr pointers of the TCB deleted from the ready list */
p_tcb->PrevPtr = (OS_TCB *)0;
p_tcb->NextPtr = (OS_TCB *)0;
}
```

8, Support multi priority

Prior to this, the OS only supported switching between two tasks, and the priority has not yet been reached,
Now start adding the priority function to the task. The lower the digital priority, the higher the logical priority.

1. Define priority related global variables

```/* Current priority */
OS_EXT            OS_PRIO                OSPrioCur;
/* Highest priority */
OS_EXT            OS_PRIO                OSPrioHighRdy;
```

2. Modify OSInit() function

```void OSInit (OS_ERR *p_err)
{
/* Configure the initial status of the OS to stop */
OSRunning =  OS_STATE_OS_STOPPED;

/* Initialize two global TCB S, which are used for task switching */
OSTCBCurPtr = (OS_TCB *)0;
OSTCBHighRdyPtr = (OS_TCB *)0;

/* Initialize priority variable */
OSPrioCur                       = (OS_PRIO)0;     //Initialize all priority related variables newly added in 1 in OSInit() function
OSPrioHighRdy                   = (OS_PRIO)0;

/* Initialize priority table */
OS_PrioInit();

OS_RdyListInit();

if (*p_err != OS_ERR_NONE) {
return;
}
}
```

3. Modify task control block TCB

```struct os_tcb {
CPU_STK         *StkPtr;
CPU_STK_SIZE    StkSize;

/* Number of task delay cycles */

OS_PRIO         Prio;     //In the task control block, add the priority field prio. The data type is OS_PRIO is an 8-bit integer after macro expansion, so only 255 priorities are supported.

OS_TCB          *NextPtr;
/* The previous pointer of the two-way linked list of the ready list */
OS_TCB          *PrevPtr;
};
```

```void OSTaskCreate (OS_TCB        *p_tcb,
void          *p_arg,
CPU_STK       *p_stk_base,
CPU_STK_SIZE  stk_size,
OS_ERR        *p_err)
{
CPU_STK       *p_sp;
CPU_SR_ALLOC();//Define a local variable to store the interrupt status before the CPU is shut down, because the code of adding the task to the ready list next belongs to the critical short code and needs to shut down the interrupt.

/* Initialize TCB as default */

/* Initialization stack */
p_arg,
p_stk_base,
stk_size );

p_tcb->Prio = prio;//Save the priority transmitted from the formal parameter to the priority field of the task control block TCB.

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

/* Enter critical section */
OS_CRITICAL_ENTER();

/* Adding the task to the ready list needs to be divided into two steps: 1. Set the corresponding position in the priority table according to the priority; 2. Put the task TCB in the OSRdyList [priority]. If there are multiple tasks in the same priority, the TCB of these tasks will be put in the OSRdyList [priority] to form a two-way linked list.*/
OS_PrioInsert(p_tcb->Prio);
OS_RdyListInsertTail(p_tcb);

/* Exit critical section */
OS_CRITICAL_EXIT();(7)

*p_err = OS_ERR_NONE;
}
```

```/* Idle task initialization */
{
/* Initialize idle task counters */

(void       *)0,
(OS_PRIO)(OS_CFG_PRIO_MAX - 1u),//Set the lowest priority for idle tasks
(OS_ERR     *)p_err );
}
```

6. Modify the OSStart() function

After adding the priority, the OSStart() function needs to be modified. The specific task to run first is determined by the priority

```/* Start RTOS and will not return */
void OSStart (OS_ERR *p_err)
{
if ( OSRunning == OS_STATE_OS_STOPPED ) {
#if 0
/* Manually configure task 1 to run first */
#endif
/* Find the highest priority */
OSPrioHighRdy   = OS_PrioGetHighest();//(1)
OSPrioCur       = OSPrioHighRdy;

/* The highest priority TCB was found */
OSTCBCurPtr     = OSTCBHighRdyPtr;

/* Mark OS to start running */
OSRunning       = OS_STATE_OS_RUNNING;

/* Start task switching and will not return */
OSStartHighRdy();

/* It will not run here. Running here indicates that a fatal error has occurred */
*p_err = OS_ERR_FATAL_RETURN;
} else {
*p_err = OS_STATE_OS_RUNNING;
}
}
```

7. Modify PendSV_Handler() function

PendSV_ Priority related code is added to the handler() function

```;*******************************************************************
;                          PendSVHandler abnormal
;*******************************************************************

OS_CPU_PendSVHandler_nosave

; OSPrioCur   = OSPrioHighRdy
LDR     R0, =OSPrioCur
LDR     R1, =OSPrioHighRdy
LDRB    R2, [R1]
STRB    R2, [R0]

; OSTCBCurPtr = OSTCBHighRdyPtr
LDR     R0, = OSTCBCurPtr
LDR     R1, = OSTCBHighRdyPtr
LDR     R2, [R1]
STR     R2, [R0]

LDR     R0, [R2]
LDMIA   R0!, {R4-R11}

MSR     PSP, R0
ORR     LR, LR, #0x04
CPSIE   I
BX      LR

NOP

ENDP
```

8. Modify OSTimeDly() function

After the task calls the OSTimeDly() function, the task is in a blocking state and needs to be removed from the ready list

```/* Blocking delay */
void  OSTimeDly(OS_TICK dly)
{
#if 0
/* Set delay time */

OSSched();
#endif

CPU_SR_ALLOC();//(1) A local variable is defined to store the interrupt status before the CPU is shut down. This code is a critical short code and needs to be shut down because the task is removed from the ready list next.

/* Enter critical zone */
OS_CRITICAL_ENTER();//(2)

/* Set delay time */

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

/* Exit critical zone */
OS_CRITICAL_EXIT();//(4)

OSSched();
}
//Remove the task from the ready list. Here, you only need to clear the corresponding bit of the task in the priority table. It is not necessary to remove the task TCB from OSRdyList [] for the time being, because the OSTimeTick() function still scans OSRdyList [] to determine whether the delay time of the task expires. After adding the time base list, when the task calls the OSTimeDly() function to delay, the TCB of the task can be deleted from the ready list, and then the TCB of the task can be inserted into the time base list. The OSTimeTick() function judges whether the delay of the task expires by scanning the time base list, which is implemented in the next code example.
```

9. Modify the OSSched() function

The task scheduling function OSSched() is no longer the previous two tasks switching in turn. It needs to be scheduled according to priority

```void OSSched(void)
{
```

CPU_SR_ALLOC();//(1)

```/* Enter critical zone */
OS_CRITICAL_ENTER();//(2)

/* Find tasks with the highest priority */
OSPrioHighRdy   = OS_PrioGetHighest();

/* If the task with the highest priority is the current task, return directly without task switching */
if (OSTCBHighRdyPtr == OSTCBCurPtr)
{
/* Exit critical zone */
OS_CRITICAL_EXIT();

return;
}
/* Exit critical zone */
OS_CRITICAL_EXIT();//(5)

```

}

10. Modify OSTimeTick() function

OSTimeTick() function is called in SysTick interrupt service function. It is a periodic function. It is specifically used to scan the ready list OSRdyList [], judge whether the delay time of the task expires, and if so, place the task in the corresponding position in the priority table

```void  OSTimeTick (void)
{
unsigned int i;
CPU_SR_ALLOC();//(1)

/* Enter critical zone */
OS_CRITICAL_ENTER();//(2)

/* Scan the TaskDelayTicks of all tasks in the ready list. If it is not 0, subtract 1 */
#if 0
for (i=0; i<OS_CFG_PRIO_MAX; i++)
{
{
}
}
#endif

for (i=0; i<OS_CFG_PRIO_MAX; i++) //(3)
{
{
{
/* A value of 0 indicates that the delay time is up and the task is ready */
OS_PrioInsert(i);
}
}
}

/* Exit critical zone */
OS_CRITICAL_EXIT();//(4)

OSSched();
}
```

11. 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);//(1)

(void *)0,
(OS_PRIO)1,//(2)
(OS_ERR *)&err );

(void *)0,
(OS_PRIO)2,//(3)
(OS_ERR *)&err );

(void *)0,
(OS_PRIO)3,//(4)
(OS_ERR *)&err );
#if 0
#endif

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

/*
*******************************************************************
*                              Function implementation
*******************************************************************
*/
/* Software delay */
void delay (uint32_t count)
{
for (; count!=0; count--);
}

{
for ( ;; ) {
flag1 = 1;
OSTimeDly(2);
flag1 = 0;
OSTimeDly(2);
}
}

{
for ( ;; ) {
flag2 = 1;
OSTimeDly(2);
flag2 = 0;
OSTimeDly(2);
}
}