本帖最后由 WangChong 于 2023-11-14 16:15 编辑
本帖最后由 WangChong 于 2023-11-14 16:04 编辑
FreeRTOS入门-01- 创建任务以及调度任务 -基于AI-m61-32s-kit
移植
- AI-m61的sdk内已经集成了FreeRTOS组件,当你从零创建一个项目的时候,只需要在本地项目的proj.conf 中启用FreeRTOS库
set(CONFIG_FREERTOS 1)
- 你还需要拷贝FreeRTOSConfig.h到你的项目文件夹下(不是必须)
- 在程序中引入 FreeRTOS.h
- 修改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))替换