【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

相关文章

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

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

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

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

安全管理体系化的智慧油站开源了。

AI视频监控平台简介 AI视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒,省去繁琐重复的适配流程,实现芯片、算法、应用的全流程组合,从而大大减少企业级应用约95%的开发成本。用户只需在界面上进行简单的操作,就可以实现全视频的接入及布控。摄像头管理模块用于多种终端设备、智能设备的接入及管理。平台支持包括摄像头等终端感知设备接入,为整个平台提

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

FreeRTOS-基本介绍和移植STM32

FreeRTOS-基本介绍和STM32移植 一、裸机开发和操作系统开发介绍二、任务调度和任务状态介绍2.1 任务调度2.1.1 抢占式调度2.1.2 时间片调度 2.2 任务状态 三、FreeRTOS源码和移植STM323.1 FreeRTOS源码3.2 FreeRTOS移植STM323.2.1 代码移植3.2.2 时钟中断配置 一、裸机开发和操作系统开发介绍 裸机:前后台系

Sentinel 高可用流量管理框架

Sentinel 是面向分布式服务架构的高可用流量防护组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。 Sentinel 具有以下特性: 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应

FreeRTOS内部机制学习03(事件组内部机制)

文章目录 事件组使用的场景事件组的核心以及Set事件API做的事情事件组的特殊之处事件组为什么不关闭中断xEventGroupSetBitsFromISR内部是怎么做的? 事件组使用的场景 学校组织秋游,组长在等待: 张三:我到了 李四:我到了 王五:我到了 组长说:好,大家都到齐了,出发! 秋游回来第二天就要提交一篇心得报告,组长在焦急等待:张三、李四、王五谁先写好就交谁的

NGINX轻松管理10万长连接 --- 基于2GB内存的CentOS 6.5 x86-64

转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=190176&id=4234854 一 前言 当管理大量连接时,特别是只有少量活跃连接,NGINX有比较好的CPU和RAM利用率,如今是多终端保持在线的时代,更能让NGINX发挥这个优点。本文做一个简单测试,NGINX在一个普通PC虚拟机上维护100k的HTTP

PMBOK® 第六版 规划进度管理

目录 读后感—PMBOK第六版 目录 规划进度管理主要关注为整个项目期间的进度管理提供指南和方向。以下是两个案例,展示了进度管理中的复杂性和潜在的冲突: 案例一:近期,一个长期合作的客户因政策要求,急需我们为多家医院升级一个小功能。在这个过程中出现了三个主要问题: 在双方确认接口协议后,客户私自修改接口并未通知我们,直到催进度时才发现这个问题关于UI设计的部分,后台开发人员未将其传递给

FreeRTOS学习笔记(六)队列

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、队列的基本内容1.1 队列的引入1.2 FreeRTOS 队列的功能与作用1.3 队列的结构体1.4 队列的使用流程 二、相关API详解2.1 xQueueCreate2.2 xQueueSend2.3 xQueueReceive2.4 xQueueSendFromISR2.5 xQueueRecei