FreeRTOS(二)FreeRTOS任务创建和删除

2024-09-05 04:36
文章标签 创建 删除 任务 freertos

本文主要是介绍FreeRTOS(二)FreeRTOS任务创建和删除,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

函数介绍

动态创建任务函数

函数xTaxkCreate()

任务句柄

任务句柄的作用

获取任务句柄

 使用任务句柄

实现动态函数创建任务流程

动态创建任务函数内部实现

静态创建任务函数

实现静态函数创建任务流程

静态创建内部实现

删除任务函数

参数说明

功能描述

注意事项

动态方法的任务创建和删除


FreeRTOS 最基本的功能就是任务管理,而任务管理最基本的操作就是创建和删除任务

函数介绍

动态创建任务函数

动态创建任务是 FreeRTOS 中的一项基本功能,它允许你在运行时根据需要创建任务,而不是在系统启动时静态定义所有任务。动态创建任务提供了更大的灵活性,特别是在需要根据系统运行状态或外部事件来调整任务数量和类型的情况下。

任务的任务控制块以及任务的栈空间所需的内存,均由 FreeRTOS FreeRTOS 管理的堆中分配

函数xTaxkCreate()

使用函数xTaskCreate()来创建任务的话那么这些 所需的RAM就会自动的从FreeRTOS的堆中分配

 同时新创建的任务默认就 是就绪态的,如果当前没有比它更高优先级的任务运行那么此任务就会立即进入运行态开始运 行,不管在任务调度器启动前还是启动后,都可以创建任务

函数原型 

BaseType_t xTaskCreate(TaskFunction_t pxTaskCode,  // 任务函数const char * const pcName,  // 任务名称configSTACK_DEPTH_TYPE usStackDepth,  // 堆栈深度void *pvParameters,        // 传递给任务的参数UBaseType_t uxPriority,     // 任务优先级TaskHandle_t * const pxCreatedTask  // 任务句柄
);

参数说明

  • pxTaskCode:这是一个函数指针,指向任务函数。任务函数是新创建的任务将要执行的函数。
  • pcName:这是一个指向字符串的指针,表示任务的名称。这个名称主要用于调试目的,例如在任务列表中识别任务。(任务名字,一般用于追踪和调试,任务名字长度不能超过。 configMAX_TASK_NAME_LEN。)
  • usStackDepth:这个参数指定任务堆栈的深度,单位是字节。堆栈深度应该足够大,以存储任务执行期间的所有局部变量和函数调用的返回地址。(任务堆栈大小,注意实际申请到的堆栈是usStackDepth的4倍。其中空闲任 务的任务堆栈大小为configMINIMAL_STACK_SIZE。)
  • pvParameters:这是一个可选参数,可以传递给任务函数。这允许在任务创建时传递初始化数据。(传递给任务函数的参数。)
  • uxPriority:这个参数指定任务的优先级。在 FreeRTOS 中,任务优先级的范围是从 0 到 configMAX_PRIORITIES - 1,其中 0 是最高的优先级。
  • pxCreatedTask:这是一个可选的输出参数,它是一个指向 TaskHandle_t 类型的指针。如果提供了这个参数,函数将在成功创建任务后,将新创建的任务的句柄存储在这里。(任务句柄,任务创建成功以后会返回此任务的任务句柄,这个句柄其实就是 任务的任务堆栈。此参数就用来保存这个任务句柄。其他API函数可能会使用到这个句柄。)

返回值

  • pdPASS:表示任务成功创建。
  • errC:表示任务创建失败,通常是因为内存不足。

eg. 

//创建LED0任务xTaskCreate((TaskFunction_t )led0_task,     	  (const char*    )"led0_task",   	(uint16_t       )LED0_STK_SIZE, (void*          )NULL,				(UBaseType_t    )LED0_TASK_PRIO,	(TaskHandle_t*  )&LED0Task_Handler); 

任务句柄

在 FreeRTOS 中,任务句柄(Task Handle)是一个非常重要的概念,它为任务提供了一个唯一的标识符,使得任务间的各种操作和交互成为可能。任务句柄是一个指向任务控制块(Task Control Block, TCB)的指针,每个任务都有一个 TCB,其中包含了任务的各种状态信息和控制数据。

任务句柄的作用
  1. 任务识别:任务句柄提供了一种识别和引用特定任务的方式。
  2. 任务操作:通过任务句柄,可以对任务进行各种操作,如挂起(suspend)、恢复(resume)、删除(delete)等。
  3. 传递信息:任务句柄可以作为参数传递给其他任务或函数,用于任务间的通信和同步。
获取任务句柄

任务句柄可以通过以下几种方式获取:

1.在任务创建时:使用 xTaskCreate()xTaskCreateStatic() 创建任务时,可以通过传递一个指向 TaskHandle_t 的指针来获取新创建任务的句柄。

TaskHandle_t xTaskHandle = NULL;
xTaskCreate(vTaskFunction, "MyTask", STACK_SIZE, NULL, PRIORITIES, &xTaskHandle);

2.通过任务的名称:如果你知道任务的名称,可以使用 xTaskGetHandle() 函数来获取任务的句柄。

TaskHandle_t xTaskHandle;
xTaskGetHandle("MyTaskName", &xTaskHandle);
 使用任务句柄

1.删除任务:使用 vTaskDelete() 函数删除任务。

vTaskDelete(xTaskHandle);

2. 挂起任务:使用 vTaskSuspend() 函数挂起任务。

vTaskSuspend(xTaskHandle);

3. 恢复任务:使用 vTaskResume() 函数恢复被挂起的任务。

vTaskResume(xTaskHandle);

4. 获取任务状态:使用 uxTaskGetState() 函数获取任务的状态。

UBaseType_t xState = uxTaskGetState(xTaskHandle);

5. 等待任务通知:使用 ulTaskNotifyTake() 函数等待任务的通知。

uint32_t ulValue;
ulValue = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

实现动态函数创建任务流程

1、将宏configSUPPORT_DYNAMIC_ALLOCATION 配置为 1

#define configSUPPORT_DYNAMIC_ALLOCATION 1

2、定义函数入口参数

任务函数通常需要一个参数,这个参数可以是 void* 类型,允许你传递任意类型的数据给任务。这个参数在任务函数内部可以被转换为需要的类型。

3、编写任务函数

任务函数是任务执行的代码。它必须是一个无限循环,因为任务函数一旦返回,任务就会结束,除非在任务函数中显式调用 vTaskDelete(NULL) 来删除自己。

void vTaskFunction(void *pvParameters)
{for(;;){// 任务代码}
}

 4、创建任务

使用 xTaskCreate() 函数来动态创建任务。你需要提供任务函数、任务名称、堆栈大小、任务参数和优先级。

#include "FreeRTOS.h"
#include "task.h"void StartTask(void)
{TaskHandle_t xHandle = NULL;// 定义任务参数MyParameters_t xParams;xParams.someValue = 10;// 创建任务if(xTaskCreate(vTaskFunction, "MyTask", configMINIMAL_STACK_SIZE, &xParams, tskIDLE_PRIORITY + 1, &xHandle) == pdPASS){// 任务创建成功}else{// 任务创建失败,处理错误}
}

 5、启动调度器

在主函数或适当的初始化代码中,调用 vTaskStartScheduler() 来启动任务调度器。

int main(void)
{// 初始化硬件和库// ...// 创建任务StartTask();// 启动调度器vTaskStartScheduler();// 如果调度器启动成功,以下代码将永远不会执行for(;;);
}

动态创建任务函数内部实现

1、申请堆栈内存&任务控制块内存

首先,xTaskCreate() 会为新任务分配内存空间,包括任务控制块(TCB)和堆栈。这些内存通常通过调用内存管理函数(如 pvPortMalloc())来分配。

2、TCB结构体成员赋值

每个任务都有一个与之关联的 TCB,它包含了任务的各种状态信息和控制数据。xTaskCreate() 会初始化这个 TCB,包括设置任务的入口函数、优先级、堆栈指针等。

tskTCB *pxNewTCB = (tskTCB *)pvPortMalloc(sizeof(tskTCB));
if (pxNewTCB != NULL) {// 初始化 TCBpxNewTCB->pxTopOfStack = NULL; // 堆栈指针pxNewTCB->uxPriority = uxPriority;// ... 其他成员的初始化
}

3、设置任务堆栈

接下来,xTaskCreate() 会为任务分配堆栈空间,并设置初始堆栈的内容。这通常涉及到将返回地址、寄存器状态等压入堆栈,以便任务在开始执行时能够正确地跳转到任务函数。

StackType_t *pxStack = (StackType_t *)pvPortMalloc(sizeof(StackType_t) * usStackDepth);
if (pxStack != NULL) {// 设置堆栈,使其准备好任务函数的第一次调用pxNewTCB->pxTopOfStack = pxStack + usStackDepth - 1; // 堆栈顶指针// 压入初始值,如返回地址、寄存器等pxNewTCB->pxTopOfStack = pxPortInitialiseStack(pxNewTCB->pxTopOfStack, pxTaskCode, pvParameters);
}

4、添加新任务到就绪列表中

一旦 TCB 和堆栈被正确设置,xTaskCreate() 会将任务添加到就绪列表中。这样,任务调度器就可以根据任务的优先级来调度任务的执行。

if (xTaskGenericCreate(pxNewTCB, ...) == pdPASS) {// 任务创建成功
}

5、返回任务句柄

如果提供了任务句柄的指针,xTaskCreate() 会将新创建的任务的句柄存储在这个位置。这个句柄可以用于后续的任务控制操作,如删除、挂起或恢复任务

静态创建任务函数

静态创建任务是 FreeRTOS 提供的一种任务创建方式,它允许你在编译时分配任务的堆栈和任务控制块(TCB),而不是在运行时动态分配。这种方式通常用于内存受限的系统,因为它可以减少运行时的内存碎片和内存分配的开销。

函数xTaxkCreate()

函数 xTaskCreateStatic() 允许在 FreeRTOS 中静态创建任务,这意味着任务的堆栈和任务控制块(TCB)是在编译时静态分配的,而不是在运行时动态分配。这种方式对于内存资源受限的系统非常有用,因为它可以减少运行时的内存碎片和分配开销。

函数原型

BaseType_t xTaskCreateStatic(TaskFunction_t pxTaskCode,  // 任务函数const char * const pcName,  // 任务名称configSTACK_DEPTH_TYPE ulStackDepth,  // 堆栈深度void *pvParameters,        // 传递给任务的参数UBaseType_t uxPriority,     // 任务优先级StackType_t *pxStackBuffer, // 静态堆栈缓冲区StaticTask_t *pxTaskBuffer  // 静态任务控制块
);

参数说明

  • pxTaskCode:指向任务函数的指针。
  • pcName:任务的名称,主要用于调试目的。
  • ulStackDepth:任务堆栈的深度,单位是堆栈的“词”数,而不是字节。在32位系统中,一个“词”通常是4个字节。
  • pvParameters:传递给任务函数的可选参数。
  • uxPriority:任务的优先级,数值越小,优先级越高。
  • pxStackBuffer:指向静态堆栈缓冲区的指针,该缓冲区必须足够大,以存储任务执行期间的所有局部变量和函数调用的返回地址。
  • pxTaskBuffer:指向静态任务控制块的指针,该控制块用于存储任务的状态信息。

返回值

  • pdPASS:任务成功创建。
  • errC:任务创建失败,通常是因为提供的堆栈或任务控制块大小不正确。

实现静态函数创建任务流程

配置宏 configSUPPORT_STATIC_ALLOCATION

在 FreeRTOS 的配置文件 FreeRTOSConfig.h 中,你需要定义一些宏来启用特定的功能。对于静态内存分配,关键的宏是 configSUPPORT_STATIC_ALLOCATION。将其设置为 1 表示你打算在代码中静态分配任务的堆栈和任务控制块(TCB)。

#define configSUPPORT_STATIC_ALLOCATION 1

定义空闲任务和定时器任务的堆栈及 TCB

FreeRTOS 要求你为系统的关键任务提供内存:

  • 空闲任务 (Idle Task):当没有其他任务运行时,空闲任务运行。它通常执行一些低优先级的工作,如内存碎片检测或简单的监控。
  • 定时器任务 (Timer Task):管理软件定时器和执行周期性任务。
// 空闲任务堆栈
static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];// 空闲任务 TCB
static StaticTask_t xIdleTaskTCB;// 定时器任务堆栈
static StackType_t xTimerStack[configTIMER_TASK_STACK_DEPTH];// 定时器任务 TCB
static StaticTask_t xTimerTaskTCB;

实现两个接口函数

FreeRTOS 提供了两个可选的钩子函数,允许你为空闲任务和定时器任务提供静态内存:

  • vApplicationGetIdleTaskMemory():返回指向空闲任务堆栈和 TCB 的指针。
  • vApplicationGetTimerTaskMemory():返回指向定时器任务堆栈和 TCB 的指针。

定义函数入口参数 

任务函数可能需要参数来初始化或配置其行为。这些参数在任务创建时传递,并在任务函数中通过 pvParameters 指针访问。

void vTaskFunction(void *pvParameters)
{// 使用 pvParameters
}

编写任务函数

任务函数是任务执行的代码。它通常包含一个无限循环,因为任务不会自行结束,除非显式删除。

void vTaskFunction(void *pvParameters)
{for(;;){// 任务逻辑}
}

静态创建内部实现

TCB 结构体成员赋值: 在静态创建任务时,需要初始化任务控制块(TCB)的各个成员,包括任务的入口函数、堆栈指针、任务状态、优先级等。

添加新任务到就绪列表中: 一旦任务的 TCB 被正确初始化,新创建的任务会被添加到就绪列表中。任务调度器会根据任务的优先级和状态来调度任务的执行。

删除任务函数

函数 vTaskDelete() 是 FreeRTOS 中用于删除任务的 API。当一个任务完成了它的工作或者不再需要时,你可以使用这个函数来删除它,释放它所使用的资源,包括堆栈和任务控制块(TCB)

void vTaskDelete( TaskHandle_t xTaskToDelete );

参数说明

  • xTaskToDelete:这是一个指向要删除的任务的任务控制块(TCB)的指针,即 TaskHandle_t 类型。如果传递 NULL,那么 vTaskDelete() 将删除调用它的任务。

功能描述

当调用 vTaskDelete() 时,会发生以下事情:

  1. 资源释放:任务的堆栈和 TCB 将被释放回 FreeRTOS 的内存管理池中,以便将来可以被其他任务或系统组件使用。

  2. 任务移除:任务将从任何任务列表(就绪列表、阻塞列表等)中移除。

  3. 调度器更新:调度器将更新,以确保不会尝试调度一个已经被删除的任务。

  4. 上下文恢复:如果被删除的任务是当前运行的任务(即调用 vTaskDelete() 的任务),那么调度器将立即进行任务切换,恢复到另一个任务的上下文。

实例:

void vATaskFunction( void *pvParameters )
{// 任务代码// ...// 任务完成工作后,删除自身vTaskDelete( NULL );
}// 在另一个任务或初始化代码中创建任务
TaskHandle_t xTaskHandle = NULL;
xTaskCreate( vATaskFunction, "A Task", STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, &xTaskHandle );// 稍后,如果需要删除这个任务,可以调用
vTaskDelete( xTaskHandle );

注意事项

  1. 慎用 NULL:调用 vTaskDelete(NULL); 会删除调用它的任务。这在任务完成其工作并希望自我删除时非常有用,但需要谨慎使用,以避免意外删除当前正在运行的任务。

  2. 资源回收:虽然任务被删除了,但是它所持有的任何同步原语(如信号量、互斥锁等)不会自动释放。你需要在删除任务之前手动释放这些资源。

  3. 删除阻塞任务:如果尝试删除一个处于阻塞状态的任务,那么任务将被删除,但任何等待该任务的同步原语也将被释放。

  4. 调度器行为vTaskDelete() 可以影响调度器的行为。例如,如果删除了一个高优先级任务,那么调度器可能会立即调度其他任务。

  5. 内存碎片:在某些配置下,频繁创建和删除任务可能会导致内存碎片。在这种情况下,考虑使用静态内存分配或调整内存管理策略。

动态方法的任务创建和删除

实例代码:

//任务优先级
#define START_TASK_PRIO		1
//任务堆栈大小	
#define START_STK_SIZE 		128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define TASK1_TASK_PRIO		2
//任务堆栈大小	
#define TASK1_STK_SIZE 		128  
//任务句柄
TaskHandle_t Task1Task_Handler;
//任务函数
void task1_task(void *pvParameters);//任务优先级
#define TASK2_TASK_PRIO		3
//任务堆栈大小	
#define TASK2_STK_SIZE 		128  
//任务句柄
TaskHandle_t Task2Task_Handler;
//任务函数
void task2_task(void *pvParameters);
int main(void)
{NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4	 delay_init();	    				//延时函数初始化	 uart_init(115200);					//初始化串口LED_Init();		  					//初始化LED//创建开始任务xTaskCreate((TaskFunction_t )start_task,            //任务函数(const char*    )"start_task",          //任务名称(uint16_t       )START_STK_SIZE,        //任务堆栈大小(void*          )NULL,                  //传递给任务函数的参数(UBaseType_t    )START_TASK_PRIO,       //任务优先级(TaskHandle_t*  )&StartTask_Handler);   //任务句柄              vTaskStartScheduler();          //开启任务调度
}//开始任务任务函数
void start_task(void *pvParameters)
{taskENTER_CRITICAL();           //进入临界区//创建TASK1任务xTaskCreate((TaskFunction_t )task1_task,             (const char*    )"task1_task",           (uint16_t       )TASK1_STK_SIZE,        (void*          )NULL,                  (UBaseType_t    )TASK1_TASK_PRIO,        (TaskHandle_t*  )&Task1Task_Handler);   //创建TASK2任务xTaskCreate((TaskFunction_t )task2_task,     (const char*    )"task2_task",   (uint16_t       )TASK2_STK_SIZE,(void*          )NULL,(UBaseType_t    )TASK2_TASK_PRIO,(TaskHandle_t*  )&Task2Task_Handler); vTaskDelete(StartTask_Handler); //删除开始任务taskEXIT_CRITICAL();            //退出临界区
}//task1任务函数
void task1_task(void *pvParameters)
{u8 task1_num=0;while(1){task1_num++;	//任务执1行次数加1 注意task1_num1加到255的时候会清零!!LED0=!LED0;printf("任务1已经执行:%d次\r\n",task1_num);if(task1_num==5) {vTaskDelete(Task2Task_Handler);//任务1执行5次删除任务2printf("任务1删除了任务2!\r\n");}vTaskDelay(1000);                           //延时1s,也就是1000个时钟节拍	}
}//task2任务函数
void task2_task(void *pvParameters)
{u8 task2_num=0;while(1){task2_num++;	//任务2执行次数加1 注意task1_num2加到255的时候会清零!!LED1=!LED1;printf("任务2已经执行:%d次\r\n",task2_num);vTaskDelay(1000);                           //延时1s,也就是1000个时钟节拍	}
}

这篇关于FreeRTOS(二)FreeRTOS任务创建和删除的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1137965

相关文章

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

docker如何删除悬空镜像

《docker如何删除悬空镜像》文章介绍了如何使用Docker命令删除悬空镜像,以提高服务器空间利用率,通过使用dockerimage命令结合filter和awk工具,可以过滤出没有Tag的镜像,并将... 目录docChina编程ker删除悬空镜像前言悬空镜像docker官方提供的方式自定义方式总结docker

Python创建Excel的4种方式小结

《Python创建Excel的4种方式小结》这篇文章主要为大家详细介绍了Python中创建Excel的4种常见方式,文中的示例代码简洁易懂,具有一定的参考价值,感兴趣的小伙伴可以学习一下... 目录库的安装代码1——pandas代码2——openpyxl代码3——xlsxwriterwww.cppcns.c

使用Python在Excel中创建和取消数据分组

《使用Python在Excel中创建和取消数据分组》Excel中的分组是一种通过添加层级结构将相邻行或列组织在一起的功能,当分组完成后,用户可以通过折叠或展开数据组来简化数据视图,这篇博客将介绍如何使... 目录引言使用工具python在Excel中创建行和列分组Python在Excel中创建嵌套分组Pyt

使用Python在Excel中插入、修改、提取和删除超链接

《使用Python在Excel中插入、修改、提取和删除超链接》超链接是Excel中的常用功能,通过点击超链接可以快速跳转到外部网站、本地文件或工作表中的特定单元格,有效提升数据访问的效率和用户体验,这... 目录引言使用工具python在Excel中插入超链接Python修改Excel中的超链接Python

Spring Boot 整合 ShedLock 处理定时任务重复执行的问题小结

《SpringBoot整合ShedLock处理定时任务重复执行的问题小结》ShedLock是解决分布式系统中定时任务重复执行问题的Java库,通过在数据库中加锁,确保只有一个节点在指定时间执行... 目录前言什么是 ShedLock?ShedLock 的工作原理:定时任务重复执行China编程的问题使用 Shed

Android kotlin语言实现删除文件的解决方案

《Androidkotlin语言实现删除文件的解决方案》:本文主要介绍Androidkotlin语言实现删除文件的解决方案,在项目开发过程中,尤其是需要跨平台协作的项目,那么删除用户指定的文件的... 目录一、前言二、适用环境三、模板内容1.权限申请2.Activity中的模板一、前言在项目开发过程中,尤

解决IDEA使用springBoot创建项目,lombok标注实体类后编译无报错,但是运行时报错问题

《解决IDEA使用springBoot创建项目,lombok标注实体类后编译无报错,但是运行时报错问题》文章详细描述了在使用lombok的@Data注解标注实体类时遇到编译无误但运行时报错的问题,分析... 目录问题分析问题解决方案步骤一步骤二步骤三总结问题使用lombok注解@Data标注实体类,编译时

MySQL分表自动化创建的实现方案

《MySQL分表自动化创建的实现方案》在数据库应用场景中,随着数据量的不断增长,单表存储数据可能会面临性能瓶颈,例如查询、插入、更新等操作的效率会逐渐降低,分表是一种有效的优化策略,它将数据分散存储在... 目录一、项目目的二、实现过程(一)mysql 事件调度器结合存储过程方式1. 开启事件调度器2. 创

C#实现添加/替换/提取或删除Excel中的图片

《C#实现添加/替换/提取或删除Excel中的图片》在Excel中插入与数据相关的图片,能将关键数据或信息以更直观的方式呈现出来,使文档更加美观,下面我们来看看如何在C#中实现添加/替换/提取或删除E... 在Excandroidel中插入与数据相关的图片,能将关键数据或信息以更直观的方式呈现出来,使文档更