FreeRTOS任务相关API简介
前言
本文介绍了几个常用的FreeRTOS任务相关的API,列举了任务的创建、删除,挂起和恢复的API,并介绍了函数功能、入参等信息。专为初学者入门而写。希望可以给大家带来帮助。
任务的创建和删除
下面简介各种任务创建API和任务删除API:
| 函数 | 描述 |
|---|---|
xTaskCreat() | 使用动态的方法创建一个任务 |
xTaskCreatStatic() | 使用静态的方法创建一个任务 |
xTaskCreatRestricted() | 创建一个使用MPU进行限制的任务,使用动态内存分配此任务使用的内存 |
vTaskDelet() | 删除一个任务 |
动态创建任务(xTaskCreate)
使用此API创建的任务,会自动从FreeRTOS的堆里面分配RAM空间,来存储任务控制块(TCB)和任务堆栈。所以在使用之前要确保内存管理文件在工程中被使用,并且确保下面这个宏的值是1:
#define configSUPPORT_DYNAMIC_ALLOCATION 1
xTaskCreate() 是 FreeRTOS 实时操作系统中用于创建新任务的核心函数。它动态分配内存并初始化一个新的任务控制块(TCB)和任务栈。
另外,此API创建的任务默认处于就绪态,所有处于就绪态的任务,按照优先级的高低以此执行。在调度器启动前后都可以调用此API创建任务。函数原型如下:
函数原型
BaseType_t xTaskCreate(
TaskFunction_t pvTaskCode, // 任务函数指针
const char * const pcName, // 任务名称
unsigned short usStackDepth, // 任务栈大小(单位:字,4字节)
void *pvParameters, // 传递给任务函数的参数
UBaseType_t uxPriority, // 任务优先级
TaskHandle_t * const pxCreatedTask // 任务句柄
);
参数说明
-
pvTaskCode:
- 类型:
TaskFunction_t(通常定义为void (*)(void *)) - 描述: 指向任务函数的指针,该函数应是一个无限循环且不应返回
- 类型:
-
pcName:
- 类型:
const char * - 描述: 任务的可读名称,用于调试目的,最大长度由
configMAX_TASK_NAME_LEN定义
- 类型:
-
usStackDepth:
- 类型:
configSTACK_DEPTH_TYPE(通常为uint16_t) - 描述: 指定任务栈的大小,以字(word)为单位(不是字节)。例如,在32位架构上,1字=4字节
- 类型:
-
pvParameters:
- 类型:
void * - 描述: 传递给任务函数的参数指针
- 类型:
-
uxPriority:
- 类型:
UBaseType_t - 描述: 任务优先级(0为最低优先级,
configMAX_PRIORITIES-1为最高)
- 类型:
-
pxCreatedTask:
- 类型:
TaskHandle_t * - 描述: 用于返回任务句柄的指针,可用于后续引用该任务。可以传入NULL
- 类型:
返回值
pdPASS(1): 任务创建成功pdFAIL(0): 任务创建失败(通常由于内存不足)
使用示例
void vTaskFunction(void *pvParameters) {
const char *msg = (const char *)pvParameters;
for(;;) {
printf("%s\n", msg);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void main() {
TaskHandle_t xHandle = NULL;
if(xTaskCreate(vTaskFunction, // 任务函数
"PrintTask", // 任务名称
128, // 栈大小(字)
"Hello World", // 传递给任务的参数
1, // 优先级
&xHandle) // 任务句柄
!= pdPASS) {
// 任务创建失败处理
}
vTaskStartScheduler(); // 启动调度器
}
注意事项
- 任务函数不应返回,通常应包含一个无限循环
- 栈大小需要足够大以容纳局部变量和函数调用栈
- 使用
vTaskDelete(NULL)可以让任务自我删除 - 对于静态内存分配的任务创建,可以使用
xTaskCreateStatic() xTaskCreate()可以在vTaskStartScheduler()之前调用,因为 FreeRTOS 允许先创建任务,再启动调度器。- 但
xTaskCreate()创建的任务必须在 FreeRTOS 的运行时环境准备好之后才能被调用(即 FreeRTOS 的堆内存、数据结构等已配置正确)。 - 真正的任务执行是从
vTaskStartScheduler()开始的,在此之前任务只是被创建,但不会运行。
内存管理
xTaskCreate() 使用 FreeRTOS 的内存管理函数动态分配任务栈和任务控制块(TCB)。如果使用自定义内存管理方案,需要确保有足够的堆空间。
特点
- 适用于大多数应用场景,系统自动管理内存。
- 任务删除后,内存可以被系统回收。
- 适合任务数量较多或内存动态分配的场景。
静态创建任务(xTaskCreateStatic)
静态创建任务需要用户手动分配任务栈空间和任务控制块(TCB),通常用于对内存使用有严格要求的嵌入式系统。
需要下面的宏等于1。
#define configSUPPORT_STATIC_ALLOCATION 1
函数原型
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 // 任务栈
);
参数说明
- pxTaskCode: 任务入口函数(与
xTaskCreate()相同) - pcName: 任务名称(与
xTaskCreate()相同) - ulStackDepth: 栈深度,以字(word)为单位(与
xTaskCreate()相同) - pvParameters: 传递给任务的参数(与
xTaskCreate()相同) - uxPriority: 任务优先级(与
xTaskCreate()相同) - pxStackBuffer: 指向用户提供的栈内存空间的指针
- pxTaskBuffer: 指向用户提供的任务控制块(TCB)内存空间的指针
返回值
- 成功时返回任务句柄
- 失败时返回 NULL(通常由于提供的缓冲区无效)
示例代码
//空闲任务任务堆栈
static StackType_t IdleTaskStack[configMINIMAL_STACK_SIZE];
//空闲任务控制块
static StaticTask_t IdleTaskTCB;
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer = &IdleTaskTCB;
*ppxIdleTaskStackBuffer = IdleTaskStack;
*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
//定时器服务任务堆栈
static StackType_t TimerTaskStack[configTIMER_TASK_STACK_DEPTH];
//定时器服务任务控制块
static StaticTask_t TimerTaskTCB;
void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer,
uint32_t *pulTimerTaskStackSize )
{
*ppxTimerTaskTCBBuffer = &TimerTaskTCB;
*ppxTimerTaskStackBuffer = TimerTaskStack;
*pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}
// 定义任务栈和TCB所需的空间
#define TASK_STACK_SIZE 128
TaskHandle_t xHandle;
StackType_t xTaskStack[TASK_STACK_SIZE];
StaticTask_t xTaskTCB;
/*开始任务的相关宏定义*****/
//任务优先级
#define STATIC_START_TASK_PRIO 1
//任务堆栈大小
#define STATIC_START_STK_SIZE 128
//任务堆栈
StackType_t Static_StartTaskStack[STATIC_START_STK_SIZE];
//任务控制块
StaticTask_t StaticStartTaskTCB;
//任务句柄
TaskHandle_t StaticStartTask_Handler;
//任务函数
void static_start_task(void *pvParameters);
void vTaskFunction(void *pvParameters) {
for(;;) {
// 任务代码
LED0 = !LED0;
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
int main()
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
delay_init(168);
uart_init(115200);
LED_Init();
KEY_Init(); //初始化按键
//创建开始任务
StartTask_Handler=xTaskCreateStatic((TaskFunction_t )static_start_task, //任务函数
(const char* )"static_start_task", //任务名称
(uint32_t )STATIC_START_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )STATIC_START_TASK_PRIO, //任务优先级
(StackType_t* )Static_StartTaskStack, //任务堆栈
(StaticTask_t* )&StaticStartTaskTCB); //任务控制块
vTaskStartScheduler(); // 启动调度器
}
//开始任务函数
void static_start_task(void * pxParameters)
{
taskENTER_CRITICAL(); //进入临界区
// 创建静态任务
xHandle = xTaskCreateStatic(
vTaskFunction,
"StaticTask",
TASK_STACK_SIZE,
NULL,
1,
xTaskStack,
&xTaskTCB
);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
说明:以上代码在正点原子的工程代码基础上修改而来。
关于示例代码的说明
由于上面的代码比较长,所以需要简要说明一下,方便大家理解。
- 首先,我们要确定configSUPPORT_STATIC_ALLOCATION 等于1:
#define configSUPPORT_STATIC_ALLOCATION 1
- 在任务调度器函数
vTaskStartScheduler()内部会创建空闲任务,在创建之前会调用下图的vApplicationGetIdleTaskMemory()函数为空闲任务分配内存,TCB等。那么,问题来了,vApplicationGetIdleTaskMemory()这个函数没有被定义啊!

所以,就需要我们自己去定义了。
//空闲任务任务堆栈
static StackType_t IdleTaskStack[configMINIMAL_STACK_SIZE];
//空闲任务控制块
static StaticTask_t IdleTaskTCB;
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer = &IdleTaskTCB;
*ppxIdleTaskStackBuffer = IdleTaskStack;
*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
在这里设置了TCB,任务栈、栈深度。关键一点,栈深度不是我们自己自由设定的,它有最小值,就是上面代码的宏。它的定义如下:
位于FreeRTOSConfig.h,
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 130 )
- 而任务调度器同样会创建软件定时器任务。

而宏configUSE_TIMERS的值在FreeRTOSConfig.h中定义。
/* Software timer definitions. */
#define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY ( 2 )
#define configTIMER_QUEUE_LENGTH 10
#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 )
因为configUSE_TIMERS等于1,所以还需要为软件定时器的任务分配栈空间和TCB。【如果不需要软件定时器,就可以手动将此处改为0,也就不用管它了】具体如下图:

于是回顾上面的示例代码,你就明白了。

- 因为静态创建任务函数需要我们自行定义任务栈和TCB,所以对于每一个任务都需要定义下面的代码。
#define TASK_STACK_SIZE 128//定义栈深度
TaskHandle_t xHandle;//定义任务句柄,方便操作任务
StackType_t xTaskStack[TASK_STACK_SIZE];//任务栈定义
StaticTask_t xTaskTCB;//TCB定义
- 创建任务的时候,需要将任务栈和TCB都作为入参传递到函数中。
- 如果需要删除任务,需要将任务句柄作为入参,例如:
vTaskDelete(StartTask_Handler); //删除开始任务
特点
- 需要用户手动分配任务栈和控制块,适合对内存使用有严格要求的场景。
- 任务删除后,内存不会被系统回收,需手动管理。
- 适合任务数量较少或内存有限的嵌入式系统。
与 xTaskCreate() 的主要区别
| 特性 | xTaskCreate() | xTaskCreateStatic() |
|---|---|---|
| 内存分配方式 | 动态 | 静态 |
| 内存来源 | FreeRTOS 堆 | 用户提供 |
| 适用场景 | 通用 | 无动态内存环境 |
| 配置要求 | 需要配置堆 | 需要配置configSUPPORT_STATIC_ALLOCATION为1 |
适用场景
- 无动态内存环境:在禁止使用动态内存分配的系统中使用
- 确定性要求高:避免动态内存分配带来的不确定性
- 内存受限系统:精确控制内存使用
- 安全关键系统:避免内存碎片问题
受MPU保护的任务(xTaskCreateRestricted)
xTaskCreateRestricted 是 FreeRTOS 提供的一个用于创建受内存保护单元(MPU)保护的任务的函数。它允许在具有 MPU 的 MCU 上创建任务,并对任务的内存访问进行限制。该函数的功能与 xTaskCreate() 类似,但增加了对 MPU 的支持,适用于需要保护任务内存的场景。函数原型如下:
BaseType_t xTaskCreateRestricted(
const TaskParameters_t * const pxTaskDefinition,
TaskHandle_t * const pxCreatedTask
);
参数说明
| 参数名 | 类型 | 说明 |
|---|---|---|
pxTaskDefinition | const TaskParameters_t * const | 指向任务定义结构体的指针,包含任务的函数、名称、堆栈大小、参数、优先级等信息。 |
pxCreatedTask | TaskHandle_t * const | 用于返回创建任务的句柄,以便后续操作任务。 |
任务定义结构体 TaskParameters_t
TaskParameters_t 是一个包含任务所有必要信息的结构体,通常包括以下字段:
pvTaskCode:任务函数指针。pcName:任务名称。usStackDepth:任务栈大小。pvParameters:传递给任务的参数。uxPriority:任务优先级。xRegions:内存区域定义,用于 MPU 保护。
示例代码
以下是一个使用 xTaskCreateRestricted 创建受 MPU 保护任务的示例代码:
#include "FreeRTOS.h"
#include "task.h"
// 定义任务参数结构体
typedef struct {
TaskFunction_t pvTaskCode;
const char * const pcName;
unsigned short usStackDepth;
void *pvParameters;
UBaseType_t uxPriority;
const MemoryRegion_t * const xRegions;
} TaskParameters_t;
// 任务函数
void vMyTask(void *pvParameters) {
// 任务代码
while (1) {
// 任务逻辑
}
}
// 任务定义
TaskParameters_t xTaskDefinition = {
.pvTaskCode = vMyTask,
.pcName = "MyTask",
.usStackDepth = 1024,
.pvParameters = NULL,
.uxPriority = tskIDLE_PRIORITY,
.xRegions = NULL // 可以定义 MPU 保护区域
};
// 创建任务
TaskHandle_t xTaskHandle;
int main() {
// 创建任务
if (xTaskCreateRestricted(&xTaskDefinition, &xTaskHandle) != pdPASS) {
// 任务创建失败
}
// 启动调度器
vTaskStartScheduler();
// 任务创建成功后,进入死循环
for (;;);
}
注意事项
- MPU 支持:
xTaskCreateRestricted仅在 FreeRTOS-MPU 环境下可用,因此需要在FreeRTOSConfig.h中启用configSUPPORT_DYNAMIC_ALLOCATION。 - 内存分配:该函数使用动态内存分配来创建任务的控制块和栈空间。
- 任务删除:使用
vTaskDelete()删除任务时,需要提供任务的句柄和任务控制块的地址。
vTaskDelete 是 FreeRTOS 中用于删除任务的函数,其主要功能是将指定任务从系统中移除,并释放其占用的资源。以下是关于该函数的详细说明:
删除任务(vTaskDelete)
vTaskDelete 用于删除一个已创建的任务。该函数会从任务的就绪列表、阻塞列表、挂起列表和事件列表中移除该任务,并释放其任务控制块(TCB)和堆栈内存。需要注意的是,空闲任务(Idle Task)负责释放由系统分配的内存,而用户分配的内存需要在任务删除前手动释放,否则可能导致内存泄漏 。
void vTaskDelete(TaskHandle_t xTaskToDelete);
参数说明
xTaskToDelete:要删除的任务的句柄。若传入NULL,则表示删除当前正在运行的任务(即调用该函数的任务)。
注意事项
- 必须在
FreeRTOSConfig.h中启用INCLUDE_vTaskDelete,否则该函数不可用。 - 删除任务时,需确保空闲任务有足够时间执行内存释放。通常,将空闲任务的优先级设置为最低,以避免内存泄漏 。
- 删除任务后,系统会将该任务添加到“待删除列表” ,由空闲任务在下次运行时进行清理 。
函数执行流程
- 检查任务句柄:若
xTaskToDelete为NULL,则删除当前任务。 - 从任务列表中移除:将任务从就绪、阻塞、挂起和事件列表中移除。
- 更新任务状态:更新任务优先级记录,并递增
uxTaskNumber以通知内核调试器重新生成任务列表。 - 释放内存:
- 若删除的是当前任务,将其插入“待删除列表”,并增加
uxDeletedTasksWaitingCleanUp变量。 - 若删除的是其他任务,调用
prvDeleteTCB()函数释放任务的 TCB 和堆栈内存。
- 若删除的是当前任务,将其插入“待删除列表”,并增加
- 强制任务切换:若删除的是当前任务,调用
portPRE_TASK_DELETE_HOOK钩子函数,并执行任务切换 。
任务的挂起和恢复
在 FreeRTOS 中,任务的挂起和恢复是通过一系列 API 函数实现的。这些函数允许开发人员在需要暂停任务执行时将其挂起,并在需要时恢复任务的运行。以下是主要的挂起和恢复 API 函数及其功能说明:
| API 函数 | 功能描述 | 说明 |
|---|---|---|
vTaskSuspend(TaskHandle_t xTaskToSuspend) | 挂起指定任务 | 将任务设置为挂起状态,任务将永远处于挂起状态,除非调用 vTaskResume() 或 xTaskResumeFromISR() 。 |
vTaskResume(TaskHandle_t xTaskToResume) | 恢复挂起任务 | 将任务从挂起状态恢复到就绪状态,如果此时被恢复任务的优先级高于正在运行任务的优先级,则会发生任务切换 。 |
xTaskResumeFromISR(TaskHandle_t xTaskToResume) | 在中断服务函数中恢复任务 | 用于在中断服务函数中恢复任务,返回值指示是否需要进行上下文切换 。 |
vTaskSuspend()
- 功能:将指定任务设置为挂起状态。
- 参数:
xTaskToSuspend是要挂起的任务句柄。如果传入NULL,则表示挂起当前任务。 - 特点:一旦任务被挂起,它将永远处于挂起状态,直到被显式恢复。
vTaskResume()
- 功能:将挂起的任务恢复到就绪状态。
- 参数:
xTaskToResume是要恢复的任务句柄。 - 特点:恢复任务后,任务将重新加入就绪队列,等待调度器调度。如果恢复任务的优先级高于当前运行任务,则会发生任务切换。
xTaskResumeFromISR()
- 功能:在中断服务函数中恢复任务。
- 参数:
xTaskToResume是要恢复的任务句柄。 - 特点:该函数用于在中断上下文中恢复任务,返回值
pdTRUE表示需要进行上下文切换,pdFALSE表示不需要。
使用场景示例
以下是一个简单的示例,展示了如何在按键中断中挂起和恢复任务:
#include "FreeRTOS.h"
#include "task.h"
// 任务函数
void vTaskFunction(void *pvParameters) {
while (1) {
printf("Task is running\n");
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
int main() {
TaskHandle_t xTaskHandle = NULL;
// 创建任务
xTaskCreate(vTaskFunction, "Task", 1024, NULL, tskIDLE_PRIORITY, &xTaskHandle);
// 启动调度器
vTaskStartScheduler();
// 任务创建成功后,进入死循环
for (;;);
}
在中断服务函数中,可以使用 vTaskSuspend() 和 xTaskResumeFromISR() 来控制任务的挂起和恢复:
void EXTI0_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_LINE0) != RESET) {
printf("Suspend task\n");
vTaskSuspend(xTaskHandle); // 挂起任务
EXTI_ClearITPendingBit(EXTI_LINE0);
}
}
void EXTI1_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_LINE1) != RESET) {
printf("Resume task\n");
xTaskResumeFromISR(xTaskHandle); // 恢复任务
EXTI_ClearITPendingBit(EXTI_LINE1);
}
}
注意事项
- 任务挂起和恢复的顺序:必须确保在恢复任务之前,任务已经被正确挂起。
- 中断优先级:在中断服务函数中调用
xTaskResumeFromISR()时,中断优先级不能高于 FreeRTOS 管理的最高优先级,否则可能导致程序异常 。 - 任务状态:挂起的任务不会参与调度,也不会执行任何代码,直到被显式恢复 。
通过合理使用 vTaskSuspend()、vTaskResume() 和 xTaskResumeFromISR(),可以有效地管理任务的执行流程,确保任务在需要时暂停或恢复。
本文到此结束,欢迎点赞、转发、收藏!
1329

被折叠的 条评论
为什么被折叠?



