FREERTOS¶
TASK CREATION¶
xTaskCreatePinnedToCore¶
Function prototype
BaseType_t xTaskCreatePinnedToCore(TaskFunction_t pxTaskCode, // Task function pointer
const char * const pcName, // Task name
const uint32_t usStackDepth, // Task stack size
void * const pvParameters, // Task parameters
UBaseType_t uxPriority, // Task priority
TaskHandle_t * const pxCreatedTask, // Task handle
const BaseType_t xCoreID); // Core ID
Function introduction
In ESP-IDF (ESP32 Development Framework), xTaskCreatePinnedToCore
is a function used to create a task and pin it to a specified CPU core. ESP32 is a dual-core microcontroller (ESP32-S series is single-core) with two processing cores: Core 0 and Core 1. This function helps developers pin tasks to specific cores for better CPU load management and real-time performance management.
Parameters
pxTaskCode
: Task function pointer. It points to the function to be executed in the new task, which is usually defined in the format ofvoid function_name(void *pvParameters)
, wherepvParameters
are the parameters passed to the task.pcName
: The name string of the task, usually used for debugging and monitoring. You can use a string name that is easy to identify.usStackDepth
: The size of the task stack in words (4 bytes). The stack size should be set according to the stack space required in the task.pvParameters
: The parameter pointer passed to the task function. If the task function does not require parameters, it can be set toNULL
.uxPriority
: The priority of the task. The larger the value, the higher the priority. The range of priority that can be set in ESP-IDF is 0 to 24.pxCreatedTask
: The pointer to the task handle, used to receive the handle of the created task. If a task handle is not needed, it can be set toNULL
.xCoreID
:0
: bound to core 0 (primary core).1
: bound to core 1.tskNO_AFFINITY
: not bound to a specific core, allowing FreeRTOS to schedule the task on any core.
Return Value
pdPASS
: Task creation was successful.pdFAIL
: Task creation failed.
Example
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// Define task function
void myTask(void *pvParameters)
{
while(1)
{
printf("Hello from myTask on Core 0\n");
vTaskDelay(pdMS_TO_TICKS(1000)); // Delay 1 second
}
}
void app_main(void)
{
// Create a task and pin it to core 0
xTaskCreatePinnedToCore(
myTask, // Task function
"MyTask", // Task name
2048, // Task stack size
NULL, // Task parameters
5, // Task priority
NULL, // Task handle
0 // Core ID (0 means bound to core 0)
);
}
xTaskCreateStaticPinnedToCore¶
Function prototype
TaskHandle_t xTaskCreateStaticPinnedToCore(TaskFunction_t pxTaskCode, // Task function pointer
const char * const pcName, // Task name
const uint32_t ulStackDepth, // Task stack size
void * const pvParameters, // Task parameters
UBaseType_t uxPriority, // Task priority
StackType_t * const puxStackBuffer, // Points to the available memory area of the stack space
StaticTask_t * const pxTaskBuffer, // Points to the available memory area of the task description
const BaseType_t xCoreID); // Core ID
Function introduction
xTaskCreateStaticPinnedToCore
is a function used to create a task and pin the task to a specified CPU core. Unlike xTaskCreatePinnedToCore
, xTaskCreateStaticPinnedToCore
allows the user to provide the task's stack space and the memory area of the task descriptor. This allows for better control over the memory allocation and management of the task.
Parameters
pxTaskCode
: Task function pointer. It points to the function to be executed in the new task, which is usually defined in the format ofvoid function_name(void *pvParameters)
, wherepvParameters
are the parameters passed to the task.pcName
: The name string of the task, usually used for debugging and monitoring. You can use a string name that is easy to identify.ulStackDepth
: The size of the task stack, in words (4 bytes). The stack size should be set according to the stack space required in the task.pvParameters
: The parameter pointer passed to the task function. If the task function does not require parameters, it can be set toNULL
.uxPriority
: The priority of the task. The larger the value, the higher the priority. The priority in ESP-IDF can be set in the range of 0 to 24.puxStackBuffer
: Pointer to the statically allocated task stack. The stack memory needs to be allocated in advance and passed to the function. The task will use this memory instead of dynamically allocated stack space.pxTaskBuffer
: Pointer to the static task control block. The control block is used by FreeRTOS to manage task information (such as status, priority, stack pointer, etc.). This control block also needs to be allocated in advance.xCoreID
:0
: Bound to core 0 (primary core).1
: Bound to core 1.tskNO_AFFINITY
: Not bound to a specific core, allowing FreeRTOS to schedule tasks on any core.
Return value
pdPASS
: Task creation is successful.pdFAIL
: Task creation failed.
Example
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// Define static memory for task stack and task control block
static StackType_t myTaskStack[2048]; // Task stack size is 2048 bytes
static StaticTask_t myTaskBuffer; // Task control block
// Define task function
void myTask(void *pvParameters)
{
while(1)
{
printf("Running myTask on Core 1\n");
vTaskDelay(pdMS_TO_TICKS(1000)); // Delay 1 second
}
}
void app_main(void)
{
// Create a task using static memory and bind it to core 0
xTaskCreateStaticPinnedToCore(
myTask, // Task function
"MyStaticTask", // Task name
2048, // Task stack size
NULL, // Task parameters
5, // Task priority
myTaskStack, // Static stack memory
&myTaskBuffer, // Static task control block
0 // Core ID (0 means bound to core 0)
);
}
DELAY AND BLOCKING¶
vTaskDelay¶
Function prototype
Function introduction
vTaskDelay
is a function used to delay task execution. After calling the vTaskDelay
function in a task, the task will pause for the specified time and then continue to execute.
Parameters
xTicksToDelay
: Delay time, in FreeRTOS clock ticks. The frequency of the clock tick is defined by theconfigTICK_RATE_HZ
macro inFreeRTOSConfig.h
.
Example
void myTask(void *pvParameters)
{
while(1)
{
printf("Hello from myTask\n");
vTaskDelay(pdMS_TO_TICKS(1000)); // Delay 1 second
}
}
Summary
vTaskDelay
- Function:
vTaskDelay
delays a task for a specified time, starting from the current time. This means that each timevTaskDelay
is called, the starting point of the delay is the current time. - Applicable scenarios: Suitable for tasks that need to be delayed for a fixed time based on the last call time (regardless of when it was called).
vTaskDelayUntil¶
Function prototype
Function introduction
vTaskDelayUntil
is a function for periodic task execution. After calling the vTaskDelayUntil
function in a task, the task will be executed periodically at the specified time interval.
Parameters
pxPreviousWakeTime
: Pointer to the last wake-up time. When called for the first time, a pointer to 0 should be passed in.xTimeIncrement
: time interval in FreeRTOS ticks. The tick frequency is defined inFreeRTOSConfig.h
by theconfigTICK_RATE_HZ
macro.
Example
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
void taskA(void *param)
{
TickType_t xLastWakeTime;
const TickType_t xFrequency = pdMS_TO_TICKS(500); // 500 ms
// Initialize last wakeup time
xLastWakeTime = xTaskGetTickCount();
while(1)
{
ESP_LOGI("main","Hello world from CSW!");
// Wait for next cycle
vTaskDelayUntil(&xLastWakeTime, xFrequency);
}
}
/**
* @brief Entry point of the program
* @param None
* @retval None
*/
void app_main(void)
{
xTaskCreatePinnedToCore(taskA, "helloworld", 2048, NULL, 3, NULL, tskNO_AFFINITY);
}
Summary
vTaskDelay
- Function:
vTaskDelayUntil
uses an "absolute time" concept for delay, which allows tasks to precisely control the execution period. The delay starts from a fixed reference point, which ensures that the task is executed at a fixed interval, even if there are other computational overheads in the task. - Applicable scenarios: Suitable for periodic tasks, ensuring that the task is executed accurately at fixed intervals to avoid delay accumulation caused by other tasks or code.
Tip
In FreeRTOS, the frequency of the clock tick is defined by the configTICK_RATE_HZ
macro. And the input time in the delay function is actually the system tick, not the physical world time, so for ease of use, FreeRTOS provides the pdMS_TO_TICKS
macro to convert milliseconds to ticks.
Inter-task synchronization¶
Note
Synchronization in RTOS refers to the collaborative working method between different tasks or between tasks and external events to ensure that multiple concurrently executed tasks are executed in the expected order or timing. "It involves the communication and coordination mechanism between threads or tasks, the purpose is to avoid data competition, solve race conditions, and ensure the correct behavior of the system.
Note
Mutual exclusion means that a resource is only allowed to be accessed by one visitor at a time, which is unique and exclusive.
Note
A queue is a data structure used to transfer data between tasks. A queue is a first-in-first-out (FIFO) data structure. Tasks can put data into the queue and take data out of the queue.
Queue¶
xQueueCreate¶
Function prototype
QueueHandle_t xQueueCreate(const UBaseType_t uxQueueLength, // Queue length
const UBaseType_t uxItemSize); // Size of each element in the queue
Function introduction
xQueueCreate
is a function used to create a queue. A queue is a first-in-first-out (FIFO) data structure used to pass data between tasks.
Parameters
-
uxQueueLength
: The length of the queue, that is, the number of elements that can be stored in the queue. -
uxItemSize
: The size of each element in the queue, in bytes.
Return value
-
Queue handle: The queue was created successfully.
-
NULL
: The queue was not created successfully.
xQueueSend¶
Function prototype
BaseType_t xQueueSend(QueueHandle_t xQueue, // Queue handle
const void *pvItemToQueue, // Data pointer to be sent to the queue, copied to the queue
TickType_t xTicksToWait); // Waiting time
Function introduction
xQueueSend
is a function used to send data to a queue. After calling the xQueueSend
function, the data will be sent to the queue.
Parameters
xQueue
: Queue handle.pvItemToQueue
: Data pointer to be sent to the queue.xTicksToWait
: Waiting time, that is, the waiting time when the queue is full. If the queue is full, the task will wait for space to be available in the queue within the waiting time. If the waiting time is 0, the task will return immediately.
xQueueSendToBack¶
Function prototype
BaseType_t xQueueSendToBack(QueueHandle_t xQueue, // Queue handle
const void *pvItemToQueue, // Data pointer to be sent to the queue, copied to the queue
TickType_t xTicksToWait); // Waiting time
Function introduction
xQueueSendToBack
is a function used to send data to a queue. After calling the xQueueSendToBack
function, the data will be sent to the queue at the end of the queue. Suitable for situations where it is clear that the data needs to be sent to the end of the queue.
Parameters
xQueue
: Queue handle.pvItemToQueue
: Data pointer to be sent to the queue.xTicksToWait
: Waiting time, that is, the waiting time when the queue is full. If the queue is full, the task will wait for space to be available in the queue during the waiting time. If the wait time is 0, the task will return immediately.
xQueueReceive¶
Function prototype
BaseType_t xQueueReceive(QueueHandle_t xQueue, // Queue handle
void *pvBuffer, // Buffer pointer for receiving data
TickType_t xTicksToWait); // Waiting time
Function introduction
xQueueReceive
is a function for receiving data from a queue. After calling the xQueueReceive
function, data will be received from the queue.
Parameters
xQueue
: Queue handle.pvBuffer
: Buffer pointer for receiving data.
xQueueSendFromISR¶
Function prototype
BaseType_t xQueueSendFromISR(QueueHandle_t xQueue, // Queue handle
const void *pvItemToQueue, // Data pointer to be sent to the queue
BaseType_t *pxHigherPriorityTaskWoken); // High priority task wake-up flag
Function introduction
xQueueSendFromISR
is a function used to send data to a queue from an interrupt service routine (ISR). After calling the xQueueSendFromISR
function, the data will be sent to the queue.
Parameters
xQueue
: Queue handle.pvItemToQueue
: Data pointer to be sent to the queue.pxHigherPriorityTaskWoken
: High priority task wake-up flag. If a high priority task is woken up when sending data, this parameter is set topdTRUE
.
Semaphore¶
xSemaphoreCreateBinary¶
Function prototype
Function introduction
xSemaphoreCreateBinary
is a function used to create a binary semaphore. A binary semaphore is a semaphore with only two states, available and unavailable.
Return value
-
Semaphore handle: semaphore creation is successful.
-
NULL
: semaphore creation failed.
xSemaphoreCreateCounting¶
Function prototype
SemaphoreHandle_t xSemaphoreCreateCounting(const UBaseType_t uxMaxCount, // Maximum count value
const UBaseType_t uxInitialCount); // Initial count value
Function introduction
xSemaphoreCreateCounting
is a function used to create a counting semaphore. A counting semaphore is a semaphore that can store multiple count values.
Parameters
-
uxMaxCount
: maximum count value. -
uxInitialCount
: initial count value.
Return value
-
Semaphore handle: semaphore creation is successful.
-
NULL
: semaphore creation fails.
xSemaphoreCreateMutex¶
Function prototype
Function introduction
xSemaphoreCreateMutex
is a function used to create a mutex semaphore. A mutex semaphore is a semaphore used to implement mutually exclusive access and is used to protect shared resources.
Return value
-
Semaphore handle: semaphore creation is successful.
-
NULL
: semaphore creation fails.
Mutex
A mutex is a synchronization mechanism used to implement mutually exclusive access. In FreeRTOS, mutexes are implemented by mutex semaphores. Mutexes ensure that only one task can access a shared resource at any time. Unlike binary semaphores, mutexes implement priority inheritance and priority inversion mechanisms to ensure that tasks can access shared resources in order of priority.
Priority Flip
Priority flip refers to the temporary occupation of resources by low-priority tasks, which causes high-priority tasks to be blocked or even further delayed by medium-priority tasks. Ultimately, the execution of high-priority tasks is indirectly delayed by low-priority tasks, a phenomenon known as priority flip.
A typical priority flip scenario is as follows: Suppose there are three tasks, with priorities from high to low: Task A, Task B, and Task C, and they share a resource (such as a mutex). Step 1: Low-priority Task C acquires the resource (lock) and is using it. Step 2: Before Task C releases the resource, high-priority Task A starts running and tries to access the same resource. But because the resource is already occupied by Task C, Task A is blocked, waiting for Task C to release the resource. Step 3: At this time, Task B, which has a priority between the two, starts running, and because its priority is higher than Task C, Task B will preempt the execution of Task C. Result: Due to the execution of Task B, the progress of Task C is delayed, causing Task A to be delayed as well. Even though Task A has the highest priority, it cannot be executed immediately because the medium-priority Task B indirectly blocks its execution. This situation is called priority flipping, because the execution of the low-priority task Task C blocks the execution of the high-priority task Task A, and the intervention of Task B makes the flipping effect more serious.
Priority flipping can cause high-priority tasks of real-time systems to fail to complete on time, resulting in system performance degradation or instability. In real-time applications (such as control systems or communication systems), priority flipping can have serious consequences.
FreeRTOS and many other RTOS use priority inheritance to solve the priority flipping problem. The principle of priority inheritance mechanism is as follows: When a low-priority task holds a resource and blocks a high-priority task, the low-priority task inherits the priority of the high-priority task until the resource is released. In the above example, after Task C blocks the high-priority Task A, it is temporarily promoted to the priority of Task A. In this way, Task C can continue to run before Task B and release resources as soon as possible, so that Task A can obtain resources and execute in time. Once Task C releases resources, its priority will be restored to the original level.
xSemaphoreTake¶
Function prototype
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, // semaphore handle
TickType_t xTicksToWait); // waiting time
Function introduction
xSemaphoreTake
is a function used to obtain a semaphore. After calling the xSemaphoreTake
function, the task will try to acquire the semaphore.
Parameters
-
xSemaphore
: semaphore handle. -
xTicksToWait
: waiting time, that is, the waiting time when the semaphore is not available. If the semaphore is not available, the task will wait for the semaphore to be available within the waiting time. If the waiting time is 0, the task will return immediately.
Return value
-
pdPASS
: Successfully acquired the semaphore. -
pdFAIL
: Failed to acquire the semaphore.
xSemaphoreGive¶
Function prototype
Function introduction
xSemaphoreGive
is a function used to release a semaphore. After calling the xSemaphoreGive
function, the semaphore will be released.
Parameters
xSemaphore
: semaphore handle.
Return value
-
pdPASS
: Release semaphore successfully. -
pdFAIL
: Release semaphore failed.
xSemaphoreDelete¶
Function prototype
Function introduction
vSemaphoreDelete
is a function used to delete a semaphore. After calling the vSemaphoreDelete
function, the semaphore will be deleted.
Parameters
xSemaphore
: semaphore handle.
Event Groups and Direct Task Notifications¶
Event Groups
Event groups are a mechanism for managing event notifications between tasks. Event groups allow tasks to wait for a combination of multiple events so that they can be woken up when any event occurs. Event groups are often used for synchronization and communication between tasks so that tasks can decide what to do next based on the state of the event.
Tip
Event groups can replace semaphores in many cases.
Event Bits
Events in an event group are represented in bits, with each event bit corresponding to one event. The value of an event bit can be 0 or 1, indicating that the event has not occurred or has occurred. The size of an event group depends on the number of event bits, which is usually 8, 16, or 32 bits.
Direct Task Notifications
Direct task notifications are a mechanism for sending notifications to tasks. Unlike event groups, direct task notifications are one-to-one notification mechanisms, i.e., one notification can only wake up one task. Direct task notifications are often used for synchronization and communication between tasks so that tasks can decide what to do next based on the state of the notification.
xEventGroupCreate¶
Function prototype
Function introduction
xEventGroupCreate
is a function used to create an event group. An event group is a mechanism for managing event notifications between tasks.
Return value
- Event group handle: Event group creation is successful.
NULL
: Event group creation fails.
xEventGroupWaitBits¶
Function prototype
EventBits_t xEventGroupWaitBits(EventGroupHandle_t xEventGroup, // Event group handle
const EventBits_t uxBitsToWaitFor, // Event bits to wait for
const BaseType_t xClearOnExit, // Whether to clear event bits when exiting
const BaseType_t xWaitForAllBits, // Whether to wait for all event bits
TickType_t xTicksToWait); // Waiting time
Function introduction
xEventGroupWaitBits
is a function used to wait for event bits in an event group. After calling the xEventGroupWaitBits
function, the task will wait for the event bits in the event group.
Parameters
xEventGroup
: Event group handle.uxBitsToWaitFor
: Event bits to wait for.xClearOnExit
: whether to clear the event bit on exit. If set topdTRUE
, the event bit is cleared on exit; if set topdFALSE
, the event bit is not cleared.xWaitForAllBits
: whether to wait for all event bits. If set topdTRUE
, wait for all event bits; if set topdFALSE
, wait for only any event bit.xTicksToWait
: Wait time, that is, the time to wait when the event bit does not occur. If the event bit does not occur, the task will wait for the event bit to occur within the wait time. If the wait time is 0, the task will return immediately.
Return value
- The state of the event group after waiting.
NULL
: The event group wait failed.
xEventGroupSetBits¶
Function prototype
EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup, // Event group handle
const EventBits_t uxBitsToSet); // Event bits to set
Function introduction
xEventGroupSetBits
is a function used to set event bits in an event group. After calling the xEventGroupSetBits
function, the event bits in the event group will be set.
Parameters
xEventGroup
: event group handle.uxBitsToSet
: event bits to be set.
Return value
- The state of the event group after setting.
xEventGroupClearBits¶
Function prototype
EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup, // event group handle
const EventBits_t uxBitsToClear); // event bits to be cleared
Function introduction
xEventGroupClearBits
is a function used to clear event bits in an event group. After calling the xEventGroupClearBits
function, the event bits in the event group will be cleared.
Parameters
xEventGroup
: event group handle.uxBitsToClear
: event bits to be cleared.
Return value
- The state of the event group after clearing.
NULL
: event group clearing failed.
Direct Task Notifications
Each RTOS task has an array of task notifications. Each task notification has a notification state of "pending" or "not pending", and a 32-bit notification value. Direct task notifications are events sent directly to the task, rather than indirectly to the task through an intermediate object (such as a queue, event group, or semaphore). Sending a "direct task notification" to a task sets the target task notification to the "suspended" state (this suspension is not a suspended task).
xTaskNotify¶
Function prototype
BaseType_t xTaskNotify(TaskHandle_t xTaskToNotify, // Task handle to be notified
const uint32_t ulValue, // Notification value
eNotifyAction eAction); // Notification action
Function introduction
xTaskNotify
is a function used to send notifications to a task. After calling the xTaskNotify
function, the notification will be sent to the task.
Parameters
xTaskToNotify
: Task handle to be notified.ulValue
: Notification value.eAction
: Notification action. Notification action can be one of the following values:eNoAction
: Do nothing.eSetBits
: Set the task notification value.eIncrement
: Increment the task notification value.eSetValueWithOverwrite
: Set the task notification value. If the task already has a notification value, overwrite it.eSetValueWithoutOverwrite
: Set the task notification value. If the task already has a notification value, do not overwrite it.
Return value
pdPASS
: Notification sent successfully.pdFAIL
: Notification sent failed.
xTaskNotifyWait¶
Function prototype
BaseType_t xTaskNotifyWait(uint32_t ulBitsToClearOnEntry, // Bits to be cleared on entry
uint32_t ulBitsToClearOnExit, // Bits to be cleared on exit
uint32_t *pulNotificationValue, // Notification value
TickType_t xTicksToWait); // Waiting time
Function introduction
xTaskNotifyWait
is a function used to wait for task notification. After calling the xTaskNotifyWait
function, the task will wait for the notification to arrive.
Parameters
ulBitsToClearOnEntry
: The bit to be cleared on entry.ulBitsToClearOnExit
: The bit to be cleared on exit.pulNotificationValue
: Notification value.xTicksToWait
: Waiting time, that is, the waiting time when the notification has not arrived. If the notification has not arrived, the task will wait for the notification to arrive within the waiting time. If the waiting time is 0, the task will return immediately.
Return value
pdPASS
: Waiting for notification is successful.pdFAIL
: Waiting for notification failed.