Skip to content

Contributed by WangChong, curated by Ai-Thinker

FreeRTOS Primer 01 - Creating and Scheduling Tasks - Based on Ai-M61-32S-Kit

Porting

Operation Steps

1
Enable the FreeRTOS component in proj.conf

The Ai-M61 SDK already integrates the FreeRTOS component. When creating a project from scratch, you only need to enable the FreeRTOS library in your local project’s proj.conf

set(CONFIG_FREERTOS             1)
2
Copy FreeRTOSConfig.h into your project folder (not required)

You also need to copy FreeRTOSConfig.h into your project folder (not required)

3
Include FreeRTOS.h in your program

Include FreeRTOS.h in your program

4
Modify line 59 of FreeRTOS.h to point to your FreeRTOSConfig.h (absolute path)

Modify line 59 of FreeRTOS.h to point to your FreeRTOSConfig.h file (absolute path)

Congratulations! You have successfully completed the FreeRTOS configuration for the Xiao An Pai M61-32S-Kit


API Introduction

Before introducing the API, let me briefly mention the OS. Just remember the most important sentence: the OS is responsible for task scheduling.

First, let's introduce today's first API

c
BaseType_t xTaskCreate(
    TaskFunction_t pvTaskCode,
    const char * const pcName,
    configSTACK_DEPTH_TYPE usStackDepth,
    void *pvParameters,
    UBaseType_t uxPriority,
    TaskHandle_t *pxCreatedTask
);
pvTaskCodePointer to the task entry function (i.e., the name of the function that implements the task; see the example below). Tasks are usually implemented as infinite loops; the function implementing a task must never attempt to return or exit. However, a task can delete itself.
pcNameA descriptive name for the task. It is mainly for debugging convenience, but can also be used to obtain the task handle. The maximum length of a task name is defined by configMAX_TASK_NAME_LEN in FreeRTOSConfig.h.
usStackDepthThe number of words (not bytes) to be allocated for the task stack. For example, if the stack width is 16 bits and usStackDepth is 100, then 200 bytes will be allocated as the stack for this task. As another example, if the stack width is 32 bits and usStackDepth is 400, then 1600 bytes will be allocated as the stack for this task. The product of stack depth and stack width must not exceed the maximum value that can be held in a variable of type size_t. See the FAQ: How big should the stack be?
pvParametersA value passed as a parameter to the created task. If pvParameters is set to the address of a variable, that variable must still exist when the created task executes—so passing the address of a stack variable is invalid.
uxPriorityThe priority at which the created task will execute. Systems with MPU support can optionally create tasks in privileged (system) mode by setting the portPRIVILEGE_BIT bit in uxPriority. For example, to create a privileged task with priority 2, set uxPriority to (2 | portPRIVILEGE_BIT).
pxCreatedTaskUsed to pass a handle to the task created by the xTaskCreate() function. pxCreatedTask is optional and can be set to NULL.

The method above is used to create a task and hand it over to FreeRTOS for scheduling. Note the uxPriority parameter: it is the task priority. The higher the priority, the more likely the task will be scheduled by the CPU first—but a higher priority does not guarantee it will always be scheduled first!


Creating a Task

Here is a calling example:

c
void turnGreenLed()
{
    while (1)
    {
        bflb_gpio_set(gpio, GPIO_PIN_14);
        bflb_mtimer_delay_ms(1000);
        bflb_gpio_reset(gpio, GPIO_PIN_14);
        vTaskDelay(pdMS_TO_TICKS(1000));
        // wait 1 second
    }
}

In the code above, I created a task that blinks the green LED. Note that this task is an infinite loop, i.e., while(1), and inside the loop the LED blinks every 1 second.

c
void turnRedLed()
{
    while (1)
    {
        bflb_gpio_set(gpio, GPIO_PIN_12);
        bflb_mtimer_delay_ms(1000);
        bflb_gpio_reset(gpio, GPIO_PIN_12);
        vTaskDelay(pdMS_TO_TICKS(1000));
        // wait 1 second
    }
}

void turnBlueLed()
{
    while (1)
    {
        bflb_gpio_set(gpio, GPIO_PIN_15);
        bflb_mtimer_delay_ms(1000);
        bflb_gpio_reset(gpio, GPIO_PIN_15);
        vTaskDelay(pdMS_TO_TICKS(1000));
        // wait 1 second
    }
}

Above, I created two more functions for lighting the blue and red LEDs. If the OS is not used for scheduling and the program is single-threaded, once one of these functions starts executing, the program can never reach any other code (because they are infinite loops).

Handing the tasks to the RTOS for scheduling

c
    xTaskCreate(turnRedLed,   // task function
                "Task1",      // task name, for debugging
                128,          // task stack size (in words)
                NULL,         // parameters passed to the task function
                1,            // task priority, the higher the value the higher the priority
                NULL);        // task handle, can be used for later operations
    xTaskCreate(turnBlueLed,  // task function
                "Task2",      // task name, for debugging
                128,          // task stack size (in words)
                NULL,         // parameters passed to the task function
                2,            // task priority, the higher the value the higher the priority
                NULL);        // task handle, can be used for later operations
    xTaskCreate(turnGreenLed, // task function
                "Task3",      // task name, for debugging
                128,          // task stack size (in words)
                NULL,         // parameters passed to the task function
                3,            // task priority, the higher the value the higher the priority
                NULL);        // task handle, can be used for later operations

In the method above, xTaskCreate hands the tasks to the OS for scheduling.


Today's second API

void vTaskStartScheduler( void );

  • Description: Starts the RTOS scheduler. After calling it, the RTOS kernel controls when each task is executed. (The RTOS is responsible for task scheduling)

Calling example:

c
vTaskStartScheduler();

Complete Code

c
#include "bflb_mtimer.h"
#include "board.h"
#include "FreeRTOS.h"
#define DBG_TAG "MAIN"
#include "bflb_gpio.h"
#include "log.h"

struct bflb_device_s *gpio;

void turnRedLed()
{
    while (1)
    {
        bflb_gpio_set(gpio, GPIO_PIN_12);
        bflb_mtimer_delay_ms(1000);
        bflb_gpio_reset(gpio, GPIO_PIN_12);
        vTaskDelay(pdMS_TO_TICKS(1000));
        // wait 1 second
    }
}

void turnBlueLed()
{
    while (1)
    {
        bflb_gpio_set(gpio, GPIO_PIN_15);
        bflb_mtimer_delay_ms(1000);
        bflb_gpio_reset(gpio, GPIO_PIN_15);
        vTaskDelay(pdMS_TO_TICKS(1000));
        // wait 1 second
    }
}

void turnGreenLed()
{
    while (1)
    {
        bflb_gpio_set(gpio, GPIO_PIN_14);
        bflb_mtimer_delay_ms(1000);
        bflb_gpio_reset(gpio, GPIO_PIN_14);
        vTaskDelay(pdMS_TO_TICKS(1000));
        // wait 1 second
    }
}

int main(void)
{
    board_init();
    gpio = bflb_device_get_by_name("gpio");

    // Red
    bflb_gpio_init(gpio, GPIO_PIN_12, GPIO_OUTPUT | GPIO_PULLUP | GPIO_DRV_3);
    // Blue
    bflb_gpio_init(gpio, GPIO_PIN_15, GPIO_OUTPUT | GPIO_PULLUP | GPIO_DRV_3);
    // Green
    bflb_gpio_init(gpio, GPIO_PIN_14, GPIO_OUTPUT | GPIO_PULLUP | GPIO_DRV_3);

    xTaskCreate(turnRedLed,   // task function
                "Task1",      // task name, for debugging
                128,          // task stack size (in words)
                NULL,         // parameters passed to the task function
                1,            // task priority, the higher the value the higher the priority
                NULL);        // task handle, can be used for later operations
    xTaskCreate(turnBlueLed,  // task function
                "Task2",      // task name, for debugging
                128,          // task stack size (in words)
                NULL,         // parameters passed to the task function
                2,            // task priority, the higher the value the higher the priority
                NULL);        // task handle, can be used for later operations
    xTaskCreate(turnGreenLed, // task function
                "Task3",      // task name, for debugging
                128,          // task stack size (in words)
                NULL,         // parameters passed to the task function
                3,            // task priority, the higher the value the higher the priority
                NULL);        // task handle, can be used for later operations

    // start the scheduler
    vTaskStartScheduler();

    while (1)
    {
    }
}

Expected Result

The RGB LED blinks.

  • Note: never use a non-RTOS delay function inside a task; if you do, the RTOS may make scheduling errors. Use vTaskDelay(pdMS_TO_TICKS(1000)) instead.

Have questions?

For other questions, please visit the unified discussion area: Ai-Thinker Discussions

Released under the MIT License. Build Time 2026-09-11 14:52:23