一.任务创建和删除
<一>.任务创建和删除的API函数
任务的创建和删除的本质就是调用FreeRTOS的API函数。
API函数 | 描述 |
xTaskCreate() | 动态方式创建任务 |
xTaskCreateStatic() | 静态方式创建任务 |
vTaskDelete() | 删除任务 |
- 动态创建任务:任务的任务控制块以及任务的栈空间所需的内存,均由FreeRTOS从FreeRTOS管理的堆中分配;
- 静态创建任务:任务的任务控制块以及任务的栈空间所需的内存,需用户分配提供。
1.动态创建函数
动态创建任务函数:
返回值 | 描述 |
pdPASS | 任务创建成功 |
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY | 任务创建失败 |
实现动态创建任务流程:
- 将宏configSUPPORT_DYNAMIC_ALLOCATION 配置为 1 ;
- 定义函数入口函数;
- 编写任务函数。
此函数创建的任务会立刻进入就绪态,由任务调度器调度运行。
动态创建任务函数内部实现:
- 申请堆栈内存&任务控制块内存;
- TCB结构体成员赋值;
- 添加新任务到就绪列表中。
任务控制块结构体成员介绍:
typedef struct tskTaskControlBlock
{
volatile StackType_t * pxTopOfStack; /* 任务栈栈顶,必须为TCB的第一个成员 */
ListItem_t xStateListItem; /* 任务状态列表项 */
ListItem_t xEventListItem; /* 任务事件列表项 */
UBaseType_t uxPriority; /* 任务优先级,数值越大,优先级越大 */
StackType_t * pxStack; /* 任务栈起始地址 */
char pcTaskName[ configMAX_TASK_NAME_LEN ]; /* 任务名字 */
…
省略很多条件编译的成员
} tskTCB;
注意:
- 任务栈栈顶,在任务切换时的任务上下文保存、任务恢复息息相关;
- 每个任务都有属于自己的任务控制块,类似身份证。
2.静态创建函数
静态创建任务函数:
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 /* 任务控制块指针,由用户分配 */
);
返回值 | 描述 |
NULL | 用户没有提供相应的内存,任务创建失败 |
其他值 | 任务句柄,任务创建成功 |
静态创建任务使用流程:
3.任务删除函数
void vTaskDelete(TaskHandle_t xTaskToDelete);
形参 | 描述 |
xTaskToDelete | 待删除任务的任务句柄 |
用于删除已被创建的任务;
被删除的任务将从就绪态任务列表、阻塞态任务列表、挂起态任务列表和事件列表中移除。
注意:
- 当传入的参数为NULL,则代表删除任务自身(当前正在运行的任务);
- 空闲任务会负责释放被删除任务中由系统分配的内存,但是由用户在任务删除前申请的内存, 则需要由用户在任务被删除前提前释放,否则将导致内存泄露 。
删除任务流程:
<二>.任务创建和删除(动态方法)
demo.c
#include "demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_PRIO 1 /* 定义任务优先级 */
#define START_TASK_STACK_SIZE 128 /*创建任务堆栈大小*/
TaskHandle_t start_task_handler; /* 定义任务句柄 */
void start_task( void * pvParameters );
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2 /* 定义任务优先级 */
#define TASK1_STACK_SIZE 128 /*创建任务堆栈大小*/
TaskHandle_t task1_handler; /* 定义任务句柄 */
void task1( void * pvParameters );
/* TASK2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_PRIO 3 /* 定义任务优先级 */
#define TASK2_STACK_SIZE 128 /*创建任务堆栈大小*/
TaskHandle_t task2_handler; /* 定义任务句柄 */
void task2( void * pvParameters );
/* TASK3 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK3_PRIO 25 /* 定义任务优先级 */
#define TASK3_STACK_SIZE 128 /*创建任务堆栈大小*/
TaskHandle_t task3_handler; /* 定义任务句柄 */
void task3( void * pvParameters );
/******************************************************************************************************/
/**
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
xTaskCreate( (TaskFunction_t ) start_task,
(const char * ) "start_task",
(configSTACK_DEPTH_TYPE ) START_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIO,
(TaskHandle_t * ) &start_task_handler );
vTaskStartScheduler();/* 开启任务调度器 */
}
void start_task( void * pvParameters )
{
taskENTER_CRITICAL(); /* 进入临界区,关闭中断 */
xTaskCreate( (TaskFunction_t ) task1,
(const char * ) "task1",
(configSTACK_DEPTH_TYPE ) TASK1_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_PRIO,
(TaskHandle_t * ) &task1_handler );
xTaskCreate( (TaskFunction_t ) task2,
(const char * ) "task2",
(configSTACK_DEPTH_TYPE ) TASK2_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK2_PRIO,
(TaskHandle_t * ) &task2_handler );
xTaskCreate( (TaskFunction_t ) task3,
(const char * ) "task3",
(configSTACK_DEPTH_TYPE ) TASK3_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK3_PRIO,
(TaskHandle_t * ) &task3_handler );
vTaskDelete(NULL);
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/* 任务一,实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{
while(1)
{
printf("TASK1正在运行!\r\n");
LED0_TOGGLE();
vTaskDelay(500);
}
}
/* 任务二,实现LED1每500ms翻转一次 */
void task2( void * pvParameters )
{
while(1)
{
printf("TASK2正在运行!\r\n");
LED1_TOGGLE();
vTaskDelay(500);
}
}
/* 任务三,判断按键KEY0,按下KEY0删除task1 */
void task3( void * pvParameters )
{
while(1)
{
printf("TASK3正在运行!\r\n");
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_11) == 1)
{
if(task1_handler != 0)
{
printf("删除TASK1!\r\n");
vTaskDelete(task1_handler);
}
}
vTaskDelay(10);
}
}
demo.h
#ifndef _DEMO_H
#define _DEMO_H
void freertos_demo(void);
#endif
main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "demo.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
led_init(); /* 初始化LED */
freertos_demo(); /* 运行FreeRTOS例程 */
}
临界区保护,就是保护哪些不想被打断的程序段。
<三>.任务创建和删除(静态方法)
FreeRTOSConfig.h:设置静态申请内存为1
#include "demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_PRIO 1 /* 定义任务优先级 */
#define START_TASK_STACK_SIZE 128 /*创建任务堆栈大小*/
TaskHandle_t start_task_handler; /* 定义任务句柄 */
StackType_t start_task_stack[START_TASK_STACK_SIZE];
StaticTask_t start_task_tcb;
void start_task( void * pvParameters );
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2 /* 定义任务优先级 */
#define TASK1_STACK_SIZE 128 /*创建任务堆栈大小*/
TaskHandle_t task1_handler; /* 定义任务句柄 */
StackType_t task1_stack[TASK1_STACK_SIZE];
StaticTask_t task1_tcb;
void task1( void * pvParameters );
/* TASK2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_PRIO 3 /* 定义任务优先级 */
#define TASK2_STACK_SIZE 128 /*创建任务堆栈大小*/
TaskHandle_t task2_handler; /* 定义任务句柄 */
StackType_t task2_stack[TASK2_STACK_SIZE];
StaticTask_t task2_tcb;
void task2( void * pvParameters );
/* TASK3 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK3_PRIO 25 /* 定义任务优先级 */
#define TASK3_STACK_SIZE 128 /*创建任务堆栈大小*/
TaskHandle_t task3_handler; /* 定义任务句柄 */
StackType_t task3_stack[TASK3_STACK_SIZE];
StaticTask_t task3_tcb;
void task3( void * pvParameters );
/******************************************************************************************************/
/* 空闲任务配置 */
StaticTask_t idle_task_tcb;
StackType_t idle_task_stack[configMINIMAL_STACK_SIZE];
/* 软件定时器任务配置 */
StaticTask_t timer_task_tcb;
StackType_t timer_task_stack[configTIMER_TASK_STACK_DEPTH];
/* 空闲任务内存分配 */
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize )
{
* ppxIdleTaskTCBBuffer = &idle_task_tcb; /* 空闲任务控制块 */
* ppxIdleTaskStackBuffer = idle_task_stack; /* 空闲任务的任务堆栈 */
* pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; /* 空闲任务的任务堆栈大小 */
}
/* 软件定时器内存分配 */
void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
StackType_t ** ppxTimerTaskStackBuffer,
uint32_t * pulTimerTaskStackSize )
{
* ppxTimerTaskTCBBuffer = &timer_task_tcb;
* ppxTimerTaskStackBuffer = timer_task_stack;
* pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}
/**
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
start_task_handler = xTaskCreateStatic( (TaskFunction_t ) start_task,
( char * ) "start_task",
( uint32_t ) START_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIO,
(StackType_t * ) start_task_stack,
(StaticTask_t * ) &start_task_tcb );
vTaskStartScheduler();/* 开启任务调度器 */
}
void start_task( void * pvParameters )
{
taskENTER_CRITICAL(); /* 进入临界区,关闭中断 */
task1_handler = xTaskCreateStatic( (TaskFunction_t ) task1,
( char * ) "task1",
( uint32_t ) TASK1_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_PRIO,
(StackType_t * ) task1_stack,
(StaticTask_t * ) &task1_tcb );
task2_handler = xTaskCreateStatic( (TaskFunction_t ) task2,
( char * ) "task2",
( uint32_t ) TASK2_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK2_PRIO,
(StackType_t * ) task2_stack,
(StaticTask_t * ) &task2_tcb );
task3_handler = xTaskCreateStatic( (TaskFunction_t ) task3,
( char * ) "task3",
( uint32_t ) TASK3_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK3_PRIO,
(StackType_t * ) task3_stack,
(StaticTask_t * ) &task3_tcb );
vTaskDelete(start_task_handler);
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/* 任务一,实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{
while(1)
{
printf("TASK1正在运行!\r\n");
LED0_TOGGLE();
vTaskDelay(500);
}
}
/* 任务二,实现LED1每500ms翻转一次 */
void task2( void * pvParameters )
{
while(1)
{
printf("TASK2正在运行!\r\n");
LED1_TOGGLE();
vTaskDelay(500);
}
}
/* 任务三,判断按键KEY0,按下KEY0删除task1 */
void task3( void * pvParameters )
{
while(1)
{
printf("TASK3正在运行!\r\n");
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_11) == 1)
{
if(task1_handler != 0)
{
printf("删除TASK1!\r\n");
vTaskDelete(task1_handler);
}
}
vTaskDelay(10);
}
}
main.c和demo.h文件和动态的一样,以后也最常使用动态创建任务。
<四>.动态任务创建和删除的API函数解析(暂不学习)
二.FreeRTOS的任务挂起与恢复
<一>.任务的挂起与恢复的API函数
API函数 | 描述 |
vTaskSuspend() | 挂起任务 |
vTaskResume() | 恢复被挂起的任务 |
xTaskResumeFromISR() | 在中断恢复被挂起的任务 |
- 挂起:挂起任务类似暂停,可恢复;删除任务,无法恢复,类似“人死两清”;
- 恢复:恢复被挂起的任务;
- FromISR:带FromISR后缀是在中断函数中专用的API函数。
1.任务挂起函数介绍
void vTaskSuspend(TaskHandle_t xTaskToSuspend)
形参 | 描述 |
xTaskToSuspend | 待挂起任务的任务句柄 |
- 此函数用于挂起任务,使用时需将宏 INCLUDE_vTaskSuspend 配置为 1;
- 无论优先级如何,被挂起的任务都将不再被执行,直到任务被恢复 ;
注意:当传入的参数为NULL,则代表挂起任务自身(当前正在运行的任务)。
2.任务恢复函数介绍(任务中恢复)
任务中恢复被挂起函数:void vTaskResume(TaskHandle_t xTaskToResume)
形参 | 描述 |
xTaskToResume | 待恢复任务的任务句柄 |
使用该函数注意宏:INCLUDE_vTaskSuspend必须定义为 1;
注意:任务无论被 vTaskSuspend() 挂起多少次,只需在任务中调用 vTakResume() 恢复一次,就可以继续运行。且被恢复的任务会进入就绪态!
3.任务恢复函数介绍(中断中恢复)
中断中恢复被挂起函数: BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume)
形参 | 描述 |
xTaskToResume | 待恢复任务的任务句柄 |
函数:xTaskResumeFromISR返回值描述如下:
返回值 | 描述 |
pdTRUE | 任务恢复后需要进行任务切换 |
pdFALSE | 任务恢复后不需要进行任务切换 |
- 使用该函数注意宏:INCLUDE_vTaskSuspend 和 INCLUDE_xTaskResumeFromISR 必须定义为 1;
- 该函数专用于中断服务函数中,用于解挂被挂起任务;
注意:中断服务程序中要调用freeRTOS的API函数则中断优先级不能高于FreeRTOS所管理的最高优先级。
<二>.任务挂起与恢复实验
demo.c
#include "demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_PRIO 1 /* 定义任务优先级 */
#define START_TASK_STACK_SIZE 128 /*创建任务堆栈大小*/
TaskHandle_t start_task_handler; /* 定义任务句柄 */
void start_task( void * pvParameters );
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2 /* 定义任务优先级 */
#define TASK1_STACK_SIZE 128 /*创建任务堆栈大小*/
TaskHandle_t task1_handler; /* 定义任务句柄 */
void task1( void * pvParameters );
/* TASK2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_PRIO 3 /* 定义任务优先级 */
#define TASK2_STACK_SIZE 128 /*创建任务堆栈大小*/
TaskHandle_t task2_handler; /* 定义任务句柄 */
void task2( void * pvParameters );
/* TASK3 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK3_PRIO 25 /* 定义任务优先级 */
#define TASK3_STACK_SIZE 128 /*创建任务堆栈大小*/
TaskHandle_t task3_handler; /* 定义任务句柄 */
void task3( void * pvParameters );
/******************************************************************************************************/
/**
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
xTaskCreate( (TaskFunction_t ) start_task,
(const char * ) "start_task",
(configSTACK_DEPTH_TYPE ) START_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIO,
(TaskHandle_t * ) &start_task_handler );
vTaskStartScheduler();/* 开启任务调度器 */
}
void start_task( void * pvParameters )
{
taskENTER_CRITICAL(); /* 进入临界区,关闭中断 */
xTaskCreate( (TaskFunction_t ) task1,
(const char * ) "task1",
(configSTACK_DEPTH_TYPE ) TASK1_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_PRIO,
(TaskHandle_t * ) &task1_handler );
xTaskCreate( (TaskFunction_t ) task2,
(const char * ) "task2",
(configSTACK_DEPTH_TYPE ) TASK2_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK2_PRIO,
(TaskHandle_t * ) &task2_handler );
xTaskCreate( (TaskFunction_t ) task3,
(const char * ) "task3",
(configSTACK_DEPTH_TYPE ) TASK3_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK3_PRIO,
(TaskHandle_t * ) &task3_handler );
vTaskDelete(NULL);
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/* 任务一,实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{
uint32_t task1_num = 0;
while(1)
{
printf("task1_num:%d\r\n",++task1_num);
LED0_TOGGLE();
vTaskDelay(500);
}
}
/* 任务二,实现LED1每500ms翻转一次 */
void task2( void * pvParameters )
{
uint32_t task2_num = 0;
while(1)
{
printf("task2_num:%d\r\n",++task2_num);
LED1_TOGGLE();
vTaskDelay(500);
}
}
/* 任务三,判断按键KEY0,按下KEY0删除task1 */
void task3( void * pvParameters )
{
while(1)
{
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_11) == 1)
{
vTaskSuspend(task1_handler);
}
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12) == 1)
{
vTaskResume(task1_handler);
}
vTaskDelay(10);
}
}
demo.h 和 main.c和之前的一样。