FreeRTOS入门-01- 创建任务以及调度任务 -基于AI-m61-32s-kit

[复制链接]
查看941 | 回复6 | 2023-11-14 16:01:29 | 显示全部楼层 |阅读模式

本帖最后由 WangChong 于 2023-11-14 16:15 编辑

本帖最后由 WangChong 于 2023-11-14 16:04 编辑

FreeRTOS入门-01- 创建任务以及调度任务 -基于AI-m61-32s-kit

移植

  1. AI-m61的sdk内已经集成了FreeRTOS组件,当你从零创建一个项目的时候,只需要在本地项目的proj.conf 中启用FreeRTOS库
set(CONFIG_FREERTOS             1)
  1. 你还需要拷贝FreeRTOSConfig.h到你的项目文件夹下(不是必须)
  2. 在程序中引入 FreeRTOS.h
  3. 修改FreeRTOS.h的第59行,指向你的FreeRTOSConfig.h文件(绝对路径)

恭喜你!你已经成功的完成了小安派M61-32s-kit的FreeRtos配置


API介绍

在介绍API之前,简单的提一下OS, 只需要记住最重要的一句话就是OS是负责任务调度

首先介绍今天的第一个API

BaseType_t xTaskCreate(    TaskFunction_t pvTaskCode,
                            const char * const pcName,
                            configSTACK_DEPTH_TYPE usStackDepth,
                            void *pvParameters,
                            UBaseType_t uxPriority,
                            TaskHandle_t *pxCreatedTask
                          );
pvTaskCode 指向任务入口函数的指针(即 实现任务的函数名称,请参阅如下示例)。任务通常以 无限循环的形式实现;实现任务的函数决不能试图返回 或退出。 但是,任务可以自我删除
pcName 任务的描述性名称。主要是为了方便 调试,但也可用于获取任务句柄。任务名称的最大长度由FreeRTOSConfig.h中的configMAX_TASK_NAME_LEN定义。
usStackDepth 分配用于 任务堆栈的 字数(不是字节)。例如,如果堆栈的宽度为 16 位,usStackDepth 为 100,则将分配 200 字节用作该任务的堆栈。 再举一例,如果堆栈的宽度为 32 位,usStackDepth 为 400,则将分配 1600 字节用作该任务的堆栈。堆栈深度与堆栈宽度的乘积不得超过size_t类型变量所能包含的最大值。请参阅常见问题:堆栈应有多大?
pvParameters 作为参数传递给创建的任务的一个值。如果pvParameters设置为变量的地址, 则在执行创建的任务时该变量必须仍然存在——因此 传递堆栈变量的地址是无效的。
uxPriority 创建任务执行的优先级。包含 MPU 支持的系统可在特权(系统)模式下选择性地创建任务, 方法是在uxPriority中设置portPRIVILEGE_BIT位。 例如,要创建一个优先级为 2 的特权任务,可将uxPriority设置为 ( 2 \
pxCreatedTask 用于将句柄传递至由xTaskCreate()函数创建的任务 。pxCreatedTask是可选的,可设置为 NULL。

上述的这个方法用于创建一个任务并且分配给FreeRtos进行调度,其中需要注意的是uxPriority参数,这个参数是任务的优先级,优先级越高,任务将可能最先被CPU调度,并不是优先级越高就一定会被第一个调度!。


创建Task

下面是一个调用示例:

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)); // 等待1秒
    }
}

在上述代码中,我创建了一个blink 绿色LED的任务,主要这个任务中是一个死循环即While(1)然后在循环体内使LED间隔1秒进行闪烁。

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)); // 等待1秒
    }
}

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)); // 等待1秒
    }
}

上面我又创建了两个点亮蓝色LED和红色LED的方法。如果这里不使用OS进行调度,并且程序处于单线程的情况下,一旦其中某一个方法已经执行的话,程序使不可能执行到其他的位置的(因为是死循环)。

将任务交给RTOS,由RTOS进行调度

 xTaskCreate(turnRedLed, // 任务函数
                "Task1",    // 任务名称,用于调试
                128,        // 任务栈大小(以字为单位)
                NULL,       // 传递给任务函数的参数
                1,          // 任务优先级,数值越高优先级越高
                NULL);      // 任务句柄,可以用于后续操作

    xTaskCreate(turnBlueLed, // 任务函数
                "Task2",    // 任务名称,用于调试
                128,        // 任务栈大小(以字为单位)
                NULL,       // 传递给任务函数的参数
                2,          // 任务优先级,数值越高优先级越高
                NULL);
    xTaskCreate(turnGreenLed, // 任务函数
                "Task3",     // 任务名称,用于调试
                128,         // 任务栈大小(以字为单位)
                NULL,        // 传递给任务函数的参数
                3,           // 任务优先级,数值越高优先级越高
                NULL);

上述方法中,使用xTaskCreate 把任务交给了OS进行调度。


今天的第二个API

void vTaskStartScheduler( void );

  • 描述: 启动 RTOS 调度器。调用后,RTOS 内核可以控制在何时执行哪些任务。(由RTOS负责任务调度)

调用示例:

vTaskStartScheduler();

完整代码如下:

#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)); // 等待1秒
    }
}

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)); // 等待1秒
    }
}

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)); // 等待1秒
    }
}

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, // 任务函数
                "Task1",    // 任务名称,用于调试
                128,        // 任务栈大小(以字为单位)
                NULL,       // 传递给任务函数的参数
                1,          // 任务优先级,数值越高优先级越高
                NULL);      // 任务句柄,可以用于后续操作

    xTaskCreate(turnBlueLed, // 任务函数
                "Task2",    // 任务名称,用于调试
                128,        // 任务栈大小(以字为单位)
                NULL,       // 传递给任务函数的参数
                2,          // 任务优先级,数值越高优先级越高
                NULL);
    xTaskCreate(turnGreenLed, // 任务函数
                "Task3",     // 任务名称,用于调试
                128,         // 任务栈大小(以字为单位)
                NULL,        // 传递给任务函数的参数
                3,           // 任务优先级,数值越高优先级越高
                NULL);
    // 启动调度器
    vTaskStartScheduler();
    while (1)
    {
    }
}

实验现象

RGBLED灯闪烁

  • 注意,千万不要在Task内使用非RTOS提供的延时函数,如果使用的话RTOS将可能出现调度错误。请使用vTaskDelay(pdMS_TO_TICKS(1000))替换

本帖被以下淘专辑推荐:

回复

使用道具 举报

bzhou830 | 2023-11-14 16:06:52 | 显示全部楼层
给王哥点赞
选择去发光,而不是被照亮
回复 支持 反对

使用道具 举报

爱笑 | 2023-11-14 17:24:30 | 显示全部楼层
给王哥点赞
用心做好保姆工作
回复 支持 反对

使用道具 举报

WT_0213 | 2023-11-16 09:42:00 | 显示全部楼层
学习了
回复

使用道具 举报

psgrep | 2023-11-25 19:52:49 | 显示全部楼层
厉害👍🏻
回复

使用道具 举报

san | 2023-11-25 23:02:01 | 显示全部楼层
学习
回复

使用道具 举报

WT_0213 | 2023-11-30 16:34:43 | 显示全部楼层
最后的那个 “注意”, 完整代码中第15、26、37使用的就是非 RTOS 提供的延时函数吧。正好要用到求教。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则