【03】FreeRTOS的任务创建(静态和动态)和删除
目录
1.任务创建和删除的API函数
1.1动态创建任务函数
1.1.1实现动态创建任务流程
1.1.2任务控制块结构成员介绍
1.2静态创建任务函数
1.2.1实现静态创建任务流程
1.3任务删除函数
1.3.1删除任务流程
2.任务创建和删除(动态方法)
2.1宏configSUPPORT_DYNAMIC_ALLOCATION置1
2.2定义函数入口参数
2.3编写任务函数
3.任务创建和删除(静态方法)
3.1将宏configSUPPORT_STATIC_ALLOCATION 配置为 1
3.2实现两个接口函数
3.3定义函数入口参数
4.总结
1.任务创建和删除的API函数
任务的创建和删除本质就是调用FreeRTOS的API函数。
API函数 | 描述 |
---|---|
xTaskCreat() | 动态方式创建任务 |
xTaskCreatStatic() | 静态方式创建任务 |
vTaskDelete | 删除任务 |
动态创建任务:任务的任务控制块以及任务的栈空间所需的内存,均由FreeRTOS从FreeRTOS管理的堆中分配。(由FreeRTOS自动分配,不需要人为操作)
静态创建任务:任务的任务控制块以及任务的栈空间所需的内存,需要用户分配提供。(人为地定义内存,比如说定义一个数组当作任务栈空间)
二者的区别除了是否需要人为操作去分配空间,还有动态申请的空间由FreeRTOS所管理,静态申请的空间不属于FreeRTOS管理,独立于FreeRTOS。
1.1动态创建任务函数
BaseType_t xTaskCreate
( TaskFunction_t pxTaskCode, /* 指向任务函数的指针 */
const char * const pcName, /* 任务名字,最大长度configMAX_TASK_NAME_LEN */
const configSTACK_DEPTH_TYPE usStackDepth, /* 任务堆栈大小,注意字为单位 */
void * const pvParameters, /* 传递给任务函数的参数 */
UBaseType_t uxPriority, /* 任务优先级,范围:0 ~ configMAX_PRIORITIES - 1 */
TaskHandle_t * const pxCreatedTask /* 任务句柄,就是任务的任务控制块 */
)
参数介绍
pxTaskCode: 指向任务函数的指针。比如创建了一个任务,任务所需要实现的功能全部放在任务函数中,通过此参数指向任务函数。
pcName:任务名字。名字长度由FreeRTOSConfig.h中的宏configMAX_TASK_NAME_LEN决定,默认为16字符长度。
usStackDepth:任务堆栈大小。注意单位是“字”。例如堆大小为100,则实际大小为100×4=400字节。
pvParameters:传递给任务函数的参数。一般用不到,传入空(NULL)。
uxPriority:任务优先级。每个任务都有自己的优先级,具体范围是0~configMAX_PRIORITIES - 1,宏定义在FreeRTOSConfig中设置。在程序中设置的为32,并且任务优先级越大,任务就越优先。
pxCreatedTask:任务句柄。删除任务是通过任务句柄进行操作,任务句柄其实就是任务的任务控制块。在函数内部任务控制块等效于任务句柄。
动态任务创建任务返回值BaseType 返回值 描述 pdPASS 任务创建成功 errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY 任务创建失败(其中MEMORY,主要是指任务堆栈过大,动态创建是在FreeRTOS管理的堆空间中创建,如果超出了堆空间大小,则会创建失败)
1.1.1实现动态创建任务流程
1、将宏cofigSUPPORT_DYNAMIC_ALLOCATION配置为1(在FreeRTOSCofig.h中配置)
2、定义函数的入口参数(可以提前定义,任务创建时代入);
3、编写任务函数(任务需要实现的功能,在任务函数中完成);
使用xTaskCreat()函数创建的任务会立即进入就绪态,由任务调度器调度运行。比如说创建了很多任务,这些任务创建后会立刻进入就绪态,由任务调度器在就绪态中找到任务优先级最高的去执行。
动态创建任务的内部实现方式:
1、申请任务堆栈and任务控制块的内存(只需要定义所申请的大小,之后由FreeRTOS自动地去申请);
2、TCB结构体成员赋值(TCB,任务控制块。类似于人的身份证,每个任务都有自己的任务控制块,任务控制块存取任务的一些特征,比如任务名、优先级、状态等);
3、添加新任务到就绪列表中(任务进入到就绪态)。
1.1.2任务控制块结构成员介绍
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;
任务控制块是一个结构体,结构体存在很多的成员变量,就好比任务的身份证,保留了任务的一些特征。
参数介绍
*pxTopOfStack:任务栈顶指针。必须为TCB的第一个成员,因为和任务切换、任务上下文保存、任务恢复都息息相关。
xStateListItem:任务状态列表项。任务处于的状态保存在此参数中,比如:就绪态、挂起态、阻塞态、运行态。
xEventListItem:任务事件列表项。如果当前正在等待某些事件,就会用到此参数。
uxPriority:任务优先级。数值越大,优先级越大。当前任务的优先级存在此参数中。
*pxStack:任务堆栈的首地址。
pcTaskName[ configMAX_TASK_NAME_LEN ]:任务名字。
1.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 /* 任务控制块指针,由用户分配 */
);
静态创建任务函数xTaskCreatStatic()和动态创建任务函数xTaskCreat()类似。
参数介绍
pxTaskCode:指向任务函数的指针。
pcName:任务函数名字。
ulStackDepth:任务堆栈大小。以“字”为单位。
pvParameters:传递的任务函数参数。一般为空,即传入“NULL”。
uxPriority:任务优先级。
puxStackBuffer:任务堆栈。一般为数组,由用户自己定义(这里和动态创建有区别,动态创建只需要指定任务堆栈大小ulStackDepth,剩下的是由FreeRTOS根据任务大小,自动去申请堆大小,而静态需要给定数组地址)。
pxTaskBuffer:任务控制块指针。指向任务控制块的地址,任务控制块保留了很对信息,信息需要内存来保存,这部分内存也需要用户自己分配。
静态创建函数返回值TaskHandle_t 返回值 描述 NULL 用户没有提供相应的内存,任务创建失败。(比如说任务堆栈puxStackBufer参数并没有指定,那么就会创建失败) 其他值 任务句柄,任务创建成功
1.2.1实现静态创建任务流程
1、需将宏cofigSUPPORT_STATIC_ALLOCATION配置为1(在FreeRTOSCofig.h中配置);
2、定义空闲任务and定时器任务的任务堆栈及TCB(空闲任务是必须有,在RTOS中CPU是不能停止的,在空闲时需要执行空闲任务。软件定时器任务是可选的,当使能了软件定时器功能,这个任务就需要被创建,如果没有被使能,就不需要创建软件定时器任务,使能也是在FreeRTOSCofig.h中进行配置。这两个任务都是静态创建,用户都需要分配任务堆栈和任务控制块);
3、实现两个接口函数(第一个是空闲任务的内存赋值vApplicationGetldleTaskMemory(),第二个是软件定时器的内存赋值vApplicationGetTimerTaskMemory()。由于在第2步任务堆栈、任务控制块需要赋值给结构体成员,就通过这两个函数来实现。空闲任务是必须的,软件定时器任务是可选的);
4、定义函数入口参数
5、编写任务函数
静态创建任务函数xTaskCreatStatic()创建的任务会立刻进入就绪态,由任务调度器进行调度。
静态创建任务内部实现逻辑
1、TCB结构体成员赋值(将任务的一些特征赋值给任务控制块TCB)
2、添加新任务到就绪列表中
1.3任务删除函数
void vTaskDelete(TaskHandle_t xTaskToDelete);
任务删除函数只有一个入口参数 xTaskToDelete:待删除任务的任务句柄。比如给我们创建了一个任务,任务是具有任务句柄的,删除任务就需要将任务句柄传入此函数就可以删除任务了。此函数用于删除已经被创建成功的任务,如果没有被创建,是不能被删除的。
被删除的任务将从就绪态任务列表、阻塞态任务列表、 挂起态任务列表和事件列表中移除(不管任务处于何种状态,只要调用删除函数vTaskDelete(),传入需要删除的任务的句柄,那么都会被删除)。
注意:
1、当传入的参数为NULL,则代表删除任务自身(当前正在运行的任务)。比如说创建了两个任务task1、task2,在task1的任务函数中调用的任务删除,传入的参数为task2的任务句柄,则删除task2这个任务,如果传入NULL参数,则删除自身task1任务。
2、空闲任务会负责释放被删除任务中被系统分配的内存(动态创建任务的内存是由系统自己分配的,而静态创建任务是由用户自己定义,这里是针对动态创建。删除自身任务,即函数xTaskDelete()传入NULL时,才会利用空闲任务释放掉被删除任务使用的系统分配的内存,如果是在task1任务中删除task2,则在task1中释放task2所使用的内存空间),但是由用户在任务删除前申请的内存,则需要用户在任务被删除前提前释放,否则将导致内存泄露(这里针对静态创建)。
1.3.1删除任务流程
1、使用删除任务函数,需将宏INCLUDE_vTaskDelete配置为1。(宏在FreeRTOSCofig.h中定义)
2、入口参数输入需要删除的任务句柄(NULL代表删除本身)。
删除任务内部实现流程
1、获取所要删除任务的控制块:通过传入的任务句柄,判断所需要删除哪个任务,NULL代表删除自身。(找到所需要删除任务的任务控制块)
2、将被删除任务,在所在列表中移除:将该任务在所在列表中移除,包括:就绪、阻塞、挂起、事件等列表。
3、判断所需要删除的任务:删除任务自身,需先添加到等待删除列表,内存释放将在空闲任务执行;删除其他任务,释放内存,任务总数量减1。
4、更新下个任务的阻塞时间:更新下一个任务的阻塞超时时间,以防被删除的任务就是下一个阻塞超时的任务。比如有3个任务,task1、task2、task3,task2及task3进入阻塞态,task2阻塞延时20ms,task3阻塞延时50ms,task2和task3会挂载到阻塞列表中,时间短的task2会放在task3前面,此时肯定是20ms的task2先到,时间到了将task2放入就绪列表,如果在task1中删除的任务刚好为task2,不管task2在何种状态都将被移除,而阻塞时间还是20ms,则需要更新成下一个阻塞时间50ms。
2.任务创建和删除(动态方法)
1、实验目的:学会 xTaskCreate() 和 vTaskDelete()的使用
2、实验设计:将设计四个任务:start_task、task1、task2、task3
四个任务的功能如下:
start_task:用来创建其他的三个任务(start_task只执行一次,执行完以后调用vTaskDelete()函数进行删除,如果重复调用,重复创建将导致FreeRTOS管理的内存堆不够使用)
task1:实现LED0每500ms闪烁一次
task2:实现LED1每1000ms闪烁一次
task3:判断按键KEY0是否按下,按下则删掉task2
本次实验将根据上次文章02中使用的文件进行。
删除main.c中用不到的变量及函数调用。在测试程序freertos_demo.c中只留下以下内容:freertos_demo()函数是FreeRTOS的入口函数,在此任务中进行任务创建和开启任务调度,开启任务调度需要调用vTaskStartScheduler()函数。
#include "freertos_demo.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
/******************************************************************************************************/
/**
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
//vTaskStartScheduler();
}
2.1宏configSUPPORT_DYNAMIC_ALLOCATION置1
在FreeRTOSConfig.h中将宏configSUPPORT_DYNAMIC_ALLOCATION置1。
#define configSUPPORT_DYNAMIC_ALLOCATION 1 //支持动态内存申请
2.2定义函数入口参数
在task.h中250行左右,介绍了xTaskCreat()函数的使用,并且举了示例,可以通过程序介绍进行创建。也可以通过官网:官网链接,显示如下,找到API Reference找到特定函数,函数介绍中有使用示例。
将函数xTaskCreat()整个复制到freertos_demo()函数中,并加入强制转换,防止报警告。xTaskCreate()函数的参数pxTaskCode为函数名,需创建start_task()函数任务,并且在freertos_demo()函数之前声明此函数的存在(此处并不是完整版函数,只是为了创建任务)。
void start_task()
{
while(1);
}
pcName和函数名设置相同即可,此处为字符串需要加双引号,“start_task”;
usStackDepth任务堆大小,在freertos_demo.c的开始创建宏。此时的单位是“字”,换算成字节需要乘以4。此处用128是偏大设置,在FreeRTOS存在查找历史剩余最小空间值的API函数uxTaskGetStackHighWaterMark(),根据剩余的大小来设置堆栈大小。
#define START_TASK_STACK_SIZE 128
pvParameters入口参数,入口参数没有,则设置NULL。
uxPriority任务优先级,同样在freertos_demo.c的开始创建宏。
#define START_TASK_PRIO 1
pxCreatedTask任务句柄,同样在freertos_demo.c的开始创建。
TaskHandle_t start_task_handler
2.3编写任务函数
freertos_demo.c中修改后的xTaskCreate()函数调用如下:
BaseType_t xTaskCreate((TaskFunction_t ) start_task,
(char * ) "start_task",
(configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,
(void * ) NILL,
(UBaseType_t ) START_TASK_PRIO,
(TaskHandle_t * ) start_task_handler )
用相同方式创建task1,task2,task3任务函数,并实现所需功能。在start_task任务函数中实现创建task1、task2、task3并删除自身的功能。
修改完(并未完成,可正常显示,需要在main()函数中调用按键的初始化 Key_Init() )如下所示:
#include "freertos_demo.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.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 4
#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,
(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 )
{
xTaskCreate((TaskFunction_t ) task1,
(char * ) "task1",
(configSTACK_DEPTH_TYPE ) TASK1_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_PRIO,
(TaskHandle_t * ) &task1_handler );
xTaskCreate((TaskFunction_t ) task2,
(char * ) "task2",
(configSTACK_DEPTH_TYPE ) TASK2_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK2_PRIO,
(TaskHandle_t * ) &task2_handler );
xTaskCreate((TaskFunction_t ) task3,
(char * ) "task3",
(configSTACK_DEPTH_TYPE ) TASK3_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK3_PRIO,
(TaskHandle_t * ) &task3_handler );
vTaskDelete(NULL);
}
/* 任务一,实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{
while(1)
{
printf("task1正在运行!\r\n");
LED0=~LED0;
vTaskDelay(500);
}
}
/* 任务二,实现LED1每500ms翻转一次 */
void task2( void * pvParameters )
{
while(1)
{
printf("task2正在运行!\r\n");
LED1=~LED1;
vTaskDelay(500);
}
}
/* 任务三,判断按键KEY0,按下KEY0删除task1 */
void task3( void * pvParameters )
{
uint8_t key = 0;
while(1)
{
printf("task3正在运行!\r\n");
key = KEY_Scan(0);
if(key == KEY0_PRES)
{
printf("删除task2!\r\n");
vTaskDelete(task2_handler);
}
vTaskDelay(10);
}
}
以上程序出现的现象是:
虽然优先级start_task<task1<task2<task3,但是,由于先创建start_task,所以先运行start_task。
在start_task创建task1,task1比start_task优先级高,则允许task1抢占start_task,在task1阻塞时运行start_task,创建task2。
task2又比start_task优先级高,则运行task2,在task2阻塞时运行start_task,创建task3。同理,运行task3,阻塞时删除start_task。
剩下的任务task3任务优先级最高,在task3阻塞时运行task2或者task1,由于task2优先级比task1高,所以一般是运行task2。
按下KEY0,删除task2,则task1会被允许,因为task3中存在延时阻塞。
多次删除task1时,串口会报错:Error:..\FreeRTOS\portable\MemMang\heap_4.c,292
如果一开始高优先级执行(即:想在start_task任务创建完再运行),在进入开始任务start_task后关闭任务调度即可。任务切换是在中断中执行的,FreeRTOS提供了进入临界区的API函数(taskENTER_CRITICAL进入临界区,taskEXIT_CRITICAL退出临界区),用来关闭中断。在task3中增加判断task2是否存在的判断,避免task2被删除了以后,再次删除task2串口打印错误。
完整的freertos_demo.c代码如下:
#include "freertos_demo.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.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 4
#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,
(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,
(char * ) "task1",
(configSTACK_DEPTH_TYPE ) TASK1_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_PRIO,
(TaskHandle_t * ) &task1_handler );
xTaskCreate((TaskFunction_t ) task2,
(char * ) "task2",
(configSTACK_DEPTH_TYPE ) TASK2_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK2_PRIO,
(TaskHandle_t * ) &task2_handler );
xTaskCreate((TaskFunction_t ) task3,
(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=~LED0;
vTaskDelay(500);
}
}
/* 任务二,实现LED1每500ms翻转一次 */
void task2( void * pvParameters )
{
while(1)
{
printf("task2正在运行!\r\n");
LED1=~LED1;
vTaskDelay(1000);
}
}
/* 任务三,判断按键KEY0,按下KEY0删除task1 */
void task3( void * pvParameters )
{
uint8_t key = 0;
while(1)
{
printf("task3正在运行!\r\n");
key = KEY_Scan(0);
if(key == KEY0_PRES)
{
if(task2_handler!=NULL){
printf("删除task2!\r\n");
vTaskDelete(task2_handler);
task2_handler=NULL;
}
}
vTaskDelay(10);
}
}
3.任务创建和删除(静态方法)
1、实验目的:学会 xTaskCreateStatic() 和 vTaskDelete()的使用
2、实验设计:将设计四个任务:start_task、task1、task2、task3
四个任务的功能如下:
start_task:用来创建其他的三个任务(start_task只执行一次,执行完以后调用vTaskDelete()函数进行删除,如果重复调用,重复创建将导致FreeRTOS管理的内存堆不够使用)
task1:实现LED0每500ms闪烁一次
task2:实现LED1每1000ms闪烁一次
task3:判断按键KEY0是否按下,按下则删掉task2
本次实验程序基于本篇文章中第二节内容。
不同的是任务创建方式,所以删除start_task()函数中任务创建,task1、task2、task3中实现的功能不做改变。删除完如下所示:
#include "freertos_demo.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.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 4
#define TASK3_STACK_SIZE 128
TaskHandle_t task3_handler;
void task3( void * pvParameters );
/******************************************************************************************************/
/**
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
vTaskStartScheduler();
}
void start_task( void * pvParameters )
{
taskENTER_CRITICAL(); /*进入临界区*/
vTaskDelete(NULL);
taskEXIT_CRITICAL(); /*退出临界区*/
}
/* 任务一,实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{
while(1)
{
printf("task1正在运行!\r\n");
LED0=~LED0;
vTaskDelay(500);
}
}
/* 任务二,实现LED1每500ms翻转一次 */
void task2( void * pvParameters )
{
while(1)
{
printf("task2正在运行!\r\n");
LED1=~LED1;
vTaskDelay(500);
}
}
/* 任务三,判断按键KEY0,按下KEY0删除task1 */
void task3( void * pvParameters )
{
uint8_t key = 0;
while(1)
{
printf("task3正在运行!\r\n");
key = KEY_Scan(0);
if(key == KEY0_PRES)
{
if(task2_handler!=NULL){
printf("删除task2!\r\n");
vTaskDelete(task2_handler);
task2_handler=NULL;
}
}
vTaskDelay(10);
}
}
3.1将宏configSUPPORT_STATIC_ALLOCATION 配置为 1
由于在老版本的FreeRTOSCofig.h中并没有定义宏configSUPPORT_STATIC_ALLOCATION,所以复制2022正点原子FreeRTOS程序中的FreeRTOSCoifig.h中程序。头文件引入路径需稍作修改。
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
/* 头文件 */
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include <stdint.h>
extern uint32_t SystemCoreClock;
/* 基础配置项 */
#define configUSE_PREEMPTION 1 /* 1: 抢占式调度器, 0: 协程式调度器, 无默认需定义 */
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 /* 1: 使用硬件计算下一个要运行的任务, 0: 使用软件算法计算下一个要运行的任务, 默认: 0 */
#define configUSE_TICKLESS_IDLE 0 /* 1: 使能tickless低功耗模式, 默认: 0 */
#define configCPU_CLOCK_HZ SystemCoreClock /* 定义CPU主频, 单位: Hz, 无默认需定义 */
//#define configSYSTICK_CLOCK_HZ (configCPU_CLOCK_HZ / 8)/* 定义SysTick时钟频率,当SysTick时钟频率与内核时钟频率不同时才可以定义, 单位: Hz, 默认: 不定义 */
#define configTICK_RATE_HZ 1000 /* 定义系统时钟节拍频率, 单位: Hz, 无默认需定义 */
#define configMAX_PRIORITIES 32 /* 定义最大优先级数, 最大优先级=configMAX_PRIORITIES-1, 无默认需定义 */
#define configMINIMAL_STACK_SIZE 128 /* 定义空闲任务的栈空间大小, 单位: Word, 无默认需定义 */
#define configMAX_TASK_NAME_LEN 16 /* 定义任务名最大字符数, 默认: 16 */
#define configUSE_16_BIT_TICKS 0 /* 1: 定义系统时钟节拍计数器的数据类型为16位无符号数, 无默认需定义 */
#define configIDLE_SHOULD_YIELD 1 /* 1: 使能在抢占式调度下,同优先级的任务能抢占空闲任务, 默认: 1 */
#define configUSE_TASK_NOTIFICATIONS 1 /* 1: 使能任务间直接的消息传递,包括信号量、事件标志组和消息邮箱, 默认: 1 */
#define configTASK_NOTIFICATION_ARRAY_ENTRIES 1 /* 定义任务通知数组的大小, 默认: 1 */
#define configUSE_MUTEXES 1 /* 1: 使能互斥信号量, 默认: 0 */
#define configUSE_RECURSIVE_MUTEXES 1 /* 1: 使能递归互斥信号量, 默认: 0 */
#define configUSE_COUNTING_SEMAPHORES 1 /* 1: 使能计数信号量, 默认: 0 */
#define configUSE_ALTERNATIVE_API 0 /* 已弃用!!! */
#define configQUEUE_REGISTRY_SIZE 8 /* 定义可以注册的信号量和消息队列的个数, 默认: 0 */
#define configUSE_QUEUE_SETS 1 /* 1: 使能队列集, 默认: 0 */
#define configUSE_TIME_SLICING 1 /* 1: 使能时间片调度, 默认: 1 */
#define configUSE_NEWLIB_REENTRANT 0 /* 1: 任务创建时分配Newlib的重入结构体, 默认: 0 */
#define configENABLE_BACKWARD_COMPATIBILITY 0 /* 1: 使能兼容老版本, 默认: 1 */
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 0 /* 定义线程本地存储指针的个数, 默认: 0 */
#define configSTACK_DEPTH_TYPE uint16_t /* 定义任务堆栈深度的数据类型, 默认: uint16_t */
#define configMESSAGE_BUFFER_LENGTH_TYPE size_t /* 定义消息缓冲区中消息长度的数据类型, 默认: size_t */
/* 内存分配相关定义 */
#define configSUPPORT_STATIC_ALLOCATION 1 /* 1: 支持静态申请内存, 默认: 0 */
#define configSUPPORT_DYNAMIC_ALLOCATION 1 /* 1: 支持动态申请内存, 默认: 1 */
#define configTOTAL_HEAP_SIZE ((size_t)(10 * 1024)) /* FreeRTOS堆中可用的RAM总量, 单位: Byte, 无默认需定义 */
#define configAPPLICATION_ALLOCATED_HEAP 0 /* 1: 用户手动分配FreeRTOS内存堆(ucHeap), 默认: 0 */
#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 0 /* 1: 用户自行实现任务创建时使用的内存申请与释放函数, 默认: 0 */
/* 钩子函数相关定义 */
#define configUSE_IDLE_HOOK 0 /* 1: 使能空闲任务钩子函数, 无默认需定义 */
#define configUSE_TICK_HOOK 0 /* 1: 使能系统时钟节拍中断钩子函数, 无默认需定义 */
#define configCHECK_FOR_STACK_OVERFLOW 0 /* 1: 使能栈溢出检测方法1, 2: 使能栈溢出检测方法2, 默认: 0 */
#define configUSE_MALLOC_FAILED_HOOK 0 /* 1: 使能动态内存申请失败钩子函数, 默认: 0 */
#define configUSE_DAEMON_TASK_STARTUP_HOOK 0 /* 1: 使能定时器服务任务首次执行前的钩子函数, 默认: 0 */
/* 运行时间和任务状态统计相关定义 */
#define configGENERATE_RUN_TIME_STATS 0 /* 1: 使能任务运行时间统计功能, 默认: 0 */
#if configGENERATE_RUN_TIME_STATS
#include "./BSP/TIMER/btim.h"
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ConfigureTimeForRunTimeStats()
extern uint32_t FreeRTOSRunTimeTicks;
#define portGET_RUN_TIME_COUNTER_VALUE() FreeRTOSRunTimeTicks
#endif
#define configUSE_TRACE_FACILITY 1 /* 1: 使能可视化跟踪调试, 默认: 0 */
#define configUSE_STATS_FORMATTING_FUNCTIONS 1 /* 1: configUSE_TRACE_FACILITY为1时,会编译vTaskList()和vTaskGetRunTimeStats()函数, 默认: 0 */
/* 协程相关定义 */
#define configUSE_CO_ROUTINES 0 /* 1: 启用协程, 默认: 0 */
#define configMAX_CO_ROUTINE_PRIORITIES 2 /* 定义协程的最大优先级, 最大优先级=configMAX_CO_ROUTINE_PRIORITIES-1, 无默认configUSE_CO_ROUTINES为1时需定义 */
/* 软件定时器相关定义 */
#define configUSE_TIMERS 1 /* 1: 使能软件定时器, 默认: 0 */
#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) /* 定义软件定时器任务的优先级, 无默认configUSE_TIMERS为1时需定义 */
#define configTIMER_QUEUE_LENGTH 5 /* 定义软件定时器命令队列的长度, 无默认configUSE_TIMERS为1时需定义 */
#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2) /* 定义软件定时器任务的栈空间大小, 无默认configUSE_TIMERS为1时需定义 */
/* 可选函数, 1: 使能 */
#define INCLUDE_vTaskPrioritySet 1 /* 设置任务优先级 */
#define INCLUDE_uxTaskPriorityGet 1 /* 获取任务优先级 */
#define INCLUDE_vTaskDelete 1 /* 删除任务 */
#define INCLUDE_vTaskSuspend 1 /* 挂起任务 */
#define INCLUDE_xResumeFromISR 1 /* 恢复在中断中挂起的任务 */
#define INCLUDE_vTaskDelayUntil 1 /* 任务绝对延时 */
#define INCLUDE_vTaskDelay 1 /* 任务延时 */
#define INCLUDE_xTaskGetSchedulerState 1 /* 获取任务调度器状态 */
#define INCLUDE_xTaskGetCurrentTaskHandle 1 /* 获取当前任务的任务句柄 */
#define INCLUDE_uxTaskGetStackHighWaterMark 1 /* 获取任务堆栈历史剩余最小值 */
#define INCLUDE_xTaskGetIdleTaskHandle 1 /* 获取空闲任务的任务句柄 */
#define INCLUDE_eTaskGetState 1 /* 获取任务状态 */
#define INCLUDE_xEventGroupSetBitFromISR 1 /* 在中断中设置事件标志位 */
#define INCLUDE_xTimerPendFunctionCall 1 /* 将函数的执行挂到定时器服务任务 */
#define INCLUDE_xTaskAbortDelay 1 /* 中断任务延时 */
#define INCLUDE_xTaskGetHandle 1 /* 通过任务名获取任务句柄 */
#define INCLUDE_xTaskResumeFromISR 1 /* 恢复在中断中挂起的任务 */
/* 中断嵌套行为配置 */
#ifdef __NVIC_PRIO_BITS
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4
#endif
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15 /* 中断最低优先级 */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 /* FreeRTOS可管理的最高中断优先级 */
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_API_CALL_INTERRUPT_PRIORITY configMAX_SYSCALL_INTERRUPT_PRIORITY
/* FreeRTOS中断服务函数相关定义 */
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
/* 断言 */
#define vAssertCalled(char, int) printf("Error: %s, %d\r\n", char, int)
#define configASSERT( x ) if( ( x ) == 0 ) vAssertCalled( __FILE__, __LINE__ )
/* FreeRTOS MPU 特殊定义 */
//#define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0
//#define configTOTAL_MPU_REGIONS 8
//#define configTEX_S_C_B_FLASH 0x07UL
//#define configTEX_S_C_B_SRAM 0x07UL
//#define configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY 1
//#define configALLOW_UNPRIVILEGED_CRITICAL_SECTIONS 1
/* ARMv8-M 安全侧端口相关定义。 */
//#define secureconfigMAX_SECURE_CONTEXTS 5
#endif /* FREERTOS_CONFIG_H */
#define configSUPPORT_STATIC_ALLOCATION 1 /* 1: 支持静态申请内存, 默认: 0 */
3.2实现两个接口函数
此时编译程序发现会出现两个报错。vApplicationGetIdleTaskMemory()和vApplicationGetTimerTaskMemory()函数未创建。先在freertos_demo.c中创建一下空闲任务和定时器任务。空闲任务和软件定时器任务栈大小都在FreeRTOSConfig.h中定义。定时器任务是可选的,在FreeRTOSConfig.h中选择是否使能宏configUSE_TIMERS,默认使能。
/*空闲任务 配置*/
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;
}
3.3定义函数入口参数
静态创建任务API函数除了最后两个参数需要重新创建外,其他参数和动态创建任务函数相同,并且返回任务句柄,任务句柄非空即为创建成功。
/* 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 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();
}
同理,创建task1、task2、task3。完整的freertos_demo代码如下:
#include "freertos_demo.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.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 4
#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(NULL);
taskEXIT_CRITICAL(); /*退出临界区*/
}
/* 任务一,实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{
while(1)
{
printf("task1正在运行!\r\n");
LED0=~LED0;
vTaskDelay(500);
}
}
/* 任务二,实现LED1每1000ms翻转一次 */
void task2( void * pvParameters )
{
while(1)
{
printf("task2正在运行!\r\n");
LED1=~LED1;
vTaskDelay(1000);
}
}
/* 任务三,判断按键KEY0,按下KEY0删除task1 */
void task3( void * pvParameters )
{
uint8_t key = 0;
while(1)
{
printf("task3正在运行!\r\n");
key = KEY_Scan(0);
if(key == KEY0_PRES)
{
if(task2_handler!=NULL){
printf("删除task2!\r\n");
vTaskDelete(task2_handler);
task2_handler=NULL;
}
}
vTaskDelay(10);
}
}
下载到开发板中,现象和动态创建任务一致。
4.总结
1、在实际的应用中,动态方式创建任务是比较常用的,除非有特殊的需求,一般都会使用动态方式创建任务;
2、动态创建相对简单,更为常用;
3、静态创建:可将任务堆栈放置在特定的内存位置,并且无需关心对内存分配失败的处理;
4、临界区保护,保护那些不想被打断的程序段,关闭freertos所管理的中断,中断无法打断,滴答中断和PendSV中断无法进行不能实现任务调度。