FreeRTOS的任务创建/删除(详解,很简单)
什么是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。任务的删除是一种谨慎操作,需要确保被删除的任务没有在其他地方被引用或使用。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!