[FreeRTOS learning plan] section 7 supports multi priority

Objectives of this section

Prior to this section, FreeRTOS did not support multi priority and only supported switching between two tasks. Starting from this chapter, we began to add priority function to tasks. In FreeRTOS, the smaller the digital priority, the smaller the logical priority, which is different from the RT thread and μ C/OS is just the opposite.

How to support multi priority

The ready list pxreadytaskslists [configmax_priorities] is an array. The TCB of the ready task is stored in the array (exactly the xStateListItem list item in the TCB). The subscript of the array corresponds to the priority of the task. The lower the priority, the smaller the subscript of the array. Idle tasks have the lowest priority, corresponding to the list with subscript 0.

There are two tasks ready in the ready list in the figure. It shows that there are two tasks ready in the ready list, with priorities of 1 and 2 respectively. The idle tasks are not drawn, and the idle tasks will be ready since the system is started, because the system must ensure that at least one task can run.

When a task is created, it will be inserted into different positions in the ready list according to the priority of the task. Tasks with the same priority are inserted into the same list in the ready list, which is the support time slice we will explain in the next section.

pxCurrenTCB is a global TCB pointer, which is used to point to the TCB of the highest priority ready task, that is, the currently running TCB. In order to make the task support priority, we just need to make pxCurrenTCB point to the TCB of the ready task with the highest priority during task switching. In the previous chapter, we manually make pxCurrenTCB rotate in task 1, task 2 and idle tasks, Now we need to change pxCurrenTCB to point to the TCB of the ready task with the highest priority when switching tasks. The key to the problem is: if the TCB of the ready task with the highest priority is found.

FreeRTOS provides two methods, one is general and the other is optimized according to specific processors. Next, let's learn about these two methods.

Find the code related to the highest priority ready task

Find the highest priority ready task related code in task Defined in C

/* ①Finding the highest priority ready task: a common approach */                                    
#if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 )
	/* ②uxTopReadyPriority Stored is the highest priority of the ready task */
	#define taskRECORD_READY_PRIORITY( uxPriority )														\
	{																									\
		if( ( uxPriority ) > uxTopReadyPriority )														\
		{																								\
			uxTopReadyPriority = ( uxPriority );														\
		}																								\
	} /* taskRECORD_READY_PRIORITY */

	/*-----------------------------------------------------------*/
	
	#define taskSELECT_HIGHEST_PRIORITY_TASK()/*③*/															\
	{																								\
	UBaseType_t uxTopPriority = uxTopReadyPriority;/*④*/														\
																										\
		/* ⑤Find the queue with the highest priority for the ready task */                                                          \
		while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) )							\
		{																								\
			--uxTopPriority;																			\
		}																								\
																										\
		/* ⑥Get the TCB of the highest priority ready task, and then update it to pxCurrentTCB */							            \
		listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) );			\
		/* ⑦Update uxTopReadyPriority */                                                                    \
		uxTopReadyPriority = uxTopPriority;																\
	} /* taskSELECT_HIGHEST_PRIORITY_TASK */

	/*-----------------------------------------------------------*/

	/* These two macro definitions are only used when selecting the optimization method. The definition here is empty */
	#define taskRESET_READY_PRIORITY( uxPriority )
	#define portRESET_READY_PRIORITY( uxPriority, uxTopReadyPriority )
    
/* ⑧Find the highest priority ready task: a method optimized according to the processor architecture */
#else /* configUSE_PORT_OPTIMISED_TASK_SELECTION */

	#define taskRECORD_READY_PRIORITY( uxPriority )	portRECORD_READY_PRIORITY( uxPriority, uxTopReadyPriority )//⑨

	/*-----------------------------------------------------------*/
	
	#define taskSELECT_HIGHEST_PRIORITY_TASK()	/*⑩	*/												    \
	{																								    \
	UBaseType_t uxTopPriority;																		    \
																									    \
		/* (11)Find the highest priority */								                            \
		portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority );								    \
		/* Get the TCB of the highest priority ready task, and then update it to pxCurrentTCB */                                       \
		listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) );	/*(12)*/	    \
	} /* taskSELECT_HIGHEST_PRIORITY_TASK() */

	/*-----------------------------------------------------------*/
#if 0
	#define taskRESET_ READY_ Priority (uxpriority) / * Note*/ 														\
	{																									\
		if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ ( uxPriority ) ] ) ) == ( UBaseType_t ) 0 )	\
		{																								\
			portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) );							\
		}																								\
	}
#else
    #define taskRESET_READY_PRIORITY( uxPriority )/*(13)*/											            \
    {																							        \
            portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) );					        \
    }
#endif
    
#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */

① There are two methods to find the ready task with the highest priority, which are specified by configure_ PORT_ OPTIMISED_ TASK_ The macro control of selection is defined as 0 to select the general method and 1 to select the method optimized according to the processor. By default, the macro is in portmacro H is defined as 1, that is, the optimized method is used, but we also understand the general method.

General method

taskRECORD_READY_PRIORITY()

②taskRECORD_READY_PRIORITY() is used to update the value of uxTopReadyPriority. uxTopReadyPriority is a task The static variable defined in C is used to represent the highest priority of the created task. It is initialized to 0 by default, that is, the priority of idle tasks.

/* Idle task priority macro definition, in task Defined in H */
#define tskIDLE_PRIORITY ( ( UBaseType_t ) 0U )

/* Define uxTopReadyPriority in task Defined in C */
staticvolatile UBaseType_t uxTopReadyPriority = tskIDLE_PRIORITY;

taskSELECT_HIGHEST_PRIORITY_TASK()

③taskSELECT_HIGHEST_PRIORITY_TASK() is used to find the ready task with the highest priority. In essence, it updates the values of uxTopReadyPriority and pxCurrentTCB.

④ The value of uxTopReadyPriority is temporarily stored in the local variable uxTopPriority, which needs to be used next.

⑤ Start from the subscript of the ready list array corresponding to the highest priority to find out whether a task exists in the current list. If not, uxTopPriority will be reduced by one to continue to find out whether a task exists in the list corresponding to the next priority. If so, the while loop will jump out to show that the ready task with the highest priority has been found. The reason why you can search from the highest priority down is that the priority of the task corresponds to the subscript of the ready list one by one. The higher the priority, the greater the subscript of the corresponding ready list array.

⑥ Get the TCB of the highest priority ready task and update it to pxCurrentTCB.

⑦ Update the value of uxTopPriority to uxTopReadyPriority.

optimization method

⑧ The optimization method is due to the fact that the Cortex-M kernel has an instruction CLZ to calculate the leading zero. The so-called leading zero is to calculate the number of zeros in front of a variable (the variable of the Cortex-M kernel MCU is 32 bits) starting from the high bit and appearing 1 bit for the first time. For example, for a 32-bit variable uxTopReadyPriority, its bits 0, 24 and 25 are all set to 1, and the other bits are 0. For details, see. Then use the leading zero instruction__ CLZ(uxTopReadyPriority) can quickly calculate that the number of leading zeros of uxTopReadyPriority is 6.


If each tag number of uxTopReadyPriority corresponds to the priority of the task, when the task is ready, the corresponding position will be 1; otherwise, it will be cleared. Then the figure shows that priority 0, priority 24 and priority 25 are ready, and the task with priority 25 has the highest priority. Using the leading zero calculation instruction, the highest priority in the ready task can be quickly calculated as:
( 31UL - (uint32_t ) __clz( ( uxReadyPriorities ) ) ) = ( 31UL - ( uint32_t ) 6 )=25.

taskRECORD_READY_PRIORITY()

⑨taskRECORD_READY_PRIORITY() is used to set a position of the variable uxTopReadyPriority to 1 according to the passed in formal parameter (usually the priority of the task). uxTopReadyPriority is a task The static variable defined in C is initialized to 0 by default. Different from the highest priority used to represent the created task in the general method, it plays the role of a priority bit chart in the optimization method, that is, each bit of the variable corresponds to the priority of the task. If the task is ready, the corresponding position 1 will be cleared, and vice versa. According to this principle, you only need to calculate the leading zeros of uxTopReadyPriority, even if you find the highest priority of the ready task. With taskRECORD_READY_PRIORITY() has the opposite effect: taskRESET_READY_PRIORITY().

taskRECORD_READY_PRIORITY()taskRESET_READY_PRIORITY() (defined in portmacro.h)

/* Set / clear the corresponding bit in the priority bitmap according to the priority */
	#define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) )
	#define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) )
	

taskRESET_READY_PRIORITY()

(13)taskRESET_READY_PRIORITY() is used to clear a bit of the variable uxTopReadyPriority according to the passed in formal parameter (usually the priority of the task).

(note): taskreset is actually called according to priority_ READY_ When the priority() function resets the corresponding bit in the uxTopReadyPriorit variable, first ensure that there are no tasks in the ready list for the linked list under the corresponding priority. However, the blocking delay scheme we currently implement is implemented by scanning the delay variable xTicksToDelay of TCB in the ready list. The delay list has not been implemented separately (the task delay list will be understood in the next section), so the task cannot be removed from the ready list temporarily when the task is not ready, Instead, simply clear the corresponding bit of the task priority in the variable uxTopReadyPriority. After we implement the task delay list in the next chapter, when the task is not ready, it will not only clear the corresponding bit of the task priority in the variable uxTopReadyPriority, but also drop the task and delete it from the ready list.

taskSELECT_HIGHEST_PRIORITY_TASK()

(10)taskSELECT_HIGHEST_PRIORITY_TASK() is used to find the ready task with the highest priority. In essence, it updates the values of uxTopReadyPriority and pxCurrentTCB.

(11) Find the highest priority according to the value of uxTopReadyPriority, and then update it to the local variable uxTopPriority.

portGET_HIGHEST_PRIORITY() is now portmacro Defined in H.

#define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31UL - ( uint32_t ) __clz( ( uxReadyPriorities ) ) )

(12) According to the value of uxTopPriority, find the TCB of the task with the highest priority from the ready list, and then update the TCB to pxCurrentTCB.

Modify the code to support multi priority

Next, we continue to iteratively modify the code in the previous section to achieve multi priority.

Modify task control block

Add priority related members to the task control block

typedef struct tskTaskControlBlock
{
  volatile StackType_t  *pxTopOfStack;                         /* Stack top */ 
	ListItem_t						xStateListItem;                        /* List items for tasks */
	StackType_t           *pxStack;                              /* Start address of task stack */
	char                  pcTaskName[ configMAX_TASK_NAME_LEN ]; /* Task name, in string form */
	TickType_t					  xTicksToDelay;                         /* For delay */
	UBaseType_t           uxPriority;
} tskTCB;
typedef tskTCB TCB_t;

Modify the xtask createstatic() function

TaskHandle_t xTaskCreateStatic(	TaskFunction_t pxTaskCode,						/* Task entry */
																const char * const pcName,						/* Task name, in string form */
																const uint32_t ulStackDepth,					/* Task stack size, in words */
																void * const pvParameters,						/* Task parameters */
																UBaseType_t  uxPriority,							/* ①Task priority. The higher the value, the higher the priority */	
																StackType_t * const puxStackBuffer,		/* Start address of task stack */
																TCB_t * const pxTaskBuffer)						/* Task control block pointer */
{
	TaskHandle_t xReturn; //The task handle is used to point to the TCB of the task
	TCB_t *pxNewTCB;      //Initialize xNewTCB to TCB structure
	
	//The start address of the task stack and the parameters of the task control block are passed into NewTCB
	if( (pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ))
	{
	  pxNewTCB = (TCB_t * ) pxTaskBuffer;
		pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer;
		
		/* ②Create a new task */
	  prvInitialiseNewTask(	pxTaskCode,			/* Task entry */
							pcName,				/* Task name, in string form */
							ulStackDepth,		/* Task stack size, in words */
							pvParameters,		/* Task parameters */
							uxPriority,
							&xReturn,			/* task handle  */
							pxNewTCB);
		
		/* ③Add task to ready list */
		prvAddNewTaskToReadyList( pxNewTCB );
	}
	else
	{
	  xReturn = NULL;
	}
	
	/* Returns the task handle. If the task is created successfully, xReturn should point to the task control block */
  return xReturn;
}

① Increase the priority parameter. The higher the value, the higher the priority.

prvInitialiseNewTask() function

② : modify prvInitialiseNewTask() function and add priority formal parameters and priority initialization related codes.

static void prvInitialiseNewTask(	TaskFunction_t pxTaskCode,					/* Task entry */
                                    const char * const pcName,					/* Task name, in string form */
									const uint32_t ulStackDepth,				/* Task stack size, in words */
									void * const pvParameters,					/* Task parameters */
									UBaseType_t uxPriority,						/* Task priority. The higher the value, the higher the priority */
									TaskHandle_t * const pxCreatedTask,     	/* task handle  */
									TCB_t *pxNewTCB )										/* Task control block pointer */
{
	
  StackType_t *pxTopOfStack;//Define task stack top data type
	UBaseType_t x;            //Define a ubasetype_ Tdata auxiliary value x, which is used in the length of the task name
	
	/* Get stack top address */
	pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
	/* Align 8 bytes down */
	pxTopOfStack = ( StackType_t * )( ( ( uint32_t ) pxTopOfStack ) &(~( ( uint32_t ) 0x0007 ) ) );
	
	/* Store the name of the task in TCB */
	for( x = ( UBaseType_t ) 0; x < (UBaseType_t) configMAX_TASK_NAME_LEN; x++ )
	{
	  pxNewTCB->pcTaskName[ x ] = pcName[ x ];
		
		if( pcName[ x ] == 0x00 )
		{
		  break;
		}
	}
	
	/* The length of the task name cannot exceed configMAX_TASK_NAME_LEN */
	pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] ='\0';
	
	/* Initialize xStateListItem list item in TCB */
	vListInitialiseItem( &(pxNewTCB->xStateListItem ));
	
	/* Sets the owner of the xStateListItem list item */
	listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );
	
  /* Initialization priority */
  if ( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) 
	{
		uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
	}
		pxNewTCB->uxPriority = uxPriority;
	
	/* Initialize task stack */
	pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); 
	
	/* Make the task handle point to the task control block */
	if( ( void * ) pxCreatedTask != NULL )
	{
	  *pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
	}
}


prvAddNewTaskToReadyList() function

③ Add the function prvaddnewtask toreadylist() to add a task to the ready list, which is in task C.

static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
{
	/* Enter critical section */
	taskENTER_CRITICAL();
	{
		/* ①Global task timer plus one operation */
        uxCurrentNumberOfTasks++;
        
        /* ②If pxCurrentTCB is empty, point pxCurrentTCB to the newly created task */
		if( pxCurrentTCB == NULL )
		{
			pxCurrentTCB = pxNewTCB;

			/* ③If you are creating a task for the first time, you need to initialize the list related to the task */
            if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
			{
				/* Initialize task related lists */
                prvInitialiseTaskLists();
			}
		}
		else /* ④If pxCurrentTCB is not empty, point pxCurrentTCB to the TCB of the highest priority task according to the priority of the task */
		{
				if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
				{
					pxCurrentTCB = pxNewTCB;
				}
		}
		uxTaskNumber++;
        
		/* ⑤Add task to ready list */
        prvAddTaskToReadyList( pxNewTCB );

	}
	/* Exit critical section */
	taskEXIT_CRITICAL();
}

① Global task timer uxCurrentNumberOfTasks plus one operation. uxCurrentNumberOfTasks is a task The static variable defined in C is initialized to 0 by default.

② If pxCurrentTCB is empty, point pxCurrentTCB to the newly created task. pxCurrentTCB is a task The global pointer defined by C is used to point to the task control block of the task currently running or about to run. The default initialization is NULL.

③ If you are creating a task for the first time, you need to call the function prvinitializetasklists() to initialize the list related to the task. At present, only the ready list needs to be initialized. This function is in task C.

/* Initialize task related lists */
void prvInitialiseTaskLists( void )
{
  UBaseType_t uxPriority;
	
	for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++)
	{
	  vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );
	}
}

④ If pxCurrentTCB is not empty, it indicates that a task already exists. Then point pxCurrentTCB to the TCB of the highest priority task according to the priority of the task. When creating a task, always point pxCurrentTCB to the TCB of the highest priority task.

⑤ Add a task to the ready list. prvAddTaskToReadyList() is a macro with parameters, which is defined in task C.

#define prvAddTaskToReadyList( pxTCB )																   \
	taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority );	/*①*/											   \
	vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), /*②*/&( ( pxTCB )->xStateListItem ) ); \

① Set the priority bit to the corresponding position bit in the chart uxTopReadyPriority according to the priority.

② Insert tasks into the ready list according to priority pxReadyTasksLists [].

Modify vTaskStartScheduler() function

void vTaskStartScheduler( void )
{
/*======================================Create idle task start==============================================*/     
    TCB_t *pxIdleTaskTCBBuffer = NULL;               /* Used to point to idle task control block */
    StackType_t *pxIdleTaskStackBuffer = NULL;       /* Starting address of idle task stack */
    uint32_t ulIdleTaskStackSize;
    
    /* Get the memory of idle tasks: task stack and task TCB */
    vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, 
                                   &pxIdleTaskStackBuffer, 
                                   &ulIdleTaskStackSize );    
    
    xIdleTaskHandle = xTaskCreateStatic(	(TaskFunction_t)prvIdleTask,              /* Task entry */
											(char *)"IDLE",                           /* Task name, in string form */
											(uint32_t)ulIdleTaskStackSize ,           /* Task stack size, in words */
											(void *) NULL,                            /* Task parameters */
											(UBaseType_t) tskIDLE_PRIORITY,           /* Task priority. The higher the value, the higher the priority */
											(StackType_t *)pxIdleTaskStackBuffer,     /* Start address of task stack */
											(TCB_t *)pxIdleTaskTCBBuffer );
		
//    /*② Add task to ready list*/                                 
//    vListInsertEnd( &( pxReadyTasksLists[0] ), &( ((TCB_t *)pxIdleTaskTCBBuffer)->xStateListItem ) );
///*======================================Create idle task end================================================*/
//                                         
//    /*③ Manually specify the first task to run*/
//    pxCurrentTCB = &Task1TCB;
                                         
    /* Initialize system time base counter */
    xTickCount = ( TickType_t ) 0U;
    
    /* Start scheduler */
    if( xPortStartScheduler() != pdFALSE )
    {
        /* If the scheduler is started successfully, it will not return, that is, it will not come here */
    }
}

① When creating an idle task, the priority is configured as tskIDLE_PRIORITY, the macro is in task H, which is 0 by default, indicating that the priority of idle tasks is the lowest.

② We have just modified the create task function xTaskCreateStatic(). When creating a task, the task has been added to the ready list, which will be commented out here.

③ In the newly modified create task function xTaskCreateStatic(), the function prvAddNewTaskToReadyList() to add tasks to the ready list is added, which will be commented out here.

Modify vtask delay() function

The modification of vtask delay() function is to add the operation of removing the task from the ready list.

void vTaskDelay( const TickType_t xTicksToDelay )
{
    TCB_t *pxTCB = NULL;
    
    /* Get TCB of current task */
    pxTCB = pxCurrentTCB;
    
    /* Set delay time */
    pxTCB->xTicksToDelay = xTicksToDelay;
	
		/* Remove task from ready list */
    //uxListRemove( &( pxTCB->xStateListItem ) );
    taskRESET_READY_PRIORITY( pxTCB->uxPriority );//be careful
    
    /* Task switching */
    taskYIELD();
}

(note): removing a task from the ready list should have completed two operations: one is to remove the task from the ready list, which is implemented by the function uxListRemove(); According to the priority of another function, readpriority will be cleared to zero in the chart_ READY_ Priority(). However, since our current time base update function xtask incrementtick still needs to scan the tasks in the ready list to determine whether the delay time of the task expires, we can't remove the task from the ready list. In the next chapter "implementation of task delay list", we will add a delay list. When the delay occurs, in addition to clearing the corresponding bits in the priority bit chart uxTopReadyPriority according to the priority, we also need to remove the task from the ready list.

Modify vTaskSwitchContext() function

In the new task switching function vTaskSwitchContext(), instead of manually switching the pxCurrentTCB pointer between task 1, task 2 and idle tasks, the function taskselect is called directly_ HIGHEST_ PRIORITY_ Task() finds the TCB of the ready task with the highest priority and updates it to pxCurrentTCB.

/* Task switching is to find the ready task with the highest priority */
void vTaskSwitchContext( void )
{
	/* Get the TCB of the highest priority ready task, and then update it to pxCurrentTCB */
    taskSELECT_HIGHEST_PRIORITY_TASK();
}

#else

void vTaskSwitchContext( void )
{
	/* If the current thread is idle, try to execute thread 1 or thread 2,
       See if their delay time ends. If the thread's delay time does not expire,
       Then return to continue executing the idle thread */
	if( pxCurrentTCB == &IdleTaskTCB )
	{
		if(Task1TCB.xTicksToDelay == 0)
		{            
            pxCurrentTCB =&Task1TCB;
		}
		else if(Task2TCB.xTicksToDelay == 0)
		{
            pxCurrentTCB =&Task2TCB;
		}
		else
		{
			return;		/* If the thread delay has not expired, return and continue to execute the idle thread */
		} 
	}
	else
	{
		/*If the current thread is thread 1 or thread 2, check another thread. If the other thread is not in the delay, switch to this thread
        Otherwise, judge whether the current thread should enter the delay state. If so, switch to the idle thread. Otherwise, there will be no switching */
		if(pxCurrentTCB == &Task1TCB)
		{
			if(Task2TCB.xTicksToDelay == 0)
			{
                pxCurrentTCB =&Task2TCB;
			}
			else if(pxCurrentTCB->xTicksToDelay != 0)
			{
                pxCurrentTCB = &IdleTaskTCB;
			}
			else 
			{
				return;		/* Return and do not switch because both threads are in delay */
			}
		}
		else if(pxCurrentTCB == &Task2TCB)
		{
			if(Task1TCB.xTicksToDelay == 0)
			{
                pxCurrentTCB =&Task1TCB;
			}
			else if(pxCurrentTCB->xTicksToDelay != 0)
			{
                pxCurrentTCB = &IdleTaskTCB;
			}
			else 
			{
				return;		/* Return and do not switch because both threads are in delay */
			}
		}
	}
}

#endif

Modify the xtask incrementtick() function

Modify the xtask incrementtick() function, that is, add the code to make the task ready when the task delay time expires.

void xTaskIncrementTick( void )
{
    TCB_t *pxTCB = NULL;
    BaseType_t i = 0;
    
    /* Update the system time base counter xTickCount, which is a Global variables defined in C */
    const TickType_t xConstTickCount = xTickCount + 1;
    xTickCount = xConstTickCount;

    
    /* Scan the xTicksToDelay of all threads in the ready list. If it is not 0, subtract 1 */
	for(i=0; i<configMAX_PRIORITIES; i++)
	{
        pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &pxReadyTasksLists[i] ) );
		if(pxTCB->xTicksToDelay > 0)
		{
			pxTCB->xTicksToDelay --;
			/* ①When the delay time is up, the task is ready */
      if( pxTCB->xTicksToDelay ==0 )
       {
          taskRECORD_READY_PRIORITY( pxTCB->uxPriority );
       }
		}
	}
    
    /* Task switching */
    portYIELD();
}

① (increase) when the delay time is up, the task is ready. That is, the corresponding position bits in the priority bit chart uxTopReadyPriority are set according to the priority. In the context switching function vTaskSwitchContext() just modified, the highest priority of ready tasks is found through the priority bit chart uxTopReadyPriority.

main function

The main function in this section is basically the same as that in the previous section, with little modification.

int main(void)
{
	
	/* Hardware initialization */
	/* Put the hardware related initialization here. If it is software simulation, there is no relevant initialization code */
	
	
	/* Create task */
	Task1_Handle = xTaskCreateStatic( ( TaskFunction_t )Task1_Entry,
	                                  (char *)"Task1",
									  (uint32_t)TASK1_STACK_SIZE,
									  (void *)NULL,
									  (UBaseType_t) 1,  //①             
									  (StackType_t *)Task1Stack,
									  (TCB_t *)&Task1TCB);/
//	/*② Add task to ready list*/ 
//	vListInsertEnd( &( pxReadyTasksLists[1]), &( ((TCB_t *)(&Task1TCB))->xStateListItem ) );

	Task2_Handle = xTaskCreateStatic( ( TaskFunction_t )Task2_Entry,
	                                  (char *)"Task2",
									  (uint32_t)TASK2_STACK_SIZE,
									  (void *)NULL,
									  (UBaseType_t) 2,//③
									  (StackType_t *)Task2Stack,
									  (TCB_t *)&Task2TCB);
//	/*④ Add task to ready list*/ 
//	vListInsertEnd( &( pxReadyTasksLists[2]), &( ((TCB_t *)(&Task2TCB))->xStateListItem ) );
  
	/* Start the scheduler and start multi task scheduling. If it is started successfully, it will not be returned */
    vTaskStartScheduler();
	for(;;)
	{
			/*Don't do anything*/
	}

}

① And ③ set the priority of the task. The higher the digital priority, the higher the logical priority.

② And ④ are deleted because in the task creation function xtask createstatic(), the function prvaddnewtasktoredylist() has been called to insert the task into the ready list.

Experimental phenomenon

Enter the software debugging and run the program at full speed. From the logic analyzer, you can see that the waveforms of the two tasks are completely synchronized, just like the CPU doing two things at the same time.

Reference: FreeRTOS Kernel Implementation and application development practice - based on RT1052

Tags: C stm32 FreeRTOS

Posted by saito on Tue, 01 Mar 2022 18:37:22 +1030