详细介绍:第三章 FreeRTOS 任务相关 API 函数

news/2025/11/22 16:36:42/文章来源:https://www.cnblogs.com/yangykaifa/p/19258079

        本章开始学习 FreeRTOS 任务相关的 API函数,重点学习函数的用法,先不深究函数内部实现原理,相关的原理性知识会在后面学习。
        本章分为如下几部分:
3.1 FreeRTOS 创建和删除任务
3.2 FreeRTOS 获取和设置任务优先级
3.3 FreeRTOS 挂起和恢复任务

3.1 FreeRTOS 创建和删除任务

        先以hello_word为模版创建一个基础工程,创建方法见第六章的6.1小节,创建好后,我们修改下文件目录名为01_FreeRTOS_Task,接下来就以该工程学习任务相关API函数。

        FreeRTOS 中用于创建、删除任务及任务相关的 API 函数如下表所示:

函数描述
xTaskCreate()动态方式创建任务
xTaskCreateStatic()静态方式创建任务
xTaskCreateRestricted()动态方式创建使用 MPU 限制的任务
xTaskCreateRestrictedStatic()静态方式创建使用 MPU 限制的任务
vTaskDelete()删除任务
vTaskList()获取任务列表
uxTaskGetStackHighWaterMark()获取任务的任务栈历史剩余最小值

表 3.1.1 任务相关 API 函数

1. 函数 xTaskCreate()

        此函数用于使用动态的方式创建任务, 任务的控制块以及任务的栈空间所需的内存,均由 FreeRTOS 从 FreeRTOS 管理的堆中分配, 若使用此函数, 需要在 FreeRTOSConfig.h(文件路径frameworks\esp-idf-v5.4.2\components\freertos\config\include\freertos\FreeRTOSConfig.h) 文件中将宏 configSUPPORT_DYNAMIC_ALLOCATION 配置为 1(默认已设置为1)。 此函数创建的任务会立刻进入就绪态, 由任务调度器调度运行。 函数原型如下所示:

BaseType_t xTaskCreate(TaskFunction_t pvTaskCode,          // 指向任务函数的指针const char * const pcName,          // 任务的可读名称(用于调试)configSTACK_DEPTH_TYPE usStackDepth, // 任务堆栈大小(以字为单位)void * const pvParameters,          // 传递给任务函数的参数指针UBaseType_t uxPriority,             // 任务的优先级TaskHandle_t * const pxCreatedTask  // 用于返回任务句柄的指针
);

        下表详细说明了每个参数的含义和注意事项:

参数

类型

说明与要点

​​pvTaskCode​​

TaskFunction_t

指向任务具体执行逻辑的函数指针。该函数必须为 void vTaskFunction(void *pvParameters)形式,并且通常应包含一个​​无限循环​​,任务不能返回(即函数内部不能有 return语句)。

​​pcName​​

const char * const

任务的描述性名称,主要用于调试和追踪。其长度受 FreeRTOSConfig.h中的 configMAX_TASK_NAME_LEN宏定义限制,超出部分会被截断。

​​usStackDepth​​

configSTACK_DEPTH_TYPE

指定任务堆栈的深度,​​单位是字(Word)​​,而不是字节。例如,在32位架构中,1个字等于4字节。如果需要1KB(1024字节)的堆栈空间,则此参数应设置为256。堆栈大小需根据任务中局部变量、函数调用深度等因素合理设置,过小会导致堆栈溢出。

​​pvParameters​​

void * const

一个泛型指针,用于在任务创建时向任务函数传递参数。可以指向一个变量、结构体或其它数据。如果不需要传递参数,则设置为 NULL

​​uxPriority​​

UBaseType_t

定义任务的优先级。​​数值越大,优先级越高​​。优先级的取值范围为0(最低,通常为 tskIDLE_PRIORITY)到 (configMAX_PRIORITIES - 1)。FreeRTOS调度器默认采用抢占式调度,高优先级任务会抢占低优先级任务的CPU使用权。

​​pxCreatedTask​​

TaskHandle_t * const

用于输出新创建任务的​​句柄(Handle)​​。该句柄可用于后续对任务进行操作,例如删除任务 (vTaskDelete)、挂起/恢复任务或修改优先级等。如果不需要使用该句柄,可以传入 NULL

表 3.1.2 函数 xTaskCreate()形参相关描述

        返回值与工作机制:

返回值

说明与要点

​​pdPASS​​

表示任务创建成功,并已放入就绪列表(Ready List),等待调度器调度。
pdFAIL​(或 errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY表示任务创建失败,通常是因系统堆内存不足,无法为任务的控制块(TCB)和堆栈分配所需空间。

表 3.1.3 函数 xTaskCreate()返回值描述

        动态内存分配​​:xTaskCreate通过动态方式创建任务,它会自动从 FreeRTOS 的堆中申请任务控制块(TCB)和任务栈所需的内存。在项目的 FreeRTOSConfig.h配置文件中,必须将宏 configSUPPORT_DYNAMIC_ALLOCATION定义为 1。任务被删除 (vTaskDelete) 后,其占用的内存会被空闲任务(Idle Task)自动回收,但是注意。


实验1:我们分别调用xTaskCreate()函数创建3个任务,其中task1和task2有入参,task3无入参,并在任务中打印相关信息。
        代码如下:

#include 
#include 
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
typedef struct TaskInfo
{TaskHandle_t xHandle;BaseType_t xResult;
} TaskInfo;
typedef struct PersonInfo
{char name[30];uint8_t age;
} PersonInfo;
void myTask1(void *para)
{uint16_t *data = (uint16_t *)para;while (1){printf("%s, In the task! data = %d\n", __FUNCTION__, *data);vTaskDelay(pdMS_TO_TICKS(1000));}
}
void myTask2(void *para)
{PersonInfo *personInfo = (PersonInfo *)para;while (1){printf("%s, In the task! name = %s, age = %d\n", __FUNCTION__, personInfo->name, personInfo->age);vTaskDelay(pdMS_TO_TICKS(1500));}
}
void myTask3()
{while (1){printf("%s, In the task!\n", __FUNCTION__);vTaskDelay(pdMS_TO_TICKS(2000));}
}
void app_main(void)
{uint16_t testCnt = 1;TaskInfo taskInfo1 = {0};TaskInfo taskInfo2 = {0};TaskInfo taskInfo3 = {0};PersonInfo personInfo = {.name = "I am task2", .age = 18};taskInfo1.xResult = xTaskCreate(myTask1,"myTask1",2048,&testCnt,1,&taskInfo1.xHandle);taskInfo2.xResult = xTaskCreate(myTask2,"myTask2",2048,&personInfo,1,&taskInfo2.xHandle);taskInfo3.xResult = xTaskCreate(myTask3,"myTask3",2048,NULL,2,NULL);if ((pdPASS != taskInfo1.xResult) || (pdPASS != taskInfo2.xResult) || (pdPASS != taskInfo3.xResult)){printf("Create task fail!\n");}while (1){testCnt++;printf("%s, In the task, testCnt = %d\n", __FUNCTION__, testCnt);vTaskDelay(pdMS_TO_TICKS(3000));}
}

        实验输出结果:

图 3.1.1 xTaskCreate()函数实验结果

2. 函数 xTaskCreateStatic()

        此函数用于使用静态的方式创建任务,任务的任务控制块以及任务的栈空间所需的内存,
需要由用户分配提供,若使用此函数,需要在FreeRTOSConfig.h文件中将宏configSUPPORT_STATIC_ALLOCATION(默认为1)配置为1。此函数创建的任务会立刻进入就绪态,由任务调度器调度运行。函数原型如下所示:

TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,       // 任务函数指针const char * const pcName,       // 任务名称(用于调试)uint32_t ulStackDepth,          // 任务栈深度void * pvParameters,            // 传递给任务的参数UBaseType_t uxPriority,         // 任务优先级StackType_t * puxStackBuffer,  // 任务栈缓冲区指针StaticTask_t * pxTaskBuffer ); // 任务控制块(TCB)缓冲区指针

        函数 xTaskCreateStatic()的形参描述, 如下表所示:

形参描述
pxTaskCode指向任务函数的指针
pcName任务名, 最大长度为 configMAX_TASK_NAME_LEN
ulStackDepth任务堆栈大小(栈深度), 其单位是 StackType_t类型的元素数量。在 32 位架构(如 ESP32)中,StackType_t通常为 4 字节。因此,若需要 1KB 的栈空间,此参数应设置为 256(1024 字节 / 4 字节)。
pvParameters传递给任务函数的参数
uxPriority任务优先级, 最大值为(configMAX_PRIORITIES-1)
puxStackBuffer任务栈指针(栈缓冲区),指向用户预先分配的栈内存的指针。这通常是一个 StackType_t类型的大数组。
pxTaskBuffer任务控制块指针(TCB缓冲区), 指向用户预先分配的任务控制块(TCB)内存的指针。这是一个 StaticTask_t类型的变量。TCB 用于存储任务的状态、优先级、栈指针等信息,操作系统通过它来管理任务。每个任务都有一个对应的 TCB,它包含了任务的所有相关信息,就像任务的"身份证"一样。

表 3.1.4 函数 xTaskCreateStatic()形参相关描述

        函数 xTaskCreateStatic()的返回值, 如下表所示:

返回值描述
NULL用户没有提供相应的内存, 任务创建失败。
其他值任务句柄, 成功创建任务时,函数返回一个 TaskHandle_t类型的任务句柄,可用于后续管理任务(如删除、挂起)。

表 3.1.5 函数 xTaskCreateStatic()返回值相关描述

    重要注意事项:

  • 内存生命周期管理​​:静态分配的内存(栈和TCB)在任务被删除后​​不会被自动释放​​。您需要自行管理这些内存的复用或生命周期。这与动态创建不同,动态创建的任务删除后,其内存由空闲任务自动回收;
  • ​​确保内存独占性​​:传递给 xTaskCreateStatic的栈和 TCB 缓冲区必须​​专属于该任务​​,在该任务存活期间不能被其他任务或代码覆盖;
  • ​​栈大小估算​​:需要更精确地估算任务所需栈空间。建议使用 FreeRTOS 提供的 uxTaskGetStackHighWaterMark()函数来监控任务运行过程中的历史最小剩余栈空间,帮助优化栈大小设置,避免浪费或溢出。

实验2:我们分别调用xTaskCreateStatic()函数创建3个任务,其中task1和task2有入参,task3无入参,并在任务中打印相关信息。
        代码如下:

#include 
#include 
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
typedef struct PersonInfo
{char name[30];uint8_t age;
} PersonInfo;
void myTask1(void *para)
{uint16_t *data = (uint16_t *)para;while (1){printf("%s, In the task! data = %d\n", __FUNCTION__, *data);vTaskDelay(pdMS_TO_TICKS(1000));}
}
void myTask2(void *para)
{PersonInfo *personInfo = (PersonInfo *)para;while (1){printf("%s, In the task! name = %s, age = %d\n", __FUNCTION__, personInfo->name, personInfo->age);vTaskDelay(pdMS_TO_TICKS(1500));}
}
void myTask3()
{while (1){printf("%s, In the task!\n", __FUNCTION__);vTaskDelay(pdMS_TO_TICKS(2000));}
}
#define MY_TASK_STACK_SIZE 2048 // 栈深度,单位为字(Word)
static StackType_t xMyTaskStack[3][MY_TASK_STACK_SIZE];
static StaticTask_t xMyTaskTCB[3];
void app_main(void)
{uint16_t testCnt = 1;TaskHandle_t xMyTaskHandle[3] = {0};PersonInfo personInfo = {.name = "I am task2", .age = 18};xMyTaskHandle[0] = xTaskCreateStatic(myTask1,            // 任务函数"myTask1",          // 任务名MY_TASK_STACK_SIZE, // 栈深度&testCnt,           // 任务参数1,                  // 优先级(数值越大优先级越高)xMyTaskStack[0],    // 栈缓冲区指针&xMyTaskTCB[0]      // TCB 缓冲区指针);xMyTaskHandle[1] = xTaskCreateStatic(myTask2,            // 任务函数"myTask2",          // 任务名MY_TASK_STACK_SIZE, // 栈深度&personInfo,        // 任务参数3,                  // 优先级(数值越大优先级越高)xMyTaskStack[1],    // 栈缓冲区指针&xMyTaskTCB[1]      // TCB 缓冲区指针);xMyTaskHandle[2] = xTaskCreateStatic(myTask3,            // 任务函数"myTask3",          // 任务名MY_TASK_STACK_SIZE, // 栈深度NULL,               // 任务参数2,                  // 优先级(数值越大优先级越高)xMyTaskStack[2],    // 栈缓冲区指针&xMyTaskTCB[2]      // TCB 缓冲区指针);if ((NULL == xMyTaskHandle[0]) || (NULL == xMyTaskHandle[1]) || (NULL == xMyTaskHandle[2])){printf("Create task fail!\n");}while (1){testCnt++;printf("%s, In the task, testCnt = %d\n", __FUNCTION__, testCnt);vTaskDelay(pdMS_TO_TICKS(3000));}
}

        实验输出结果:

图 3.1.2 实验xTaskCreateStatic()函数实验结果

3. 函数 xTaskCreateRestricted()

此函数用于创建受 MPU(内存保护单元)保护的任务,能有效提升系统的稳定性和安全性。当任务试图访问未授权的内存区域时,MPU 会触发异常,防止系统崩溃或数据损坏。
        任务控制块以及任务的栈空间所需的内存, 均由 FreeRTOS 从 FreeRTOS 管理的堆中分配, 若使用此函数, 需要将宏configSUPPORT_DYNAMIC_ALLOCATION 和宏 portUSING_MPU_WRAPPERS(在IDF下FreeRTOSConfig.h配置文件中未找到) 同时配置为 1。此函数创建的任务会立刻进入就绪态, 由任务调度器调度运行。 函数原型如下所示:

BaseType_t xTaskCreateRestricted( const TaskParameters_t *pxTaskDefinition,TaskHandle_t *pxCreatedTask );

参数

类型

说明

​​pxTaskDefinition​​

const TaskParameters_t *

指向 TaskParameters_t结构体的指针,该结构体包含了任务的所有配置参数。

​​pxCreatedTask​​

TaskHandle_t *

用于返回新创建任务的句柄,后续可通过此句柄操作任务。

表 3.1.6 函数 xTaskCreateRestricted()形参相关描述

  TaskParameters_t结构体​​(定义在 task.h中)是关键,它整合了创建任务所需的全部信息,通常包括:

成员说明

pvTaskCode

任务函数指针

pcName

任务名称
usStackDepth堆栈深度
pvParameters传递给任务的参数
uxPriority任务优先级
puxStackBuffer指向堆栈缓冲区的指针(静态分配时)
xRegionsMPU 内存区域配置(核心,定义任务可访问的内存区域)

表 3.1.7 函数TaskParameters_t结构体​​描述

返回值描述
pdPASS任务创建成功。
​​其他值​​(如 pdFAIL任务创建失败,通常是由于 FreeRTOS 的堆内存不足或 MPU 配置无效。

表 3.1.8 函数xTaskCreateRestricted()返回值描述

4. 函数 xTaskCreateRestrictedStatic()

        此函数用于使用静态的方式创建受 MPU 保护的任务, 此函数创建的任务的任务控制块以及任务的栈空间所需的内存, 需要由用户自行分配提供, 若使用此函数, 需要将宏configSUPPORT_STATIC_ALLOCATION 和宏 portUSING_MPU_WRAPPERS (在IDF下FreeRTOSConfig.h配置文件中未找到)同时配置为 1。此函数创建的任务会立刻进入就绪态, 由任务调度器调度运行。 函数原型如下所示:

BaseType_t xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition,TaskHandle_t * pxCreatedTask );

        函数 xTaskCreateRestrictedStatic()的形参描述, 如下表所示:

形参描述
pxTaskDefinition指向任务参数结构体的指针, 建结构体中包含任务函数、 任
务名、 任务优先级等任务参数
pxCreadedTask任务句柄, 任务成功创建后, 会返回任务句柄。 任务句柄就
是任务的任务控制块

表 3.1.9 函数 xTaskCreateRestrictedStatic()形参相关描述

        函数 xTaskCreateRestrictedStatic()的返回值, 如下表所示:

返回值描述
pdPASS任务创建成功
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY没有提供相应的内存, 任务创建失败

表 3.1.10 函数 xTaskCreateRestrictedStatic()返回值相关描述

  重要注意事项:

  • ​​MPU 区域配置​​:xRegions成员的配置是使用该函数最复杂也是最关键的部分。您需要根据任务实际需要访问的内存地址范围(如代码段、数据段、外设寄存器等)来正确设置每个区域的起始地址、大小和访问权限(如只读、读写、不可执行等)。错误的配置可能导致任务无法正常运行;
  • ​​任务协作​​:受 MPU 保护的任务​​必须设计为协作式​​。任务函数内部必须包含能让出 CPU 的调用,如 vTaskDelay(), xQueueReceive()等。​​严禁使用空循环(忙等待)​​,否则会阻塞低优先级的空闲任务,导致系统资源无法回收,最终可能引发看门狗复位;
  • ​​内存管理责任​​:由于采用静态分配,当任务被 vTaskDelete()删除后,其 TCB 和栈所占用的内存(即您预先分配的数组)​​不会被自动释放​​。您需要自行管理这些内存的复用。
5、任务创建相关函数比较

特性

xTaskCreate

xTaskCreateStatic

xTaskCreateRestricted

xTaskCreateRestrictedStatic

​​内存分配​​

动态(系统自动)

静态(用户提供)

动态(系统自动)

​​静态(用户提供)​​

​​MPU 保护​​

不适用

不适用

​​支持​​

​​支持​​

​​确定性/可靠性​​

高(无堆碎片风险)

​​极高(无堆碎片风险+内存保护)​​

​​使用复杂度​​

简单

中等

复杂

​​最复杂​​

​​适用场景​​

通用应用,快速开发

资源紧张,需高确定性

需MPU保护但内存管理偏好动态

​​安全关键,对确定性和可靠性要求极高的系统​

表 3.1.11 任务创建函数比较

6. 函数 vTaskList()

        此函数用于以“表格”的形式获取系统中任务的信息,若使用此函数,在SDK配置编辑器(menuconfig)中设置配置项configUSE_TRACE_FACILITY、configUSE_STATS_FORMATTING_FUNCTIONS(默认没有勾选,否则即使包了头文件也会报未定义)和在FreeRTOSConfig.h文件中同时配置configUSE_TRACE_FACILITY、configUSE_STATS_FORMATTING_FUNCTIONS为1(默认为1),此函数的函数原型如下所示:

voidvTaskList(char*pcWriteBuffer);

        函数vTaskList()的形参描述,如下表所示:

形参描述
pcWriteBuffer接收任务信息的缓存指针,该函数会遍历当前系统中的所有任务(包括空闲任务和软件定时器服务任务等系统任务),将每个任务的关键信息格式化为一个字符串表格,存入提供的缓冲区中。

表 3.1.12 函数 vTaskList()形参相关描述

        函数 vTaskList()无返回值。
        使用注意事项:

  • 实时性影响​​:调用 vTaskList()时,FreeRTOS 会挂起调度器以确保数据一致性。因此,在函数执行期间所有任务都会被暂停,​​可能会影响系统的实时性​​,所以建议仅在调试时使用
  • ​​仅在任务上下文中调用​​:vTaskList()设计为在任务代码中调用,​​不要​​在中断服务程序(ISR)中调用它。

        实验2:我们调用vTaskList()函数打印出创建的3个任务信息。

        代码如下(只在main函数中修改,其他部分修改见上面xTaskCreate()实验代码):

static char taskList[512] = {0};while (1){testCnt++;printf("%s, In the task, testCnt = %d\n", __FUNCTION__, testCnt);vTaskList(taskList);printf("-----------------Task List----------------\n");printf("Name          State Priority   Stack   Num\n");printf("%s\n", taskList);vTaskDelay(pdMS_TO_TICKS(3000));}

        实验输出结果:

图 3.1.3 vTaskList()函数实验结果

        打印“表格”说明:

列名

说明

​​Name​​

任务名称(创建任务时指定)。

​​State​​

任务当前状态,用单个字母表示:
- ​​R (Running)​​: 运行态
- ​​B (Blocked)​​: 阻塞态
- ​​S (Suspended)​​: 挂起态
- ​​D (Deleted)​​: 删除态(等待清理)
- ​​R (Ready)​​: 就绪态

​​Priority​​

任务当前的优先级。数字越大,优先级越高。

​​Stack​​

任务的​​堆栈高水位线​​(High Water Mark),即自任务运行以来堆栈剩余空间的最小值。​​这个值是诊断堆栈是否溢出的关键指标​​。单位通常是字节。

​​Num​​

由 FreeRTOS 内核分配的唯一任务编号。当多个任务同名时,可通过此编号区分。

表 3.1.13 函数 vTaskList()打印结果说明

7. 函数vTaskDelete()

        此函数用于删除已被创建的任务,被删除的任务将被从就绪态任务列表、阻塞态任务列表、挂起态任务列表和事件列表中移除,要注意的是,空闲任务会负责释放被删除任务中由系统分配的内存,但是由用户在任务删除前申请的内存,则需要由用户在任务被删除前提前释放,否则将导致内存泄露。若使用此函数,需要在FreeRTOSConfig.h文件中将宏INCLUDE_vTaskDelete配置为1(默认为1)。函数原型如下所示:

voidvTaskDelete(TaskHandle_txTaskToDelete);

        函数vTaskDelete()的形参描述,如下表所示:

形参描述
TaskHandle_t

指定要删除的任务。

  • 如果传入一个​​有效的任务句柄​​,则删除该句柄对应的任务;
  • 如果传入 ​NULL​,则表示删除​​当前正在调用该函数的任务自身​​(即“任务自删”)。

表 3.1.13 函数 vTaskDelete()形参相关描述

        vTaskDelete内部机制与资源释放:

        理解 vTaskDelete()的内部机制有助于避免常见的资源管理错误,其核心流程和内存释放责任可以概括为下图:

        从上图可以看出,FreeRTOS 采用了一种“惰性删除”策略来平衡实时性和资源管理 。最关键的一点是:​vTaskDelete()函数本身不会立即释放任务的内存​​。对于自删的任务,其任务控制块(TCB)和堆栈内存的释放工作是由​​空闲任务(Idle Task)​​ 在后台完成的 。这意味着你必须确保空闲任务能够获得 CPU 时间运行,否则会导致内存泄漏 。
        此外,需要特别注意的是,如果任务在运行期间使用 pvPortMalloc()等函数​​手动分配了内存​​,或者在运行中获取了信号量、互斥锁等资源,这些资源​​不会被自动释放​​。程序员必须在调用 vTaskDelete()之前自行清理这些资源,否则会造成资源泄漏 。

    重要注意事项:

  • ​​禁止在中断中使用​​:vTaskDelete()不是中断安全函数,​​绝对不能在中断服务程序(ISR)中调用​​。
  • ​​静态任务的内存管理​​:对于使用 xTaskCreateStatic()创建的静态任务,其 TCB 和堆栈内存是由用户静态分配的。调用 vTaskDelete()后,任务会被从调度器中移除,但这些静态内存不会被释放,需要用户自行管理。
  • ​​句柄管理​​:任务被删除后,其任务句柄就失效了。建议在删除任务后,将对应的句柄变量设置为 NULL,以防止后续误用成为“野指针”。
  • ​​确保空闲任务运行​​:由于空闲任务负责清理资源,因此不能让它长时间阻塞或“饿死”。这意味着自删的任务​​必须设计为协作式​​,在其循环中应包含 vTaskDelay()、等待队列或信号量等能让出 CPU 的函数,而不能是纯粹的忙等待(while(1);)。

        实验3:我们分别调用vTaskDelete()函数创建3个任务,在主任务中删除task2,task3任务中删除自己,观察任务中打印相关信息,同时我们通过vTaskList观察任务信息。
代码如下:

#include 
#include 
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
typedef struct TaskInfo
{TaskHandle_t xHandle;BaseType_t xResult;
} TaskInfo;
typedef struct PersonInfo
{char name[30];uint8_t age;
} PersonInfo;
void myTask1(void *para)
{uint16_t *data = (uint16_t *)para;while (1){printf("%s, In the task! data = %d\n", __FUNCTION__, *data);vTaskDelay(pdMS_TO_TICKS(2000));}
}
void myTask2(void *para)
{PersonInfo *personInfo = (PersonInfo *)para;while (1){printf("%s, In the task! name = %s, age = %d\n", __FUNCTION__, personInfo->name, personInfo->age);vTaskDelay(pdMS_TO_TICKS(1500));}
}
void myTask3()
{printf("%s, In the task!\n", __FUNCTION__);vTaskDelay(pdMS_TO_TICKS(3000));vTaskDelete(NULL);
}
void app_main(void)
{uint16_t testCnt = 1;static char taskList[512] = {0};TaskInfo taskInfo1 = {0};TaskInfo taskInfo2 = {0};TaskInfo taskInfo3 = {0};PersonInfo personInfo = {.name = "I am task2", .age = 18};taskInfo1.xResult = xTaskCreate(myTask1,"myTask1",2048,&testCnt,1,&taskInfo1.xHandle);taskInfo2.xResult = xTaskCreate(myTask2,"myTask2",2048,&personInfo,1,&taskInfo2.xHandle);taskInfo3.xResult = xTaskCreate(myTask3,"myTask3",2048,NULL,2,NULL);if ((pdPASS != taskInfo1.xResult) || (pdPASS != taskInfo2.xResult) || (pdPASS != taskInfo3.xResult)){printf("Create task fail!\n");}while (1){testCnt++;vTaskList(taskList);printf("-----------------Task List----------------\n");printf("Name          State Priority   Stack   Num\n");printf("%s\n", taskList);printf("%s, In the task, testCnt = %d\n", __FUNCTION__, testCnt);vTaskDelay(pdMS_TO_TICKS(3000));if (NULL != taskInfo1.xHandle){vTaskDelete(taskInfo1.xHandle);taskInfo1.xHandle = NULL;}}
}

        实验结果如下:

图 3.1.4 vTaskDelete()函数实验结果

8. 函数uxTaskGetStackHighWaterMark()

        该函数用于查询指定任务自创建以来,其堆栈空间的​​历史最小剩余量​​,这个值被称为“高水位线”(High Water Mark),返回值越小,表示任务曾经消耗的堆栈空间越多,堆栈溢出的风险也就越大​​。若使用此函数,需在FreeRTOSConfig.h文件中设置配置项INCLUDE_uxTaskGetStackHighWaterMark为1(默认为1),此函数的函数原型如下所示:

UBaseType_tuxTaskGetStackHighWaterMark(TaskHandle_txTask);

        函数uxTaskGetStackHighWaterMark()的形参描述,如下表所示:

形参描述
xTask要查询的​​任务句柄​​(TaskHandle_t)。如果传入 NULL,则表示查询​​当前任务​​(即调用该函数的任务自身)的堆栈高水位线。

表 3.1.14 函数 uxTaskGetStackHighWaterMark()形参相关描述

函数 uxTaskGetStackHighWaterMark()的返回值, 如下表所示:

返回值描述
整数

返回值的单位是​​字(Word)​​。在 ESP32(32 位架构)等平台上,1 字等于 4 字节。

  • 返回值的意义​​:它表示从任务开始运行到调用该函数时,堆栈剩余空间的最小值。例如,返回值为 100,表示在最坏的情况下,堆栈还有 100 字(400 字节)未被使用过;
  • 危险信号​​:如果返回值接近甚至等于 ​​0​​,则极有可能已经发生了​​堆栈溢出​​。

表 3.1.15 函数 uxTaskGetStackHighWaterMark()返回值相关描述

    重要注意事项:

  • ​​主要用于调试​​:此函数会消耗一些 CPU 时间,建议​​主要在调试阶段使用​​,以帮助合理设置任务的堆栈大小(任务栈设置太小会导致程序崩溃,系统重启)。在产品发布版本中可考虑移除相关调用;
  • ​​估算堆栈需求​​:通过高水位线,您可以估算出任务的最大堆栈使用量:​​最大使用量 ≈ 任务的总堆栈大小(字) - 高水位线(字)​​;
  • ​​确保任务已创建​​:在查询其他任务的高水位线时,请确保该任务句柄有效且任务已被创建;
  • ​​理解“历史最小值”​​:该函数返回的是自任务运行以来的历史最小值。即使某次查询到的值较大,只要历史最小值很小,就依然存在风险。

 实验4:我们分别创建2个任务task1和task2,在task1和task2任务中打印相关信息,然后调用uxTaskGetStackHighWaterMark()函数获取task1和task2,并打印堆栈空间的​​历史最小剩余量。

代码如下:

#include 
#include 
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
typedef struct TaskInfo
{TaskHandle_t xHandle;BaseType_t xResult;
} TaskInfo;
typedef struct PersonInfo
{char name[30];uint8_t age;
} PersonInfo;
void myTask1(void *para)
{uint16_t *data = (uint16_t *)para;while (1){printf("%s, In the task! test uxTaskGetStackHighWaterMark1\n", __FUNCTION__);printf("%s, In the task! test uxTaskGetStackHighWaterMark2\n", __FUNCTION__);printf("%s, In the task! test uxTaskGetStackHighWaterMark3\n", __FUNCTION__);printf("%s, In the task! data = %d\n", __FUNCTION__, *data);vTaskDelay(pdMS_TO_TICKS(1000));}
}
void myTask2(void *para)
{PersonInfo *personInfo = (PersonInfo *)para;while (1){printf("%s, In the task! name = %s, age = %d\n", __FUNCTION__, personInfo->name, personInfo->age);vTaskDelay(pdMS_TO_TICKS(1000));}
}
void app_main(void)
{uint16_t testCnt = 1;TaskInfo taskInfo1 = {0};TaskInfo taskInfo2 = {0};PersonInfo personInfo = {.name = "I am task2", .age = 18};taskInfo1.xResult = xTaskCreate(myTask1,"myTask1",2048,&testCnt,2,&taskInfo1.xHandle);taskInfo2.xResult = xTaskCreate(myTask2,"myTask2",2048,&personInfo,1,&taskInfo2.xHandle);if ((pdPASS != taskInfo1.xResult) || (pdPASS != taskInfo2.xResult)){printf("Create task fail!\n");}while (1){testCnt++;UBaseType_t waterMarkSize = uxTaskGetStackHighWaterMark(taskInfo1.xHandle);printf("%s, task1 waterMarkSize = %d\n", __FUNCTION__, waterMarkSize);waterMarkSize = uxTaskGetStackHighWaterMark(taskInfo2.xHandle);printf("%s, task2 waterMarkSize = %d\n", __FUNCTION__, waterMarkSize);printf("%s, In the task! testCnt = %d\n", __FUNCTION__, testCnt);vTaskDelay(pdMS_TO_TICKS(3000));}
}

       实验结果如下:

图 3.1.5 uxTaskGetStackHighWaterMark()函数实验结果

3.2 FreeRTOS 获取和设置任务优先级

        下面介绍2个函数,获取任务优先级及设置任务优先级。

函数描述
uxTaskPriorityGet()获取任务优先级
vTaskPrioritySet()设置任务优先级

表 3.2.1 获取任务优先级及设置任务优先级函数

1. 函数 uxTaskPriorityGet()

        此函数用于获取指定任务的任务优先级, 若使用此函数, 需在 FreeRTOSConfig.h 文件中设
置配置项 INCLUDE_uxTaskPriorityGet 为 1(默认设置为1), 此函数的函数原型如下所示:

UBaseType_t uxTaskPriorityGet(const TaskHandle_t xTask);

        函数 uxTaskPriorityGet()的形参描述, 如下表所示:

形参参数类型描述
xTaskTaskHandle_t

任务句柄:

  • 如果传入一个​​有效的任务句柄​​,则查询该句柄对应任务的优先级;
  • 如果传入 ​NULL​,则表示查询​​当前正在调用该函数的任务自身​​的优先级 。

表 3.2.2 函数 uxTaskPriorityGet()形参相关描述


        函数 uxTaskPriorityGet()的返回值, 如下表所示:

返回值参数类型描述
整数UBaseType_t

返回的是任务的当前优先级数值:

在 FreeRTOS 中,​​数值越大,表示优先级越高​​,范围通常从 0(最低优先级,通常是空闲任务)到 configMAX_PRIORITIES - 1

表 3.2.3 函数 uxTaskPriorityGet()返回值相关描述

2. 函数 vTaskPrioritySet()

        此函数用于设置指定任务的优先级, 若使用此函数, 需在 FreeRTOSConfig.h 文件中设置配
置项 INCLUDE_vTaskPrioritySet 为 1(默认为1), 此函数的函数原型如下所示:

void vTaskPrioritySet(TaskHandle_t xTask, UBaseType_t uxNewPriority)

        函数 vTaskPrioritySet()的形参描述, 如下表所示:

形参描述
xTask要修改优先级的任务的​​句柄​​。如果传入 NULL,则表示修改​​当前正在调用此函数的任务自身​​的优先级 。
uxNewPriority目标任务设置的​​新优先级​​。该值会自动限制在 0(最低优先级,通常是空闲任务)到 configMAX_PRIORITIES - 1(最高优先级)的范围内。如果设置的值超过上限,系统会自动将其设置为 configMAX_PRIORITIES - 1

表 3.2.4 函数 vTaskPrioritySet()形参相关描述

        函数 vTaskPrioritySet()无返回值。

    重要注意事项:

  • 优先级数值与任务调度​​:在 FreeRTOS 中,​​数值越大表示优先级越高​​。调度器总是保证处于就绪态的​​最高优先级任务​​运行;
  • ​​配置优先级范围​​:最大可用优先级由 FreeRTOSConfig.h中的 configMAX_PRIORITIES定义。建议此值不要超过32,以节省内存和提高调度效率;
  • ​​中断服务程序(ISR)中不可使用​​:vTaskPrioritySet()不能在中断服务程序中调用。若需要在ISR中修改任务优先级,应使用中断安全的 xTaskPrioritySetFromISR()函数;
  • ​​优先级继承机制​​:在使用互斥信号量(Mutex)时,FreeRTOS 可能自动临时提升低优先级任务的优先级(优先级继承),以解决优先级反转问题。手动设置的优先级在互斥锁释放后会恢复。

        实验5:我们分别创建2个任务task1和task2,task1优先级高于task2,在任务中打印相关信息,获取任务优先级并打印;然后调整task2的优先级高于task1。预期结果:未调整task2任务优先级之前,task1中的打印优先于task2,中的打印优先打印出来;调整task2任务优先级后,task2中的打印优先于task1。

需要注意:代码中使用xPortGetCoreID()获取当前任务运行核号,这是为了确保两个任务在同一个核上运行,保证实验可靠性。
代码如下:

#include 
#include 
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
typedef struct TaskInfo
{TaskHandle_t xHandle;BaseType_t xResult;
} TaskInfo;
typedef struct PersonInfo
{char name[30];uint8_t age;
} PersonInfo;
void myTask1(void *para)
{uint16_t *data = (uint16_t *)para;while (1){uint16_t coreId = xPortGetCoreID();printf("%s, In the task! coreId = %d, data = %d\n", __FUNCTION__, coreId, *data);vTaskDelay(pdMS_TO_TICKS(1000));}
}
void myTask2(void *para)
{PersonInfo *personInfo = (PersonInfo *)para;while (1){uint16_t coreId = xPortGetCoreID();printf("%s, In the task! coreId = %d,  name = %s, age = %d\n", __FUNCTION__, coreId, personInfo->name, personInfo->age);vTaskDelay(pdMS_TO_TICKS(1000));}
}
void app_main(void)
{uint16_t testCnt = 1;TaskInfo taskInfo1 = {0};TaskInfo taskInfo2 = {0};PersonInfo personInfo = {.name = "I am task2", .age = 18};taskInfo1.xResult = xTaskCreate(myTask1,"myTask1",2048,&testCnt,2,&taskInfo1.xHandle);taskInfo2.xResult = xTaskCreate(myTask2,"myTask2",2048,&personInfo,1,&taskInfo2.xHandle);if ((pdPASS != taskInfo1.xResult) || (pdPASS != taskInfo2.xResult)){printf("Create task fail!\n");}vTaskDelay(pdMS_TO_TICKS(5000));  //延时先让task1和task2按照设置优先级打印UBaseType_t task1Priority = uxTaskPriorityGet(taskInfo1.xHandle);printf("task1 priority = %d\n", task1Priority);UBaseType_t task2Priority = uxTaskPriorityGet(taskInfo2.xHandle);printf("task2 priority = %d\n", task2Priority);vTaskPrioritySet(taskInfo2.xHandle, 3);  //设置task2的任务优先级为3,高于task1task1Priority = uxTaskPriorityGet(taskInfo1.xHandle);printf("task1 priority = %d\n", task1Priority);task2Priority = uxTaskPriorityGet(taskInfo2.xHandle);printf("task2 priority = %d\n", task2Priority);while (1){testCnt++;printf("%s, In the task! testCnt = %d\n", __FUNCTION__, testCnt);vTaskDelay(pdMS_TO_TICKS(3000));}
}

       实验结果如下:

图 3.2.1 获取及调整任务优先级函数实验结果

3.3 FreeRTOS 挂起和恢复任务

        下面介绍5个关于任务挂起与恢复的函数。

函数描述
vTaskSuspend()挂起任务
vTaskResume()恢复被挂起的任务
vTaskSuspendAll()挂起任务调度器
vTaskResumeAll()恢复任务调度器
xTaskResumeFromISR() 在中断中恢复被挂起的任务
1. 函数vTaskSuspend()

        此函数用于挂起任务,若使用此函数,需要在FreeRTOSConfig.h文件中将宏INCLUDE_vTaskSuspend配置为1。无论优先级如何,被挂起的任务都将不再被执行,直到任
务被恢复。此函数并不支持嵌套,不论使用此函数重复挂起任务多少次,只需调用一次恢复任
务的函数,那么任务就不再被挂起。函数原型如下所示:

void vTaskSuspend(TaskHandle_t xTaskToSuspend)

        函数 vTaskSuspend()的形参描述, 如下表所示:

形参描述
xTaskToSuspend要挂起的任务的​​句柄​​(TaskHandle_t)。如果传入 NULL,则表示挂起​​当前正在调用该函数的任务自身​​。

表 3.3.1 函数 vTaskSuspend()形参相关描述

        函数 vTaskSuspend()无返回值。

        当调用 vTaskSuspend()后,FreeRTOS 内核会执行以下操作:

  • ​​从调度列表中移除​​:将目标任务从就绪列表、阻塞列表或事件列表中移除;
  • ​​添加到挂起列表​​:把任务加入到挂起任务列表(xSuspendedTaskList)中;
  • ​​触发调度​​:如果被挂起的是当前正在运行的任务,则会强制进行一次任务切换(Context Switch)。

        被挂起的任务将​​不再参与调度​​,无法获得CPU时间,但其所有的状态和局部变量都被保留在内存中。退出挂起状态的唯一方法是另一个任务(或中断)调用 vTaskResume()或xTaskResumeFromISR()来恢复它。

        重要注意事项:

事项

说明

​​配置宏​​

使用前必须在 FreeRTOSConfig.h中将 INCLUDE_vTaskSuspend定义为 1,否则该函数不可用。

​​挂起次数​​

对同一任务多次调用 vTaskSuspend()挂起,只需调用一次 vTaskResume()即可恢复。

​​空闲任务​​

​不能挂起空闲任务(Idle Task)​​,否则可能导致已删除任务的内存无法被回收,引起系统死锁或内存泄漏。

​​资源死锁​​

若任务在挂起前持有关键资源(如互斥锁),可能导致其他需要该资源的任务无限期等待,设计时需谨慎。

​​与阻塞的区别​​

​挂起(Suspend)​​是主动让任务停止调度,不依赖任何事件;​​阻塞(Block)​​是任务等待特定事件(如信号量、延时)时被动进入的状态。

表 3.3.2 函数 vTaskSuspend()使用注意事项

2. 函数 vTaskResume()

        此函数用于在任务中恢复被挂起的任务, 若使用此函数, 需要在 FreeRTOSConfig.h 文件中
将宏 INCLUDE_vTaskSuspend 配置为 1。 不论一个任务被函数 vTaskSuspend()挂起多少次, 只
需要使用函数 vTakResume()恢复一次, 就可以继续运行。 函数原型如下所示:

void vTaskResume(TaskHandle_t xTaskToResume)

        函数 vTaskResume()的形参描述, 如下表所示:

形参描述
xTaskToResume要恢复的任务的​​句柄​​(TaskHandle_t)。这个句柄通常在任务创建时通过 xTaskCreate()函数的最后一个参数获取

表 3.3.3 函数 vTaskResume()形参相关描述

        函数 vTaskResume()无返回值。

        重要注意事项:

  • 专用于恢复挂起任务​​:vTaskResume()​​只能恢复通过 vTaskSuspend()函数挂起的任务​​;它不能恢复因等待信号量、队列、延时等事件而阻塞的任务;
  • 非嵌套性​​:无论调用了多少次 vTaskSuspend()来挂起一个任务,只需要调用​​一次​​vTaskResume()即可将其恢复;
  • 不能恢复当前任务​​:逻辑上不能恢复当前正在运行的任务(即调用 vTaskResume()的任务自身),代码中通过 configASSERT( pxTaskToResume )进行校验;
  • 中断服务程序(ISR)中禁用​​:​​绝对不要在中断服务程序(ISR)中调用 vTaskResume()​​;如果需要在中断中恢复任务,必须使用其专用版本 xTaskResumeFromISR();
  • ​​与调度器挂起的区别​​:vTaskResume()和 vTaskSuspend()用于操作单个任务的状态;而 vTaskSuspendAll()和 xTaskResumeAll()用于挂起和恢复整个调度器,会影响所有任务的调度;
  • ​​确保句柄有效性​​:在恢复任务前,务必确保传入的任务句柄是有效的。

        实验6:我们分别创建2个任务task1和task2,在任务中打印相关信息,在task2中通过传入task1的handle来挂起task1一段时间(挂起器件task1中无打印信息),然后恢复。
代码如下:

#include 
#include 
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
typedef struct TaskInfo
{TaskHandle_t xHandle;BaseType_t xResult;
} TaskInfo;
void myTask1(void *para)
{uint16_t *data = (uint16_t *)para;while (1){printf("%s, In the task! data = %d\n", __FUNCTION__, *data);vTaskDelay(pdMS_TO_TICKS(1000));}
}
void myTask2(void *para)
{TaskHandle_t* xHandle = (TaskHandle_t *)para;while (1){printf("%s, In the task!\n", __FUNCTION__);vTaskDelay(pdMS_TO_TICKS(3000));printf("挂起myTask1...\n");vTaskSuspend(*xHandle);vTaskDelay(pdMS_TO_TICKS(5000));printf("恢复myTask1...\n");vTaskResume(*xHandle);}
}
void app_main(void)
{uint16_t testCnt = 1;TaskInfo taskInfo1 = {0};TaskInfo taskInfo2 = {0};taskInfo1.xResult = xTaskCreate(myTask1,"myTask1",2048,&testCnt,2,&taskInfo1.xHandle);taskInfo2.xResult = xTaskCreate(myTask2,"myTask2",2048,&taskInfo1.xHandle,1,&taskInfo2.xHandle);if ((pdPASS != taskInfo1.xResult) || (pdPASS != taskInfo2.xResult)){printf("Create task fail!\n");}while (1){testCnt++;printf("%s, In the task! testCnt = %d\n", __FUNCTION__, testCnt);vTaskDelay(pdMS_TO_TICKS(3000));}
}

       实验结果如下:

图 3.2.2 获取及调整任务优先级函数实验结果

3. 函数vTaskSuspendAll()

   vTaskSuspendAll()是一个用于​​挂起任务调度器​​的函数。它的核心作用是临时禁止任务切换,让当前任务能够独占 CPU 运行,而不会被打断。函数原型如下:

void vTaskSuspendAll( void );

        函数无入参和返回值。

调用 vTaskSuspendAll()后,FreeRTOS 的调度器会暂停工作。这意味着:

  • 任务切换被禁止​​:即使有更高优先级的任务进入就绪状态,系统也不会立即进行任务切换。当前正在运行的任务将继续独占 CPU 执行权,直到调用 xTaskResumeAll()恢复调度器;
  • ​​中断不受影响​​:这是一个关键特点。​​硬件中断仍然可以正常产生并得到响应​​,中断服务程序(ISR)会照常执行。这保证了系统对紧急事件的实时响应能力。

适用场景与优势:
        vTaskSuspendAll()非常适合用于保护那些​​执行时间稍长、需要保证连续性,但又不能关闭中断​​的操作场景。

  • ​​保护共享资源(无中断访问)​​:当多个任务需要复杂地操作同一全局数据结构(如链表、缓冲区)时,使用 vTaskSuspendAll()可以确保整个操作序列的原子性,避免在操作过程中被其他任务打断导致数据不一致。与完全关中断的临界区相比,它的开销更小,且不影响中断响应;
  • ​​执行原子性操作​​:例如,需要连续向多个队列发送消息,或者进行复杂的硬件初始化流程,希望这一系列操作完成后才允许任务调度。

        重要注意事项:

  • ​​严禁在中断中调用​​:vTaskSuspendAll()和 xTaskResumeAll()​​只能用于任务(Task)中,绝对不能在中断服务程序(ISR)中调用​​;
  • ​​避免长时间挂起​​:挂起调度器会阻塞所有高优先级任务,破坏系统的实时性。因此,受保护的代码段应​​尽可能短小​​,理想情况下在微秒级完成;
  • ​​禁止在挂起期间调用阻塞API​​:在调度器挂起期间,​​绝对不能调用​​如vTaskDelay()。xQueueReceive()等可能引起任务阻塞的函数。因为调度器已停止,没有其他任务可以运行,会导致系统​​死锁​​;
  • ​​确保嵌套匹配​​:务必保证 vTaskSuspendAll()和 xTaskResumeAll()的调用次数严格匹配。如果挂起次数多于恢复次数,调度器将无法恢复,导致系统“冻僵”。

为了更清晰地理解 vTaskSuspendAll()的定位,下表将其与 FreeRTOS 中其他两种常用的保护机制进行对比。

特性

vTaskSuspendAll()(调度器挂起)

vTaskSuspend()(任务挂起)

taskENTER_CRITICAL()(临界区)

​作用对象​

整个系统的任务调度器

单个指定的任务

CPU 中断

​主要影响​

禁止任务切换

将指定任务置于挂起态

屏蔽中断(或部分中断)

​中断响应​

✅ ​​不受影响​​,可正常响应

✅ 不受影响

❌ ​​被屏蔽​​,实时性受影响

​适用场景​

保护任务间共享资源(操作时间稍长)

暂停某个特定任务的执行

保护极短的关键代码段(如操作变量)

​推荐保护时长​

中等(微秒到毫秒级)

可长可短

极短(几个指令周期)

4. 函数vTaskResumeAll()

        该函数用于​​恢复被挂起的调度器​​的关键函数。它与 vTaskSuspendAll()配对使用,共同控制整个系统的任务调度。函数原型如下:

BaseType_t xTaskResumeAll( void );

       vTaskResumeAll返回值 , 如下表所示:

返回值描述
pdTRUE表示恢复调度器后,有一个​​更高优先级的任务就绪​​,需要立即进行任务切换(上下文切换)。
pdFALSE表示无需立即进行任务切换 。

表 3.3.4 函数 vTaskResumeAll()返回值描述

        重要注意事项:

  • ​​嵌套调用​​:vTaskSuspendAll()和 xTaskResumeAll()支持嵌套调用。系统内部维护一个计数器,必须保证​​调用多少次 vTaskSuspendAll(),就要对应调用多少次 xTaskResumeAll()​​,调度器才会真正恢复;
  • ​​禁止在中断中使用​​:xTaskResumeAll()​​不能在中断服务程序(ISR)中调用;
  • ​​与临界区的区别​​:挂起调度器 (vTaskSuspendAll/xTaskResumeAll) 只是禁止了任务切换,​​中断仍然有效​​。而进入临界区 (taskENTER_CRITICAL/taskEXIT_CRITICAL) 会屏蔽中断,适用于更短、更紧急的关键段保护;
  • ​​避免在挂起期间调用阻塞API​​:在调度器挂起期间,​​严禁调用​​如 vTaskDelay()、xQueueSend()等可能导致任务阻塞的函数,否则可能引起系统死锁。

        实验6:我们分别创建2个任务task1和task2,在任务中打印相关信息,在task2中调用vTaskSuspendAll(),然后延时(正常此处执行受保护的代码,如修改共享数据灯),然后调用xTaskResumeAll()​​恢复。
代码如下:

#include 
#include 
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
typedef struct TaskInfo
{TaskHandle_t xHandle;BaseType_t xResult;
} TaskInfo;
void myTask1(void *para)
{uint16_t *data = (uint16_t *)para;while (1){printf("%s, In the task! data = %d\n", __FUNCTION__, *data);vTaskDelay(pdMS_TO_TICKS(100));}
}
void myTask2(void *para)
{uint32_t cnt = 0;while (1){printf("%s, In the task!\n", __FUNCTION__);printf("挂起整个系统的任务调度器...\n");vTaskSuspendAll();//执行需要保护的、非常快速的代码printf("恢复整个系统的任务调度器...\n");xTaskResumeAll();}
}
void app_main(void)
{uint16_t testCnt = 1;TaskInfo taskInfo1 = {0};TaskInfo taskInfo2 = {0};taskInfo1.xResult = xTaskCreate(myTask1,"myTask1",2048,&testCnt,2,&taskInfo1.xHandle);taskInfo2.xResult = xTaskCreate(myTask2,"myTask2",2048,NULL,1,&taskInfo2.xHandle);if ((pdPASS != taskInfo1.xResult) || (pdPASS != taskInfo2.xResult)){printf("Create task fail!\n");}while (1){testCnt++;printf("%s, In the task! testCnt = %d\n", __FUNCTION__, testCnt);vTaskDelay(pdMS_TO_TICKS(1000));}
}
5. 函数 xTaskResumeFromISR()

        此函数用于在中断服务程序中恢复一个之前被 vTaskSuspend()函数挂起的任务,使其重新进入就绪状态,能够被调度器调度运行。若使用此函数, 需要在 FreeRTOSConfig.h 文件中
将宏 INCLUDE_xTaskResumeFromISR 配置为 1。 不论一个任务被函数 vTaskSuspend()挂起多次, 只需要使用函数 vTakResumeFromISR()恢复一次, 就可以继续运行。 函数原型如下所示:

BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume)

        函数 xTaskResumeFromISR()的形参描述, 如下表所示:

形参描述
xTaskToResume指定要恢复的​​任务句柄​​(TaskHandle_t)。这个句柄在任务创建时获取。

表 3.3.5 函数 xTaskResumeFromISR()形参相关描述

        函数 xTaskResumeFromISR()的返回值, 如下表所示:

返回值描述
pdTRUE表示被恢复的任务优先级​​等于或高于​​被中断打断的任务优先级。这意味着在退出中断后​​需要进行一次任务切换​​,以便调度器能够立即运行更高优先级的任务。
pdFALSE表示被恢复的任务优先级​​低于​​当前被中断的任务优先级。这意味着在退出中断后​​不需要立即进行任务切换​​。

表 3.3.6 函数 xTaskResumeFromISR()返回值相关描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/973229.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据库的安全与保护(下) - 实践

数据库的安全与保护(下) - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco&q…

2025年口碑好的江苏婚纱照/婚前影像/小众婚纱照/园林婚纱照/光影婚纱照/外景婚纱照/秀禾婚纱照/中式婚纱照/结婚照品牌推荐:弥素摄影领跑

摘要 江苏婚纱照行业在2025年持续蓬勃发展,随着新人对个性化、高品质拍摄需求的增长,小众高质感品牌逐渐成为市场新宠。本文基于行业数据和用户口碑,为您推荐排名前十的江苏婚纱照品牌,并提供详细对比,帮助您做出…

2025年江苏婚纱照/婚前影像/小众婚纱照/园林婚纱照/光影婚纱照/外景婚纱照/秀禾婚纱照/中式婚纱照/结婚照品牌口碑推荐榜:弥素摄影领跑行业

摘要 江苏婚纱照行业近年来蓬勃发展,新人对个性化、高品质拍摄需求日益增长。本文基于行业数据和用户反馈,综合评选出口碑前十的江苏婚纱照品牌,其中淮安弥素摄影有限公司以卓越的服务和原创技术位居榜首。文章提供…

打印机字体漏洞分析:CVE-2024-12649技术深度解析

本文深入分析Canon打印机TTF解释器漏洞CVE-2024-12649的技术细节,包括恶意字体构造、堆栈溢出利用和DryOS系统安全机制绕过,揭示了通过打印文档实现代码执行的攻击链。CVE-2024-12649:Canon TTF解释器漏洞分析 漏洞…

2025年11月22日

系统总结了后半学习的注意事项并完成了当下比较急需的任务

2025年德商数控母线加工机实力厂家权威推荐榜单:德商母线加工机/德商铜排加工机/德商母排加工机源头厂家精选

在智能电网建设与新能源行业快速发展的背景下,德商系列数控母线加工机以其高精度、高效率及卓越的稳定性,正成为电力设备制造企业提升生产效率的关键装备。 高端母线加工机能够实现冲孔、剪切、折弯等多道工序一次性…

【Java后端进行ai coding实践系列】如何使用ai coding达成计划任务增删改查

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025-11-21 hetao1733837的刷题记录

2025-11-21 hetao1733837的刷题记录2025-11-21 hetao1733837的刷题记录 LG14415/LOJ3004 [JOISC 2015] Inheritance 原题链接1:[JOISC 2015] Inheritance 原题链接2:「JOISC 2015 Day 4」Inheritance 分析 呃……居然…

2025 最新腻子粉厂家推荐!环保与性能双优腻子粉品牌排行榜,涵盖母婴级 / 工程级产品权威测评儿童级健康腻子粉/工程腻子粉/工程腻子粉施工/建筑腻子粉公司推荐

引言 在墙面装饰基层处理中,腻子粉的品质直接决定装饰效果与使用寿命,而市场产品良莠不齐,环保不达标、粘结力不足等问题频发。为精准筛选优质产品,本次榜单依托国际涂料协会(ECCA)最新测评标准,结合全球建材质…

java freemarker(ftl)模板填充导出PDF,支持中文乱码

添加依赖<dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.32</version></dependency><dependency><group…

2025年广东洁净度检测公司权威推荐榜单:广东医院(诊所)洁净环境检测/广东空气净化器检测平台/广东新风机检测服务机构精选

在生物医药、电子半导体与医疗卫生等行业,生产与研发环境的洁净度直接关系到产品质量与安全。专业洁净度检测正成为保障产业高质量发展的关键环节。 洁净度检测通过对空气中悬浮粒子、微生物浓度以及相关环境参数的精…

C# Avalonia 18- ControlTemplates - FlipPanelTest

C# Avalonia 18- ControlTemplates - FlipPanelTestFlipPanel2类是负责控制逻辑。 FlipPanel2.csusing Avalonia; using Avalonia.Animation.Easings; using Avalonia.Controls; using Avalonia.Controls.Metadata; us…

2025 最新仿石漆厂家权威推荐榜:真石漆 / 绿色环保仿石漆优质品牌精选仿石漆/真石漆/绿色真石漆/有资质的仿石漆公司推荐

引言 随着建筑装饰对质感与环保的双重追求,仿石漆已成为外墙装饰的主流选择,但市场品牌混杂、品质良莠不齐的问题仍困扰采购者。本次榜单基于国际涂料协会(IFPA)测评标准,结合 3000 小时耐候性测试、环保指标检测…

2025年纱线烘干机制造厂权威推荐榜单:气流烘干机/筒子烘干机/快速烘干机源头制造厂精选

在纺织产业升级和技术进步的推动下,纱线烘干机以其高效节能和稳定可靠的性能特点,正成为纺织企业提升产品质量和生产效率的关键设备。 根据纺织机械行业数据统计,2024年中国纺织烘干设备市场规模达到68亿元,年均增…

CTF逆向Re:零基础系统性入门教程-5-动态调试

目录 动态调试是什么 先说一些基础概念 为什么要动态调试 怎么动态调试 1.动态调试代替思考 2.动态调试劫持控制动态调试是什么 简单说,就是运行程序,运行中监控程序的运行流程,数据变化,和静态分析的对比如下场景…

CF1817B Fish Graph

题目大意 定义了一个鱼图,求是否存在并输出环和两点的边。 题意分析 直接暴力,但是需要加一点点小优化,具体如下:环的检测:我们首先需要在图中找到一个环,如果遇到了一个已经访问过的节点,并且该节点不是当前路…

CF1630C Paint the Middle

最简题意 给定一个长度为 \(n\) 的数组,数组中的元素初始值都为零。可以通过以下操作来修改颜色:选择三个元素 \((i,j,k)\) 满足 \(1\leq i<j<k \leq n\),并且保证 \(a_i=a_k\),同时 \(c_i=c_k=0\),那么可以…

CF1707B Difference Array

题目大意 题目大意非常简短,就是给你一个数组。每次同时进行 \(a_i=a_{i+1}-a_i\) 的操作,求最后留下的一个数是什么,如下:计算数组中相邻两个元素的差值,得到新的数组。 对新数组排序,然后将其替换。 重复此过程…

P3113 [USACO14DEC] Marathon G

题目大意 马拉松路线由 \(N\) 个检查点组成,选手必须按顺序经过每个检查点。有两种操作:更新某个检查点的坐标。 查询从检查点 \(X\) 到 \(Y\) 的最短路径长度,允许跳过其中一个中间。要求高效处理这些操作。 思路分…

封装map和set(红黑树作为底层结构如何完成map和set插入遍历)

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …