FreeRTOS的任务创建/删除(详解,很简单)

2023-12-13 20:37:02

什么是FreeRTOS的任务?

????????在FreeRTOS中,任务(Task)是系统中执行的基本单元。任务是一段具有独立执行流的代码,它可以在系统中独立运行。每个任务都有自己的栈空间和程序计数器(Program Counter),使得它能够保持自己的上下文并独立于其他任务运行。

????????在FreeRTOS中,任务的创建和调度由内核负责。任务可以具有不同的优先级,高优先级的任务将在低优先级任务之前执行。FreeRTOS使用抢占式调度(Preemptive Scheduling),这意味着如果有更高优先级的任务准备好运行,它可以抢占当前正在运行的任务。

????????任务的创建和管理是使用FreeRTOS提供的API函数完成的。以下是一些常见的任务管理函数:

????????xTaskCreate(): 用于创建任务。

????????vTaskDelete(): 用于删除任务。

????????vTaskDelay(): 用于使任务延迟一段时间。

????????vTaskSuspend()和vTaskResume(): 用于挂起和恢复任务的执行。

????????vTaskPrioritySet(): 用于设置任务的优先级。

????????任务可以通过调用vTaskDelay()来使自己暂停一段时间,也可以通过调用vTaskSuspend()挂起自己的执行,然后通过vTaskResume()来恢复执行。

????????在FreeRTOS中,任务是多线程的基本单位,通过任务的创建、删除、挂起、恢复等操作,可以实现多任务协同工作,提高系统的实时性和并发性。

????????在FreeRTOS中,任务就是一个函数,原型如下:

void ATaskFunction(void *pvParameters);

要注意的是:

1、这个函数不能返回

2、同一个函数,可以用来创建多个任务;换句话说,多个任务可以运行同一个函数;

3、函数内部,尽量使用局部变量:

? ? ? ? 1)每个任务都有自己的栈

? ? ? ? 2)每个任务运行这个函数时

? ? ? ? ? ? ? ? (1)任务A的局部变量放在任务A的栈里、任务B的局部变量放在任务B的栈里

? ? ? ? ? ? ? ? (2)不同任务的局部变量,有自己的副本

? ? ? ? 3)函数使用全局变量、静态变量的话

? ? ? ? ? ? ? ? (1)只有一个副本:多个任务使用的是同一个副本

? ? ? ? ? ? ? ? (2)要防止冲突(后续会讲)

示例:

void ATaskFunction( void *pvParameters )
{
????/* 对于不同的任务,局部变量放在任务的栈里,有各自的副本 */
?????? int32_t lVariableExample = 0;

??? /* 任务函数通常实现为一个无限循环 */
?????? for( ;; )
?????? {
????????????? /* 任务的代码 */
?????? }

??? /* 如果程序从循环中退出,一定要使用vTaskDelete删除自己
???? * NULL表示删除的是自己
???? */

?????? vTaskDelete( NULL );
??? /* 程序不会执行到这里, 如果执行到这里就出错了 */
}

任务的创建

创建任务时使用的函数如下:

BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, // 函数指针, 任务函数
??????????????????????? const char * const pcName, // 任务的名字
??????????????????????? const configSTACK_DEPTH_TYPE usStackDepth, // 栈大小,单位为word,10表示40字节
??????????????????????? void * const pvParameters, // 调用任务函数时传入的参数
??????????????????????? UBaseType_t uxPriority,??? // 优先级
??????????????????????? TaskHandle_t * const pxCreatedTask ); // 任务句柄, 以后使用它来操作这个任务

参数说明:

参数描述
pvTaskCode

函数指针,可以简单地认为任务就是一个C函数。

它稍微特殊一点:永远不退出,或者退出时要调用"vTaskDelete(NULL)"

pcName

任务的名字,FreeRTOS内部不使用它,仅仅起调试作用。

长度为:configMAX_TASK_NAME_LEN

usStackDepth

每个任务都有自己的栈,这里指定栈大小。

单位是word,比如传入100,表示栈大小为100 word,也就是400字节。

最大值为uint16_t的最大值。

怎么确定栈的大小,并不容易,很多时候是估计。

精确的办法是看反汇编码。

pvParameters调用pvTaskCode函数指针时用到:pvTaskCode(pvParameters)
uxPriority

优先级范围:0~(configMAX_PRIORITIES – 1)

数值越小优先级越低,

如果传入过大的值,xTaskCreate会把它调整为(configMAX_PRIORITIES – 1)

pxCreatedTask

用来保存xTaskCreate的输出结果:task handle。

以后如果想操作这个任务,比如修改它的优先级,就需要这个handle。

如果不想使用该handle,可以传入NULL。

返回值

成功:pdPASS;

失败:errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY(失败原因只有内存不足)

注意:文档里都说失败时返回值是pdFAIL,这不对。

pdFAIL是0,errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY是-1。

创建任务的例子

#include "stm32f1xx.h"
#include "FreeRTOS.h"
#include "task.h"


// 函数原型
void SystemClock_Config(void);
static void MX_GPIO_Init(void);

// 任务函数
void vTask1(void *pvParameters) {
??? while (1) {
??????? HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 切换LED状态
??????? vTaskDelay(1000 / portTICK_PERIOD_MS); // 每隔1秒执行一次
??? }
}

void vTask2(void *pvParameters) {
??? while (1) {
??????? // 任务2的处理逻辑
??????? vTaskDelay(2000 / portTICK_PERIOD_MS); // 每隔2秒执行一次
??? }
}

void vTask3(void *pvParameters) {
??? while (1) {
??????? // 任务3的处理逻辑
??????? vTaskDelay(3000 / portTICK_PERIOD_MS); // 每隔3秒执行一次
??? }
}

int main(void) {
??? // 硬件初始化
??? HAL_Init();
??? SystemClock_Config();
??? MX_GPIO_Init();

??? // 创建任务
??? xTaskCreate(vTask1, "Task1", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
??? xTaskCreate(vTask2, "Task2", configMINIMAL_STACK_SIZE, NULL, 2, NULL);
??? xTaskCreate(vTask3, "Task3", configMINIMAL_STACK_SIZE, NULL, 3, NULL);

??? // 启动调度器
??? vTaskStartScheduler();

??? // 代码不应该运行到这里,如果运行到这里说明发生了错误
??? while (1) {
??????? // 处理错误
??? }
}

任务的删除

删除任务时使用的函数如下:

void vTaskDelete( TaskHandle_t xTaskToDelete );

参数说明:

参数描述
pvTaskCode

任务句柄,使用xTaskCreate创建任务时可以得到一个句柄。

也可传入NULL,这表示删除自己。

?

怎么删除任务?举个不好的例子(例子来自韦东山):

????????自杀:vTaskDelete(NULL)

????????被杀:别的任务执行vTaskDelete(pvTaskCode),pvTaskCode是自己的句柄

????????杀人:执行vTaskDelete(pvTaskCode),pvTaskCode是别的任务的句柄

删除任务的例子

????????在FreeRTOS中,任务的删除通常是在任务自身的函数中通过调用vTaskDelete()来完成的。其中演示了如何在任务中删除其他任务:

#include "stm32f1xx.h"
#include "FreeRTOS.h"
#include "task.h"

// 函数原型
void SystemClock_Config(void);
static void MX_GPIO_Init(void);

// 任务函数
TaskHandle_t Task1_Handle, Task2_Handle, Task3_Handle;

void vTask1(void *pvParameters) {
??? while (1) {
??????? // 任务1的处理逻辑
??????? vTaskDelay(1000 / portTICK_PERIOD_MS); // 每隔1秒执行一次
??? }
}

void vTask2(void *pvParameters) {
??? while (1) {
??????? // 任务2的处理逻辑
??????? vTaskDelay(2000 / portTICK_PERIOD_MS); // 每隔2秒执行一次

??????? // 删除任务1
??????? vTaskDelete(Task1_Handle);
??? }
}

void vTask3(void *pvParameters) {
??? while (1) {
??????? // 任务3的处理逻辑
??????? vTaskDelay(3000 / portTICK_PERIOD_MS); // 每隔3秒执行一次

??????? // 删除任务2
??????? vTaskDelete(Task2_Handle);
??? }
}

int main(void) {

??? // 硬件初始化
??? HAL_Init();
??? SystemClock_Config();
??? MX_GPIO_Init();

??? // 创建任务1
??? xTaskCreate(vTask1, "Task1", configMINIMAL_STACK_SIZE, NULL, 1, &Task1_Handle);

??? // 创建任务2
??? xTaskCreate(vTask2, "Task2", configMINIMAL_STACK_SIZE, NULL, 2, &Task2_Handle);

??? // 创建任务3
??? xTaskCreate(vTask3, "Task3", configMINIMAL_STACK_SIZE, NULL, 3, &Task3_Handle);

??? // 启动调度器
??? vTaskStartScheduler();

??? // 代码不应该运行到这里,如果运行到这里说明发生了错误
??? while (1) {
??????? // 处理错误
??? }
}

// 系统时钟配置
void SystemClock_Config(void) {
??? // 与前述代码相同,这里省略
}

// GPIO初始化
static void MX_GPIO_Init(void) {
??? // 与前述代码相同,这里省略
}

????????在这个例子中,任务2和任务3中分别使用了vTaskDelete()函数删除了任务1和任务2。请注意,在FreeRTOS中,任务只能删除自己或优先级较低的任务,因此任务2可以删除任务1,而任务3可以删除任务2。任务的删除是一种谨慎操作,需要确保被删除的任务没有在其他地方被引用或使用。

文章来源:https://blog.csdn.net/m0_56694518/article/details/134909600
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。