【ESP-IDF FreeRTOS】任务管理

2024-08-22 17:36
文章标签 管理 任务 idf freertos esp

本文主要是介绍【ESP-IDF FreeRTOS】任务管理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

上一篇我们介绍了延时函数。讲到了因为FreeRTOS是多任务的,因此我们使用延时函数可以将当前任务挂起,就可以让CPU去执行其他任务,等到延时时间结束,再接着执行之前的任务,这样可以充分利用CPU的性能。

那么既然ESP-IDF FreeRTOS是多任务的,那么我们必然是可以创建任务的,之前一直用的app_main是ESP-IDF FreeRTOS默认创建的main任务。

创建任务可用的函数有俩,如果是多核的ESP32,那么可以使用四个(不过本质上还是两个,这个后面再说)

static inline BaseType_t xTaskCreate(TaskFunction_t pxTaskCode, const char *const pcName, const configSTACK_DEPTH_TYPE usStackDepth, void *const pvParameters, UBaseType_t uxPriority, TaskHandle_t *const pxCreatedTask)

上面这个函数就可以创建出一个新的任务并且添加到准备运行的任务列表中,也就是说我们用这个函数一旦创建了任务,那么马上就会准备开始执行。

第一个参数传入函数指针,这个函数里的内容就是我们这个任务要执行的内容。需要注意的是这个任务无返回值,可以接受一个void*类型的参数(也可以不接收)。另外这个函数要么内部是死循环,要么需要自己把自己杀死,否则编译报错。具体如何杀死自己,后面会介绍。

我们可以看看参数一的类型,看看这个任务函数的原型是什么。

类型很简单,就是无返回值,可以有个void*类型参数的函数。

第二个参数传入一个字符串表示这个任务的名字,这个随便起,不懂起名字的小伙伴可以和自己的任务函数的函数名一致(记得是传入字符串)。

第三个参数传入这个任务函数的栈大小,单位是字(word),我们ESP32是32位的,因此一个字是4个Byte,这个单位换算要清楚。每个任务函数都有各自独立的栈,我们需要各自指定栈的大小,这个栈的大小我们大概的估计一下就行,不用很精确。一般来说不确定就给个1024,多余总比不够用好,不过还是要具体问题具体分析。

如果这个栈大小给的少了,那么当执行这个任务的时候,会导致我们ESP32不停地重启。

第四个参数传入给任务函数传的参数,没有就填NULL。

第五个是优先级,数值越小优先级越小(注意这个,这边和STM32中的优先级不一样)。优先级用于任务的调度,也就是说多个任务理论上是可以看成是并行的,这个我们后面会验证。

优先级的数值我们可以根据宏定义configMAX_PRIORITIES来判断,这个宏定义就是我们能用的最大的优先级+1(优先级从0开始,一共有configMAX_PRIORITIES这么多个的等级)。

第六个是任务句柄,是传出参数,我们可以先定义一个任务句柄,然后在创建任务的时候传进去。任务句柄主要是用来删除任务的,当然删除任务并不一定是要使用到任务句柄,也可以让任务自杀,这个后面会说。

多个任务可以共用同一个函数。可以使用传入的参数来区分是哪个任务调用的。

那么下面就简单举个例子。

下面创建了两个任务,调用的是同一个函数,通过参数的不同来打印不同的内容。

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"void test(void* who){int* val = who;printf("this is test%d\r\n",*val);while(1){printf("hello%d\r\n",*val);vTaskDelay(pdMS_TO_TICKS(1000));}
}void app_main(void){TaskHandle_t test1_handle,test2_handle;int t1 = 1,t2 = 2;xTaskCreate(test,"test1",1024*2,&t1,configMAX_PRIORITIES/2,&test1_handle);xTaskCreate(test,"test2",1024*2,&t2,configMAX_PRIORITIES/2,&test2_handle);printf("this is main\r\n");while(1){printf("world\r\n");vTaskDelay(pdMS_TO_TICKS(1000));}
}

上面这个算是我们用的最多的创建任务的函数了,它会帮我们自动分配内存,当然我们也可以使用其他函数来自己给任务分配内存。

static inline TaskHandle_t xTaskCreateStatic(TaskFunction_t pxTaskCode, const char *const pcName, const uint32_t ulStackDepth, void *const pvParameters, UBaseType_t uxPriority, StackType_t *const puxStackBuffer, StaticTask_t *const pxTaskBuffer)

前面几个参数和自动分配内存的函数的参数是一样的,不一样的是最后两个。

分别是我们给它静态分配的栈,最后一个是一个结构体,它保存的是任务的数据结构。

且这个函数的返回值是任务的句柄,而我们动态分配的函数的任务句柄是通过最后一个传出参数给我们的,也就是说静态分配比动态分配要多出两个参数。

静态分配内存比较复杂,且我们大多情况使用动态分配内存的函数创建任务就够了,所以这边就贴上官方的示例代码,感兴趣的小伙伴看看就行。

// Dimensions of the buffer that the task being created will use as its stack.
// NOTE:  This is the number of words the stack will hold, not the number of
// bytes.  For example, if each stack item is 32-bits, and this is set to 100,
// then 400 bytes (100 * 32-bits) will be allocated.
#define STACK_SIZE 200
// Structure that will hold the TCB of the task being created.
StaticTask_t xTaskBuffer;
// Buffer that the task being created will use as its stack.  Note this is
// an array of StackType_t variables.  The size of StackType_t is dependent on
// the RTOS port.
StackType_t xStack[ STACK_SIZE ];
// Function that implements the task being created.
void vTaskCode( void * pvParameters ){
// The parameter value is expected to be 1 as 1 is passed in the
// pvParameters value in the call to xTaskCreateStatic().configASSERT( ( uint32_t ) pvParameters == 1UL );for( ;; ){// Task code goes here.}
}// Function that creates a task.
void vOtherFunction( void ){TaskHandle_t xHandle = NULL;// Create the task without using any dynamic memory allocation.xHandle = xTaskCreateStatic(vTaskCode,       // Function that implements the task."NAME",          // Text name for the task.STACK_SIZE,      // Stack size in words, not bytes.( void * ) 1,    // Parameter passed into the task.tskIDLE_PRIORITY,// Priority at which the task is created.xStack,          // Array to use as the task's stack.&xTaskBuffer );  // Variable to hold the task's data structure.
// puxStackBuffer and pxTaskBuffer were not NULL, so the task will have
// been created, and xHandle will be the task's handle.  Use the handle
// to suspend the task.vTaskSuspend( xHandle );
}

细心的小伙伴可能发现了,上面出现了一个我们没见过的函数vTaskSuspend。

我们先简单介绍一下任务的几种状态。

首先我们通过ESP-IDF FreeRTOS可以创建多个任务,正常情况下任务是“并行”的(看起来是并行的,实际上是通过分割时间片来轮流运行,因为太快了所以在我们看来就是并行的),但严格说来,在一个时间点上,CPU只能执行一个任务(多核除外,多核是每个核都可以执行一个任务),那么正在执行的任务就处在运行状态,而其他没执行的,在等待执行的任务就处在就绪状态。

如果有个任务执行了vTaskDelay或者xTaskDelayUntil,那么它会暂时挂起,也就是阻塞状态,当然除了这俩函数会让任务阻塞,其他还有很多办法,这个我们后面再说。

如果有个任务,我现在不想让它执行了,那我可以删除它,可我万一以后还需要它,我只要它暂时消失怎么办呢?那么我们可以让它处于暂停状态,等我要它了,我再唤醒它,让它再次处于就绪状态。

而vTaskSuspend就是让任务处于暂停状态的函数。

void vTaskSuspend(TaskHandle_t xTaskToSuspend)

传入任务句柄即可将此任务暂停,如果传入的是NULL,那么是暂停自己(调用这个函数的任务)。

当我们需要它了,我们用下面这个函数再次唤醒它。

void vTaskResume (TaskHandle_t xTaskToResume)

传入任务句柄即可唤醒对应任务,但是需要注意的是,不能通过传入NULL来自己唤醒自己,这个应该不难理解。

接下来来个小例子示范一下这俩函数。

在主函数里计数,计到3秒的时候把test任务暂停(打印hello),计到六秒的时候再继续。

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"void test(void*){printf("this is test\r\n");while(1){printf("hello\r\n");vTaskDelay(pdMS_TO_TICKS(1000));}
}void app_main(void){TaskHandle_t test1_handle;xTaskCreate(test,"test1",1024*2,NULL,configMAX_PRIORITIES/2,&test1_handle);printf("this is main\r\n");int count = 0;TickType_t curTime = xTaskGetTickCount();while(1){printf("count is %d\r\n",++count);if(count == 3) vTaskSuspend(test1_handle);else if(count ==6) vTaskResume(test1_handle);xTaskDelayUntil(&curTime,pdMS_TO_TICKS(1000));}
}

关于任务状态,我们再介绍一个函数。

void vTaskDelete(TaskHandle_t xTaskToDelete)

传入任务句柄来删除对应任务,如果传入NULL,那么当前任务自己删除自己(紫砂)。

关于创建任务,ESP-IDF FreeRTOS还提供了另外两个函数。

BaseType_t xTaskCreatePinnedToCore(TaskFunction_t pxTaskCode, const char *const pcName, const uint32_t ulStackDepth, void *const pvParameters, UBaseType_t uxPriority, TaskHandle_t *const pxCreatedTask, const BaseType_t xCoreID)

TaskHandle_t xTaskCreateStaticPinnedToCore(TaskFunction_t pxTaskCode, const char *const pcName, const uint32_t ulStackDepth、void *const pvParameters、UBaseType_t uxPriority、StackType_t *const puxStackBuffer、 StaticTask_t *const pxTaskBuffer, const BaseType_t xCoreID)

简单来说这两个函数就是乐鑫为了适配多核系统给整出来的,也就是说这俩函数只能在ESP-IDF FreeRTOS里使用。它们相较于上面动态分配和静态分配的创建任务的函数就多了一个参数,这个参数来指定我们这个任务固定到哪个核心上去。

但我们平常使用不用专门去记这两个,第一是普适性不强,只能在ESP-IDF FreeRTOS中,并且是双核的ESP32才能使用。第二是就算我们使用了xTaskCreate,如果我们的芯片支持双核,那么它底层还是会帮我们调用多核版本的。

默认帮我设置的核心是不固定核心,可以在任意一个核心上运行,这也算是自动分配了。

我们可以使用下面这个函数获取当前内核管理的任务数。

UBaseType_t uxTaskGetNumberOfTasks(void)

这篇关于【ESP-IDF FreeRTOS】任务管理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

高效管理你的Linux系统: Debian操作系统常用命令指南

《高效管理你的Linux系统:Debian操作系统常用命令指南》在Debian操作系统中,了解和掌握常用命令对于提高工作效率和系统管理至关重要,本文将详细介绍Debian的常用命令,帮助读者更好地使... Debian是一个流行的linux发行版,它以其稳定性、强大的软件包管理和丰富的社区资源而闻名。在使用

Python Invoke自动化任务库的使用

《PythonInvoke自动化任务库的使用》Invoke是一个强大的Python库,用于编写自动化脚本,本文就来介绍一下PythonInvoke自动化任务库的使用,具有一定的参考价值,感兴趣的可以... 目录什么是 Invoke?如何安装 Invoke?Invoke 基础1. 运行测试2. 构建文档3.

解决Cron定时任务中Pytest脚本无法发送邮件的问题

《解决Cron定时任务中Pytest脚本无法发送邮件的问题》文章探讨解决在Cron定时任务中运行Pytest脚本时邮件发送失败的问题,先优化环境变量,再检查Pytest邮件配置,接着配置文件确保SMT... 目录引言1. 环境变量优化:确保Cron任务可以正确执行解决方案:1.1. 创建一个脚本1.2. 修

Java实现任务管理器性能网络监控数据的方法详解

《Java实现任务管理器性能网络监控数据的方法详解》在现代操作系统中,任务管理器是一个非常重要的工具,用于监控和管理计算机的运行状态,包括CPU使用率、内存占用等,对于开发者和系统管理员来说,了解这些... 目录引言一、背景知识二、准备工作1. Maven依赖2. Gradle依赖三、代码实现四、代码详解五

如何使用celery进行异步处理和定时任务(django)

《如何使用celery进行异步处理和定时任务(django)》文章介绍了Celery的基本概念、安装方法、如何使用Celery进行异步任务处理以及如何设置定时任务,通过Celery,可以在Web应用中... 目录一、celery的作用二、安装celery三、使用celery 异步执行任务四、使用celery

什么是cron? Linux系统下Cron定时任务使用指南

《什么是cron?Linux系统下Cron定时任务使用指南》在日常的Linux系统管理和维护中,定时执行任务是非常常见的需求,你可能需要每天执行备份任务、清理系统日志或运行特定的脚本,而不想每天... 在管理 linux 服务器的过程中,总有一些任务需要我们定期或重复执行。就比如备份任务,通常会选在服务器资

SpringBoot使用minio进行文件管理的流程步骤

《SpringBoot使用minio进行文件管理的流程步骤》MinIO是一个高性能的对象存储系统,兼容AmazonS3API,该软件设计用于处理非结构化数据,如图片、视频、日志文件以及备份数据等,本文... 目录一、拉取minio镜像二、创建配置文件和上传文件的目录三、启动容器四、浏览器登录 minio五、

IDEA中的Kafka管理神器详解

《IDEA中的Kafka管理神器详解》这款基于IDEA插件实现的Kafka管理工具,能够在本地IDE环境中直接运行,简化了设置流程,为开发者提供了更加紧密集成、高效且直观的Kafka操作体验... 目录免安装:IDEA中的Kafka管理神器!简介安装必要的插件创建 Kafka 连接第一步:创建连接第二步:选

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

软考系统规划与管理师考试证书含金量高吗?

2024年软考系统规划与管理师考试报名时间节点: 报名时间:2024年上半年软考将于3月中旬陆续开始报名 考试时间:上半年5月25日到28日,下半年11月9日到12日 分数线:所有科目成绩均须达到45分以上(包括45分)方可通过考试 成绩查询:可在“中国计算机技术职业资格网”上查询软考成绩 出成绩时间:预计在11月左右 证书领取时间:一般在考试成绩公布后3~4个月,各地领取时间有所不同