静态创建任务函数
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 | 用户没有提供相应的内存,任务创建失败 |
其他值 | 任务句柄,任务创建成功 |
静态创建内部剖析
静态创建任务使用流程
我们静态创建任务首先要进行宏configSUPPORT_STATIC_ALLOCATION置位,这个宏在FreeRTOSConfig.h中
在开启调度函数中如果我们置位了configSUPPORT_STATIC_ALLOCATION
则我们需要定义下面两个函数为空闲任务和软件定时器任务分配内存,因为静态创建任务需要用户为创建的任务手动分配内存
两个接口函数的实现:
需要定义任务控制块、任务栈、以及任务栈大小,将形参代入
如果我们要静态创建任务,可以到我们的task.h找静态创建的代码
静态创建和动态创建的区别:
前面五个参数一致,动态创建的第六个参数是定义的句柄,用来控制和管理任务,而静态创建的第五个和第六个参数分别是任务栈的空间(用一个数组来表示)和任务控制块指针,静态创建的任务句柄是静态创建函数的返回值。
开始任务的创建:
宏定义任务参数以及声明任务函数
在开始任务创建task1、task2、task3:
编写任务函数
静态创建任务的任务栈以及任务控制块都是需要我们手动去分配内存的,比较麻烦,所以我们还是比较常用动态创建任务。
静态创建任务其内部实现
右键静态创建任务函数的任务控制块类型,然后go to一下跳转到定义,就可以看到静态创建任务的TCB结构体定义,操作如下:
与我们动态创建任务的TCB结构体是对应的,如下:
(1)进入静态创建任务函数
与动态创建任务不同,静态创建需要判断我们是否为该任务分配了空间,如果没有分配直接返回NULL,如果分配了就在调用prvInitialiseNewTask时将定义的任务返回值作为实参传递,最终生成我们的任务句柄(指向任务控制块的指针),作为静态创建任务的返回值。
定义一个新的TCB结构体,然后调用两个API函数prvInitialiseNewTask和 prvAddNewTaskToReadyList
分别进行新创建函数的初始化和添加到就绪列表中
剖析prvInitialiseNewTask
在创建任务函数里,给定义的TCB结构体分配空间,TCB结构体的第一个成员就是栈顶,然后调用 prvInitialiseNewTask,将任务创建传过来的各个参数赋值到TCB结构体的成员中,调用pxPortInitialiseStack函数初始化任务栈,将栈顶地址、任务函数指针、任务参数传递给任务栈初始化函数,将初始化好的任务栈首地址返回给栈顶,存储在TCB结构体的首成员中。
然后就是任务栈的初始化,静态创建和动态创建的初始化任务栈是一样的,都是向下生长
初始化任务栈剖析
剖析prvAddNewTaskToReadyList
删除任务流程
1)当传入的参数为NULL,则代表删除任务本身(当前正在运行的任务)
2)空闲任务会负责释放被删除任务中由系统分配的内存,但是由用户在任务删除前申请的内存,则需要由用户在任务被删除前提前释放,否则将导致内存泄漏
注意:
1、在实际的应用中,动态创建任务是比较常用的,除非有特殊的要求,一般采用动态创建任务
2、动态创建相对简单,更为常用
3、静态创建:可将任务堆栈放置在特定的内存位置,并且无需关心对内存分配失败的处理
4、临界区保护,保护那些不想被我们打断的程序段,关闭freertos所管理的中断,中断无法打开,滴答中断和,Pandsv中断无法进行,不能实现任务调度(任务调度就是在PandSV中断中进行)