Detailed Explanation of FreeRTOS Task Management and Communication Mechanisms

1 Task Creation and Management

Task Creation

Use <span>xTaskCreate()</span> to create a task:

BaseType_t xTaskCreate(    TaskFunction_t pxTaskCode,     // Task function (entry)    const char * const pcName,     // Task name (for debugging)    configSTACK_DEPTH_TYPE usStackDepth, // Stack size (in words)    void * const pvParameters,     // Task parameters    UBaseType_t uxPriority,        // Priority (0~configMAX_PRIORITIES-1)    TaskHandle_t * const pxCreatedTask // Task handle (can be NULL));

Note: The task function must conform to the prototype <span>void (*TaskFunction_t)(void *)</span>.

Task Deletion

<span>vTaskDelete(TaskHandle_t xTaskToDelete); </span>

You can delete any task, including itself (pass in <span>NULL</span>).

Task Suspension and Wakeup

  • Suspend a task:<span>vTaskSuspend(TaskHandle_t xTask);</span>

  • Wake up a task:<span>vTaskResume(TaskHandle_t xTask);</span>

Used to control the execution timing of tasks, but care should be taken to avoid permanently suspending tasks, which can waste system resources.

2 Queues

Definition

A queue is a typical FIFO (First In First Out) data structure used for data communication between tasks or between interrupts and tasks.

Creating a Queue

<span>QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength, UBaseType_t uxItemSize); </span>

  • <span>uxQueueLength</span>: Maximum number of items the queue can hold

  • <span>uxItemSize</span>: Size of each item (in bytes)

Sending Data to the Queue

<span>xQueueSend(xQueue, pvItemToQueue, xTicksToWait); </span>

  • <span>xTicksToWait = 0</span>: Non-blocking send

  • <span>portMAX_DELAY</span>: Block until successful (requires blocking API support)

Receiving Data from the Queue

<span>xQueueReceive(xQueue, pvBuffer, xTicksToWait); </span>

3 Semaphores

Semaphores are used for synchronization and resource access control between tasks. FreeRTOS provides several types of semaphores:

1. Binary Semaphore

Used for synchronizing events or interrupt signals (like a “phone booth” model).

  • Create:<span>xSemaphoreCreateBinary();</span>

  • Take:<span>xSemaphoreTake(xSemaphore, xBlockTime);</span>

  • Give:<span>xSemaphoreGive(xSemaphore);</span>

Feature: The same task cannot take it again without releasing it.

2. Mutex

Suitable for protecting access to shared resources (like peripherals, memory, etc.) to prevent resource conflicts.

  • Create:<span>xSemaphoreCreateMutex();</span>

Characteristic: Only the task that obtained the mutex can release it; incorrect release can lead to deadlock.

3. Counting Semaphore

Used for resource counting or controlling concurrency (like a “parking space” model).

  • Create:<span>xSemaphoreCreateCounting(uxMaxCount, uxInitialCount);</span>

  • Current remaining resource count:<span>uxSemaphoreGetCount(xSemaphore);</span>

4. Recursive Mutex

Allows a task to take the lock multiple times within the same logic, with each take needing a corresponding release.

  • Create:<span>xSemaphoreCreateRecursiveMutex();</span>

  • Take:<span>xSemaphoreTakeRecursive(xMutex, xBlockTime);</span>

  • Give:<span>xSemaphoreGiveRecursive(xMutex);</span>

4 Software Timers

Software timers are used for delaying or periodically executing certain task functions, in a non-blocking manner.

Creating a Software Timer

TimerHandle_t xTimerCreate( const char * const pcTimerName, TickType_t xTimerPeriodInTicks, UBaseType_t uxAutoReload, // pdTRUE: periodic; pdFALSE: one-time void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction // Timer callback);

To start the timer, you need to call <span>xTimerStart()</span>. The system tick rate is defined by <span>configTICK_RATE_HZ</span> (usually set to 1000, meaning 1ms per tick).

5 Event Groups

Event groups provide a bit manipulation mechanism, suitable for multi-event synchronization (for example, <span>KEY1 & KEY2 -> LED1_ON</span>).

Creating and Setting

  • Create:<span>xEventGroupCreate();</span>

  • Set bits:<span>xEventGroupSetBits(xEventGroup, uxBitsToSet);</span>

Waiting for Event Bits to be Satisfied

<span>xEventGroupWaitBits( xEventGroup, uxBitsToWaitFor, xClearOnExit, // whether to clear bits on exit xWaitForAllBits, // wait for all bits vs any bit xTicksToWait ); </span>

6 Task Notifications

Task notifications are a lightweight communication mechanism that is more efficient than queues and semaphores (supports up to 32-bit notification values).

Sending Notifications (in Interrupt)

<span>xTaskNotifyFromISR(xTaskToNotify, ulValue, eAction, pxHigherPriorityTaskWoken); </span>

  • <span>ulValue</span>: Notification value

  • <span>eAction</span>: Notification mode (e.g., overwrite, accumulate)

  • <span>pxHigherPriorityTaskWoken</span>: Used for context switching in interrupts

Waiting for Notifications

<span>xTaskNotifyWait( ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait ); </span>

Using task notifications can efficiently replace traditional mechanisms like semaphores and events.

Appendix: Common FreeRTOS Configuration Parameters

Macro Definition Meaning
<span>configMAX_PRIORITIES</span> Maximum number of priorities (usually 5 or higher)
<span>configMINIMAL_STACK_SIZE</span> Minimum stack size for tasks (128 means 512 bytes)
<span>configMAX_TASK_NAME_LEN</span> Maximum length of task names (usually 16)
<span>configTICK_RATE_HZ</span>

Below is a complete and well-structured FreeRTOS demo example code, including the basic process of task creation and starting the task scheduler, suitable for beginners to reference:

✅ Example: FreeRTOS Task Creation and Scheduler Start (in main function)

#include "FreeRTOS.h"#include "task.h"#include <stdio.h>// Task handles (optional)TaskHandle_t Task1Handle = NULL;TaskHandle_t Task2Handle = NULL;// Task function definitionsvoid Task1(void *pvParameters){    while (1)    {        printf("Task 1 is running...\n");        vTaskDelay(pdMS_TO_TICKS(1000));  // Delay 1000ms    }}void Task2(void *pvParameters){    while (1)    {        printf("Task 2 is running...\n");        vTaskDelay(pdMS_TO_TICKS(500));  // Delay 500ms    }}// Main functionint main(void){    // Create Task 1    xTaskCreate(        Task1,                   // Task function        "Task1",                 // Task name (for debugging)        128,                     // Stack size (in words, 128 x 4 = 512 bytes)        NULL,                    // Parameters        2,                       // Priority (range: 0 ~ configMAX_PRIORITIES-1)        &Task1Handle             // Task handle    );    // Create Task 2    xTaskCreate(        Task2,        "Task2",        128,        NULL,        1,                       // Lower priority than Task 1        &Task2Handle    );    // Start the task scheduler    vTaskStartScheduler();    // Will never reach here unless stack is insufficient or scheduler fails to start    while (1);}

🔍 Explanation

  • <span>xTaskCreate()</span>: Registers the task and prepares it for scheduling, must be called before <span>vTaskStartScheduler()</span>.

  • <span>vTaskDelay()</span>: Implements task delay, using <span>pdMS_TO_TICKS()</span> macro to avoid hardcoding.

  • <span>vTaskStartScheduler()</span>: Starts the scheduler, after which FreeRTOS will automatically manage task switching.

Leave a Comment