Task Management in FreeRTOS

1. Task Creation and Deletion

1. What is a Task

In FreeRTOS, a task is essentially a function, with the following prototype:

void ATaskFunction(void *pvParameters);

It is important to note that within the function, local variables should be used as much as possible. Each task has its own stack, meaning that when task A runs this function, its local variables are stored in task A’s stack, while task B’s local variables are stored in task B’s stack. Local variables of different tasks have their own copies. If global or static variables are used, there is only one copy: multiple tasks will share the same copy.

For example:

void ATaskFunction(void *pvParameters)
{
/* For different tasks, local variables are stored in the task's stack, each having their own copy */
int32_t lVariableExample = 0;

/* Task functions are typically implemented as an infinite loop */
for( ;; )
 {
/* Task code */
 }

/* If the program exits the loop, it must use vTaskDelete to delete itself
  * NULL indicates that it is deleting itself
  */
vTaskDelete( NULL );

/* The program should not reach here; if it does, an error has occurred */
}

2. Creating Tasks

The function used to create tasks is as follows:

/**
  * @brief  Function to create a task with dynamic memory allocation
  * @param  pvTaskCode: Task function
  * @param  pcName: Task name, used solely for debugging
  * @param  usStackDepth: Task stack depth, in words
  * @param  pvParameters: Task parameters
  * @param  uxPriority: Task priority
  * @param  pxCreatedTask: Task handle, can be used for deleting/suspending the task
  * @retval pdTRUE: Creation successful, errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY: Memory insufficient for creation
  */
BaseType_t xTaskCreate(TaskFunction_t pvTaskCode,
        const char * const pcName,
        unsigned short usStackDepth,
        void *pvParameters,
        UBaseType_t uxPriority,
        TaskHandle_t *pxCreatedTask);

/**
  * @brief  Function to create a task with static memory allocation
  * @param  pvTaskCode: Task function
  * @param  pcName: Task name
  * @param  usStackDepth: Task stack depth, in words
  * @param  pvParameters: Task parameters
  * @param  uxPriority: Task priority
  * @param  puxStackBuffer: Array for task stack space
  * @param  pxTaskBuffer: Storage space for task control block
  * @retval Handle of the created task
  */
TaskHandle_t xTaskCreateStatic(TaskFunction_t pvTaskCode,
          const char * const pcName,
          uint32_t ulStackDepth,
          void *pvParameters,
          UBaseType_t uxPriority,
          StackType_t * const puxStackBuffer,
          StaticTask_t * const pxTaskBuffer);

The two task creation functions differ in the following ways, and unless otherwise specified, dynamic memory allocation will be used for creating tasks or other instances:

  • <span>xTaskCreateStatic</span> requires the user to specify the task stack space array and the storage space for the task control block, while <span>xTaskCreate</span> dynamically allocates storage space for the task, requiring no user specification.
  • <span>xTaskCreateStatic</span> returns the handle of the successfully created task, while <span>xTaskCreate</span> requires the task handle to be defined and specified as a parameter, and its return value only indicates success or failure of task creation. Parameter descriptions:
Parameter Description
pvTaskCode Function pointer, can be simply considered as a C function for the task. It is somewhat special: it never exits, or if it does, it must call “vTaskDelete(NULL)”.
pcName Task name (for debugging only). Maximum length: <span>configMAX_TASK_NAME_LEN</span>
usStackDepth Each task has its own stack, and here the stack size is specified. The unit is words; for example, passing in 100 means a stack size of 100 words, which is 400 bytes. The maximum value is the maximum value of uint16_t. Determining the stack size is not easy; often it is an estimate. A precise method is to look at the disassembly.
pvParameters Parameters passed to the task function (passed via <span>pvTaskCode(pvParameters)</span><code><span>).</span>
uxPriority Task priority range: 0~(configMAX_PRIORITIES – 1) – the smaller the number, the lower the priority – exceeding the limit will automatically adjust to the upper limit (configMAX_PRIORITIES – 1).
pxCreatedTask Used to store the output result of xTaskCreate: task handle. If you want to operate on this task later, such as modifying its priority, you will need this handle. If you do not want to use this handle, you can pass NULL.
Return Value Success: pdPASS; Failure: errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY (the only failure reason is insufficient memory). Note: The documentation states that the return value on failure is pdFAIL, which is incorrect. pdFAIL is 0, while errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY is -1.

3. Deleting Tasks

The function used to delete tasks is as follows:

void vTaskDelete(TaskHandle_t xTaskToDelete);

Parameter description:

Parameter Description
pvTaskCode Task handle, which can be obtained when creating a task using xTaskCreate. NULL can also be passed, indicating to delete itself.

Examples:

  • Self-delete:<span>vTaskDelete(NULL)</span>
  • Deleted by another task: another task executes <span>vTaskDelete(pvTaskCode)</span>, where <span>pvTaskCode</span> is its own handle.
  • Kill another task: execute <span>vTaskDelete(pvTaskCode)</span>, where <span>pvTaskCode</span> is another task’s handle.

2. Task Priority and Tick

1. Task Priority

Higher priority tasks run first. The range of priority values is:<span>0~(configMAX_PRIORITIES – 1)</span>, with larger values indicating higher priority.

<span>FreeRTOS</span> scheduler can use <span>2</span> methods to quickly find the highest priority runnable task. The value of <span>configMAX_PRIORITIES</span> varies depending on the method used.

1. General Method

  • Implemented using <span>C</span> functions, the same code applies to all architectures. There are no restrictions on the value of <span>configMAX_PRIORITIES</span>. However, it is advisable to keep the value small, as larger values waste memory and time.
  • <span>configUSE_PORT_OPTIMISED_TASK_SELECTION</span> is defined as <span>0</span> or undefined, this method is used.

2. Architecture-Specific Optimized Method

  • Architecture-specific assembly instructions can quickly find the highest bit set to <span>1</span> from a <span>32</span>-bit number. Using these instructions allows for quickly identifying the highest priority runnable task. When using this method, the value of <span>configMAX_PRIORITIES</span> cannot exceed <span>32</span>.
  • <span>configUSE_PORT_OPTIMISED_TASK_SELECTION</span> is defined as <span>1</span> when this method is used.

In summary:

  • <span>FreeRTOS</span> ensures that the highest priority runnable task can execute immediately.
  • For runnable tasks of the same priority, they are executed in turn.

2. Tick

<span>FreeRTOS</span> also has a heartbeat, which uses a timer to generate interrupts at fixed intervals. This is called a <span>Tick</span>, for example, a clock interrupt occurs every <span>10ms</span>.

As shown in the figure: suppose <span>t1, t2, t3</span> occur as clock interrupts, the time between two interrupts is called a time slice <span>(time slice, tick period)</span>. The length of the time slice is determined by <span>configTICK_RATE_HZ</span>. Assuming <span>configTICK_RATE_HZ</span> is <span>100</span>, then the time slice length is <span>10ms</span>.

Task Management in FreeRTOS

How do tasks of the same priority switch? See the figure below:

Task 2 runs from <span>t1</span> to <span>t2</span>, at <span>t2</span> a <span>tick</span> interrupt occurs, entering the <span>tick</span> interrupt handler: selecting the next task to run. After executing the interrupt handler, it switches to the new task: task 1. Task 1 runs from <span>t2</span> to <span>t3</span>. As can be seen from the figure, the task running time does not strictly start from <span>t1, t2, t3</span>.

Task Management in FreeRTOS

With the concept of <span>Tick</span>, we can use <span>Tick</span> to measure time, for example:

vTaskDelay(2); // Wait for 2 ticks, assuming configTICK_RATE_HZ=100, Tick period is 10ms, wait for 20ms

// You can also use the pdMS_TO_TICKS macro to convert ms to ticks
vTaskDelay(pdMS_TO_TICKS(100)); // Wait for 100ms

Note that delays based on <span>Tick</span> are not precise; for example, the intention of <span>vTaskDelay(2)</span> is to delay for <span>2</span> tick periods, but it may return after just over <span>1</span> tick. As shown in the figure:

Task Management in FreeRTOS

When using the <span>vTaskDelay</span> function, it is recommended to use milliseconds as the unit, converting time to <span>Tick</span> using <span>pdMS_TO_TICKS</span>. This way, the code is independent of <span>configTICK_RATE_HZ</span>; even if the configuration changes, we do not need to modify the code.

3. Modifying Priority

We use <span>uxTaskPriorityGet</span> to obtain the priority of a task:

UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask );

Use the parameter <span>xTask</span> to specify the task; setting it to <span>NULL</span> indicates to get its own priority.

To set the priority of a task, use <span>vTaskPrioritySet</span>:

void vTaskPrioritySet( TaskHandle_t xTask,
        UBaseType_t uxNewPriority );
  • Use the parameter <span>xTask</span> to specify the task; setting it to <span>NULL</span> indicates to set its own priority;
  • The parameter <span>uxNewPriority</span> indicates the new priority, with a range of <span>0~(configMAX_PRIORITIES – 1)</span>.

3. Task States

1. Blocked State

In actual products, we do not let a task run continuously; instead, we use an “event-driven” approach to run it:

  • The task must wait for a certain event to occur before it can run.
  • During the waiting period, it does not consume CPU resources.
  • While waiting for an event, the task is in a blocked state (<span>Blocked</span>).

A blocked task can wait for two types of events:

  • Time-related events
    • Can wait for a period of time: I wait for 2 minutes.
    • Can also wait indefinitely until a specific absolute time: I wait until 3 PM.
  • Synchronization events: these events are generated by other tasks or interrupt programs.
    • Queues
    • Binary semaphores
    • Counting semaphores
    • Mutexes
    • Recursive mutexes
    • Event groups
    • Task notifications
    • Example 1: Task A waits for Task B to send it data.
    • Example 2: Task A waits for the user to press a button.
    • There are many sources of synchronization events (these concepts will be explained in detail later):

When waiting for a synchronization event, a timeout can be added. For example, waiting for data in a queue, with a timeout set to 10ms:

  • If data arrives within 10ms: success returns.
  • If 10ms passes and no data arrives: timeout returns.

2. Suspended State

<span>FreeRTOS</span> tasks can also enter a suspended state, the only way to do this is through the <span>vTaskSuspend</span> function. The function prototype is as follows:

void vTaskSuspend( TaskHandle_t xTaskToSuspend );

The parameter <span>xTaskToSuspend</span> indicates the task to be suspended; if NULL, it indicates to suspend itself.

To exit the suspended state, it can only be operated by others:

  • Called by another task:<span>vTaskResume</span>
  • Called by an interrupt program:<span>xTaskResumeFromISR</span>

In practical development, the suspended state is not often used.

3. Ready State

This task is fully prepared and can run at any time; it is just not its turn yet. At this point, it is in the ready state (Ready).

4. State Transitions

Task Management in FreeRTOS
  • Create Task –> Ready State: After task creation is complete, it enters the ready state, indicating that the task is ready to run, just waiting for the scheduler to schedule it.
  • Ready State → Running State: When a task switch occurs, the highest priority task in the ready list is executed, thus entering the running state.
  • Running State –> Ready State: If a higher priority task is created or resumed, a task scheduling occurs during the tick interrupt, at which point the highest priority task becomes the running state, and the previously running task transitions from running to ready state, still in the ready list, waiting for the highest priority task to finish running before continuing to run the original task (this can be seen as the CPU usage being preempted by a higher priority task).
  • Running State –> Blocked State: When a running task becomes blocked (suspended, delayed, waiting for a semaphore), it will be removed from the ready list, transitioning from running to blocked state, and then a task switch occurs, running the current highest priority task in the ready list.
  • Blocked State –> Ready State: When a blocked task is resumed (task resumed, timeout of delay, timeout of reading semaphore, or reading a semaphore), the resumed task will be added to the ready list, transitioning from blocked to ready state; if the priority of the resumed task is higher than that of the currently running task, a task switch will occur, transitioning that task from ready to running state.
  • Ready State –> Suspended State: A task can be suspended by calling the <span>vTaskSuspend()</span> function, which will suspend the task in the ready state; the suspended task will not get CPU usage and will not participate in scheduling unless it is resumed from the suspended state.
  • Blocked State –> Suspended State: Similarly, a task can be suspended by calling the <span>vTaskSuspend()</span> function, which will suspend the task in the blocked state.
  • Running State –> Suspended State: Similarly, a task can be suspended by calling the <span>vTaskSuspend()</span> function, which will suspend the task in the running state. In summary, regardless of the current task state, calling <span>vTaskSuspend()</span> will suspend the task.
  • Suspended State –> Ready State: The only way to resume a suspended task is to call <span>vTaskResume()</span> or <span>vTaskResumeFromISR()</span> function; if the priority of the resumed task is higher than that of the currently running task, a task switch will occur, transitioning that task from ready to running state.
/**
  * @brief  Query the current state of a task
  * @param  pxTask: Task handle to query, NULL queries itself
  * @retval Enumeration type of task state
  */
eTaskState eTaskGetState(TaskHandle_t pxTask);

/* Task state enumeration return values */
typedef enum
{
 eRunning = 0, /* The task is querying its own state, so it must be in the running state */
 eReady,   /* Ready state */
 eBlocked,  /* Blocked state */
 eSuspended,  /* Suspended state */
 eDeleted,  /* The task being queried has been deleted, but its TCB has not been released yet */
 eInvalid  /* Invalid state */
} eTaskState;

......

/**
  * @brief  Suspend a task
  * @param  pxTaskToSuspend: Handle of the task to be suspended, NULL to suspend itself
  * @retval None
  */
void vTaskSuspend(TaskHandle_t pxTaskToSuspend);

/**
  * @brief  Resume a task from suspended state
  * @param  pxTaskToResume: Handle of the task being resumed
  * @retval None
  */
void vTaskResume(TaskHandle_t pxTaskToResume);

/**
  * @brief  Interrupt-safe version of vTaskResume
  * @param  pxTaskToResume: Handle of the task being resumed
  * @retval Returns whether a context switch is needed before exiting the interrupt (pdTRUE/pdFALSE)
  */
BaseType_t xTaskResumeFromISR(TaskHandle_t pxTaskToResume);

4. Delay Functions

There are two Delay functions:

  • <span>vTaskDelay</span>: Must wait for at least the specified number of <span>Tick Interrupts</span> to become ready.
  • <span>vTaskDelayUntil</span>: Must wait until a specified absolute time to become ready. The prototypes of these two functions are as follows:
void vTaskDelay( const TickType_t xTicksToDelay ); /* xTicksToDelay: Wait for how many ticks */

/* pxPreviousWakeTime: The last wake-up time
 * xTimeIncrement: Block until (pxPreviousWakeTime + xTimeIncrement)
 * Both units are Tick Count
 */
BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime,
       const TickType_t xTimeIncrement );

The following diagram illustrates:

  • When using <span>vTaskDelay(n)</span>, the time interval for entering and exiting <span>vTaskDelay</span> is at least <span>n</span> Tick interrupts.
  • When using <span>xTaskDelayUntil(&Pre, n)</span>, the time interval between two exits of <span>xTaskDelayUntil</span> is at least <span>n</span> Tick interrupts.
    • When exiting <span>xTaskDelayUntil</span>, the task enters the ready state and generally gets a chance to execute.
    • Thus, <span>xTaskDelayUntil</span> can be used to allow a task to run periodically.
Task Management in FreeRTOS

5. Idle Task and Its Hook Functions

1. Introduction

A good program has tasks that are event-driven: most of the time, they are in a blocked state. It is possible that all tasks we create cannot execute, but the scheduler must find a runnable task: hence, we need to provide an idle task. When using <span>vTaskStartScheduler()</span> to create and start the scheduler, this function internally creates the idle task:

  • The idle task has a priority of 0: it cannot prevent user tasks from running.
  • The idle task is either in the ready state or in the running state, and will never be blocked.

The idle task’s priority of 0 means that once a user task becomes ready, the idle task is immediately switched out to allow the user task to run. In this case, we say the user task has “preempted” the idle task, which is implemented by the scheduler.

It is important to note: if you use <span>vTaskDelete()</span> to delete a task, you must ensure that the idle task has a chance to execute; otherwise, the memory of the deleted task cannot be released.

We can add an idle task hook function, which is called each time the idle task loop executes. The hook function serves the following purposes:

  • Execute some low-priority, background functions that need to run continuously.
  • Measure the system’s idle time: if the idle task can execute, it means all high-priority tasks have stopped, so measuring the time occupied by the idle task can calculate the processor utilization.
  • Allow the system to enter power-saving mode: if the idle task can execute, it means there is nothing important to do, so it can enter power-saving mode.

Limitations of the idle task hook function:

  • Cannot cause the idle task to enter a blocked or suspended state.
  • If you will use <span>vTaskDelete()</span> to delete tasks, the hook function must execute very efficiently. If the idle task gets stuck in the hook function, it cannot release memory.

2. Prerequisites for Using Hook Functions

In the task.c file:

  1. Define this macro as <span>1</span>: <span>configUSE_IDLE_HOOK</span>
  2. Implement the <span>vApplicationIdleHook</span> function.
Task Management in FreeRTOS

6. Scheduling Algorithms

The so-called scheduling algorithm determines which ready task can switch to the running state.

Configure the scheduling algorithm through two configuration items in the configuration file <span>FreeRTOSConfig.h</span>: <span>configUSE_PREEMPTION</span> and <span>configUSE_TIME_SLICING</span>.

There is also a third configuration item:<span>configUSE_TICKLESS_IDLE</span>, which is an advanced option used to turn off Tick interrupts for power saving.

The behavior of the scheduling algorithm is mainly reflected in two aspects: high-priority tasks run first, and how ready tasks of the same priority are selected. The scheduling algorithm must ensure that ready tasks of the same priority can run in a “round-robin” manner. Round-robin scheduling does not guarantee that the running time of tasks is fairly distributed; we can also refine the method of time allocation.

Understanding various scheduling algorithms from three perspectives:

  • Can it be preempted? Can high-priority tasks execute first (configuration item: <span>configUSE_PREEMPTION</span>)
    • When the current task is executing, if a higher priority task becomes ready, it cannot run immediately; it must wait for the current task to voluntarily yield CPU resources.
    • Other tasks of the same priority must also wait: higher priority tasks cannot preempt, and tasks of the same priority should be more compliant.
    • Yes: This is called “preemptive scheduling”; high-priority ready tasks execute immediately, which will be detailed later.
    • No: If it cannot be preempted, it can only be cooperative, known as “cooperative scheduling mode”.
  • Under the premise of preemption, do tasks of the same priority execute in turn (configuration item: <span>configUSE_TIME_SLICING</span>)
    • Round-robin execution: This is called “time-slice round-robin”; tasks of the same priority execute in turn, you execute one time slice, then I execute one time slice.
    • Non-round-robin execution: The current task will continue executing until it voluntarily yields or is preempted by a higher priority task.
  • Under the premise of “preemption” + “time-slice round-robin”, further refinement: does the idle task yield to user tasks (configuration item: <span>configIDLE_SHOULD_YIELD</span>)
    • The idle task is subordinate; each time it executes a loop, it checks whether to yield to user tasks.
    • The idle task executes like user tasks, everyone executes in turn, and no one is special. The list is as follows:
Configuration Item A B C D E
configUSE_PREEMPTION 1 1 1 1
configUSE_TIME_SLICING 1 1 0 0
configIDLE_SHOULD_YIELD 1 0 1 0
Description Commonly used Rarely used Rarely used Rarely used

Note:

  • A: Preemptive + time-slice round-robin + idle task yields.
  • B: Preemptive + time-slice round-robin + idle task does not yield.
  • C: Preemptive + non-time-slice round-robin + idle task yields.
  • D: Preemptive + non-time-slice round-robin + idle task does not yield.
  • E: Cooperative scheduling.
/**
  * @brief  Start the scheduler
  * @retval None
  */
void vTaskStartScheduler(void);
 
/**
  * @brief  Stop the scheduler
  * @retval None
  */
void vTaskEndScheduler(void);
 
/**
  * @brief  Suspend the scheduler
  * @retval None
  */
void vTaskSuspendAll(void);
 
/**
  * @brief  Resume the scheduler
  * @retval Returns whether a context switch is needed before exiting the interrupt (pdTRUE/pdFALSE)
  */
BaseType_t xTaskResumeAll(void);

In addition to task switching occurring due to time-slice round-robin or preemption by higher priority tasks, there are other scheduling methods, such as tasks voluntarily yielding the processor to other tasks, which will be detailed in the subsequent “Interrupt Management” chapter; here, we will briefly understand them as follows:

/**
  * @brief  Yield to another task of equal priority
  * @retval None
  */
void taskYIELD(void);
 
/**
  * @brief  Whether to perform a context switch when exiting ISR (assembly)
  * @param  xHigherPriorityTaskWoken: pdFALSE does not request a context switch, otherwise requests a context switch
  * @retval None
  */
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
 
/**
  * @brief  Whether to perform a context switch when exiting ISR (C language)
  * @param  xHigherPriorityTaskWoken: pdFALSE does not request a context switch, otherwise requests a context switch
  * @retval None
  */
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);

7. Utility Functions

There are many utility functions related to tasks; the official website lists a total of 23 API functions. Here, we will briefly introduce some commonly used API functions:

1. Get Task Information

/**
  * @brief  Get information about a task; configUSE_TRACE_FACILITY must be enabled (enabled by default)
  * @param  xTask: Task handle to query, NULL queries itself
  * @param  pxTaskStatus: Pointer to a TaskStatus_t structure to store task status information
  * @param  xGetFreeStackSpace: Whether to return the high water mark of stack space
  * @param  eState: Specify the state of the task when querying information; setting to eInvalid will automatically get the task state
  * @retval None
  */
void vTaskGetInfo(TaskHandle_t xTask,
      TaskStatus_t *pxTaskStatus,
      BaseType_t xGetFreeStackSpace,
      eTaskState eState);

/**
  * @brief  Get the current task handle
  * @retval Returns the current task handle
  */
TaskHandle_t xTaskGetCurrentTaskHandle(void);

/**
  * @brief  Get a task handle (long-running, not suitable for mass use)
  * @param  pcNameToQuery: Task name string to get the task handle
  * @retval Returns the handle of the specified queried task
  */
TaskHandle_t xTaskGetHandle(const char *pcNameToQuery);

/**
  * @brief  Get the idle task handle
  * @note: Must set INCLUDE_xTaskGetIdleTaskHandle to 1; cannot be adjusted in CubeMX, must be defined manually
  * @retval Returns the idle task handle
  */
TaskHandle_t xTaskGetIdleTaskHandle(void);

/**
  * @brief  Get the high water mark of a task (minimum available stack space size, in words)
  * @param  xTask: Handle of the task to get the high water mark, NULL queries itself
  * @retval 
  */
UBaseType_t uxTaskGetStackHighWaterMark(TaskHandle_t xTask);

/**
  * @brief  Get the task name string of a task
  * @param  xTaskToQuery: Handle of the task to get the name string, NULL queries itself
  * @retval Returns the task name string of a task
  */
char* pcTaskGetName(TaskHandle_t xTaskToQuery);

2. Get Kernel Information

/**
  * @brief  Get the status of all tasks in the system, returning an array of TaskStatus_t structures for each task
  * @param  pxTaskStatusArray: Pointer to the array, each member is of type TaskStatus_t, used to store the retrieved information
  * @param  uxArraySize: Set the number of members in the array pxTaskStatusArray
  * @param  pulTotalRunTime: Returns the total run time after FreeRTOS runs; NULL indicates not to return this data
  * @retval Returns the actual number of task information retrieved
  */
UBaseType_t uxTaskGetSystemState(TaskStatus_t * const pxTaskStatusArray,
         const UBaseType_t uxArraySize,
         unsigned long * const pulTotalRunTime);
 
/**
  * @brief  Returns the state of the scheduler
  * @retval 0: Suspended, 1: Not started, 2: Running
  */
BaseType_t xTaskGetSchedulerState(void);
 
/**
  * @brief  Get the total number of tasks currently managed by the kernel
  * @retval Returns the total number of tasks currently managed by the kernel
  */
UBaseType_t uxTaskGetNumberOfTasks(void);
 
/**
  * @brief  Get a string list of all tasks in the kernel
  * @param  pcWriteBuffer: Pointer to a character array to store the retrieved string information
  * @retval None
  */
vTaskList(char *pcWriteBuffer);

3. Other Functions

/**
  * @brief  Get the tag value of a task
  * @param  xTask: Handle of the task to get the tag value, NULL indicates to get its own tag value
  * @retval Returns the tag value of the task
  */
TaskHookFunction_t xTaskGetApplicationTaskTag(TaskHandle_t xTask); 
 
/**
  * @brief  Interrupt-safe version of getting a task's tag value
  */
TaskHookFunction_t xTaskGetApplicationTaskTagFromISR(TaskHandle_t xTask);
 
/**
  * @brief  Set a task's tag value, the tag value is stored in the task control block
  * @param  xTask: Handle of the task to set the tag value, NULL indicates to set itself
  * @param  pxTagValue: The tag value to set
  * @retval None
  */
vTaskSetApplicationTaskTag(TaskHandle_t xTask, 
        TaskHookFunction_t pxTagValue);

Original text:https://blog.csdn.net/Teminator_/article/details/141347112

The source of this article is from the internet, and the copyright belongs to the original author. If there is any infringement, please contact for deletion.

Leave a Comment