FreeRTOS task management, task status, task priority

Task status

Running state

Tasks are divided into running state and non-running state. During the operation of the processor, only one task can be in the running state at any time , and the non-running state contains many sub-states.

When the task is in the non-running state, the task is in the dormant state, and its state is saved so that the next time the scheduler decides that it should enter the running state. When a task resumes execution, it starts execution from the instructions that will be executed before leaving the running state for the last time.

Insert picture description here

The FreeRTOS scheduler is the only entity that can switch task states.

Non-operational state

Blocking state

The task waiting for the event is in the blocking state, and the task will enter the blocking state due to waiting for two different events

  1. Time-related events-This type of event is either a delayed relative time or an absolute time to arrive. For example, the task may enter the blocking state and wait for 10ms to pass
  2. Synchronous event-this type of event originates from another task or interrupt

In FreeRTOS, message queues, binary semaphores, counting semaphores, mutually exclusive semaphores, recursive mutually exclusive semaphores, time stamp groups, and task notifications will all generate synchronization events

Suspended

The scheduler cannot use tasks that are in a suspended state. The only way to enter the suspended state is by calling the vTaskSuspend() API function, and the way to release the suspended state is by calling the vTaskResume() or xTaskResumeFromISR() API function. Most applications do not use the suspended state

Ready state

If the task is neither in the running state, nor in the blocking state or suspended state, it is considered as the ready state. These tasks are able to run, so they are ready to run, but they are not running at this time

Insert picture description here
Create a task to the ready state : After the task is created, it enters the ready state, indicating that the task is ready, and it can run only after the scheduler is scheduled.

Ready state to running state : When a task switch occurs in the scheduler, the highest priority task in the ready list is executed first and enters the running state

Run state to ready state : After creating or restoring a higher priority task, the task will be scheduled. At this time, the task with the highest priority in the ready list will become running. Then, the original running task will change from the running state to the ready state and be in the ready list. After the higher priority task runs, continue to run the original task.

Running state to blocking state : When the running task is blocked (suspended, delayed, read semaphore waiting), the task will be deleted from the ready list, the task state will change from the running state to the blocking state, and then the task switch will occur. The highest priority task currently in the run ready list.

Blocking state to ready state : After the blocked task is restored (task recovery, delay time timeout, read semaphore timeout or read semaphore, etc.) at this time the restored task will be added to the ready list, thus changing from the blocked state to Ready state; if the priority of the restored task is higher than the priority of the running task at this time, a task switch will occur, and the task will switch the task state again from the ready state to the running state.

Ready state, blocking state, running state to suspended state : Tasks can suspend tasks in any state by calling the vTaskSuspend() API function. Suspended tasks cannot get the right to use the CPU or participate in scheduling. , Unless it is released from the suspended state.

Suspended state to ready state : The only way to restore a suspended task is to call the VTaskResume() or vTaskResumeFromISR() API function. If the priority of the resumed task is higher than the priority of the running task at this time, it will When a task switch occurs, the task will switch the task state again, from the ready state to the running state.

Create task

xTaskCreate () API

BaseType_t xTaskCreate( TaskFunction_t pvTaskCode,
					  const char * const pcName,
					  uint16_t usStackDepth,
					  void *pvParameters,
					  UBaseType_t uxPriority,
					  TaskHandle_t *pxCreatedTask );
Parameters Name Description
pvTaskCode The parameter is a pointer to the function that implements the task (in fact, the function name)
pcName Human-defined task name (the macro definition configMAX_TASK_NAME_LEN in FreeRTOS.h defines the maximum length of the task name string, if it exceeds this length, it will be truncated)
usStackDepth This parameter is the depth of the task stack space (when the task is created, the kernel allocates its own stack space), the unit is Words (words), not Bytes (bytes), such as 32-bit wide stack space, usStackDepth=100, this stack space is allocated 400 bytes (100 * 4 =400Bytes), the size of stack depth * stack width must not exceed the maximum value of unit_16 type variables
pvParameters The value (void * type) passed to the task function, or NULL if no parameters are passed
uxPriority Task priority, range: 0-----(configMAX_PRIORITIES – 1)
pxCreatedTask Task handle, can be used to change task priority or delete tasks, etc. If the task handle is not used, it can be set to NULL
Returned value Return pdPASS, indicating that the task was created successfully, and return pdFALL, indicating that the creation failed, which may be due to insufficient stack space

Routine

void vTask1( void *pvParameters )  //任务函数1
{
    
    
	const char *pcTaskName = "Task 1 is running\r\n";
	volatile uint32_t ul; /* volatile to ensure ul is not optimized away. */
	/* As per most tasks, this task is implemented in an infinite loop. */
	for( ;; )
	{
    
    
		/* Print out the name of this task. */
		vPrintString( pcTaskName );
		/* Delay for a period. */
		for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
		{
    
    
		}
	}
}

void vTask2( void *pvParameters )  //任务函数2
{
    
    
	const char *pcTaskName = "Task 2 is running\r\n";
	volatile uint32_t ul; /* volatile to ensure ul is not optimized away. */
	/* As per most tasks, this task is implemented in an infinite loop. */
	for( ;; )
	{
    
    
		/* Print out the name of this task. */
		vPrintString( pcTaskName );
		/* Delay for a period. */
		for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
		{
    
    
		}
	}
}

int main( void )
{
    
    
	xTaskCreate( vTask1, /* Pointer to the function that implements the task. */
				"Task 1",/* Text name for the task. This is to facilitatedebugging only. */
				1000, /* Stack depth - small microcontrollers will use muchless stack than this. */
				NULL, /* This example does not use the task parameter. */
				1, /* This task will run at priority 1. */
				NULL ); /* This example does not use the task handle. */
	/* Create the other task in exactly the same way and at the same priority. */
	xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, NULL );
	vTaskStartScheduler();  //开启任务调度器

	for( ;; );
}

Create task within task

In one task contains another task, Task2 task can be created in Task1 task function.

void vTask1( void *pvParameters )
{
    
    
	const char *pcTaskName = "Task 1 is running\r\n";
	volatile uint32_t ul; /* volatile to ensure ul is not optimized away. */
	/*在进入死循环前,创建任务2*/		
	xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, NULL );
	for( ;; )
	{
    
    
		/* Print out the name of this task. */
		vPrintString( pcTaskName );
		/* Delay for a period. */
		for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
		{
    
    
		}
	}
}

Task parameters

Use task function parameters to pass the text to be printed

void vTask1( void *pvParameters )
{
    
    
	const char *pcTaskName = "Task 1 is running\r\n";
	volatile uint32_t ul; /* volatile to ensure ul is not optimized away. */
	xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, NULL );
	for( ;; )
	{
    
    
		/* Print out the name of this task. */
		vPrintString( pcTaskName );
		/* Delay for a period. */
		for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
		{
    
    
		}
	}
}

static const char *pcTextForTask1 = "Task 1 is running\r\n";
static const char *pcTextForTask2 = "Task 2 is running\r\n";
int main( void )
{
    
    
	/* Create one of the two tasks. */
	xTaskCreate( vTaskFunction, /*任务函数名 */
				"Task 1", /* 任务名 */
				1000, /* 任务堆栈空间深度*/
				(void*)pcTextForTask1, /* 使用任务参数传递要打印的文本 */
				1, /* 任务优先级 */
				NULL ); /*此处未用任务句柄 */
	xTaskCreate( vTaskFunction, "Task 2", 1000, (void*)pcTextForTask2, 1, NULL );
	
	vTaskStartScheduler();  //开启任务调度

	for( ;; );
}

Task priority

The xTaskCreate() API function defines an initial priority for the task. This priority can be modified using the vTaskPrioritySet() API function after the scheduler runs

The value range of task priority is 0 ---- (configMAX_PRIORITIES – 1) , but different tasks can use a common task priority

configMAX_PRIORITIES is defined in FreeRTOS.h

The maximum value of configMAX_PRIORITIES depends on the method used:

  1. Generic Method

When using this method, FreeRTOS does not limit the maximum value that configMAX_PRIORITIES can set. However, it is always wise to automatically keep the value of configMAX_PRIORITIES small, because the higher its value, the more RAM will be consumed, and the worst-case execution time will be longer

If configUSE_PORT_OPTIMISED_TASK_SELECTION is set to 0 in the FreeRTOSConfig.h header file, then the generic method will be used, or configUSE_PORT_OPTIMISED_TASK_SELECTION is not defined in the header file, or the generic method is the only method provided by the FreeRTOS port in use

  1. Architecture Optimized Method

The architecture optimization method uses a small amount of assembly code, which is faster than the Generic Method method. If you use the architecture optimization method, then configMAX_PRIORITIES cannot be greater than 32. As with the general method, it is recommended to keep configMAX_PRIORITIES at the necessary minimum, because the higher its value, the more RAM will be consumed.

If configUSE_PORT_OPTIMISED_TASK_SELECTION is set to 1 in FreeRTOSConfig.h, then the method of architecture optimization will be used

The FreeRTOS scheduler will always ensure that the highest priority task that can run is the task that enters the running state. When multiple tasks of the same priority can run, the scheduler will turn each task into or out of the running state in turn

Guess you like

Origin blog.csdn.net/weixin_44333597/article/details/107848257