目录
一. 任务控制块结构成员介绍
认识: 任务控制块是一个结构体,结构体存在很多的成员变量,就好比任务的身份证,保留了任务的一些特征。
typedef struct tskTaskControlBlock
{
volatile StackType_t * pxTopOfStack;
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 ]:
任务名字。
二. 任务的删除
2.1 任务删除函数
void vTaskDelete(TaskHandle_t xTaskToDelete);
参数介绍
xTaskToDelete:待删除任务的任务句柄。
比如给我们创建了一个任务,任务是具有任务句柄的,删除任务就需要将任务句柄传入此函数就可以删除任务了。
注:
① 此函数用于删除已经被创建成功的任务,如果没有被创建,是不能被删除的。
② 被删除的任务将从就绪态任务列表、阻塞态任务列表、 挂起态任务列表和事件列表中移除(不管任务处于何种状态,只要调用删除函数vTaskDelete(),传入需要删除的任务的句柄,那么都会被删除)。
③ 当传入的参数为NULL,则代表删除任务自身(当前正在运行的任务)。
④ 区别:
1. 针对动态创建:空闲任务会负责释放被删除任务中被系统分配的内存(删除自身任务,即函数xTaskDelete()传入NULL时,才会利用空闲任务释放掉被删除任务使用的系统分配的内存,如果是在task1任务中删除task2,则在task1中释放task2所使用的内存空间。)
2. 针对静态创建:由用户在任务删除前申请的内存,则需要用户在任务被删除前提前释放,否则将导致内存泄露。
2.2 删除任务流程
① 使用删除任务函数,需将宏INCLUDE_vTaskDelete配置为1。
② 入口参数输入需要删除的任务句柄(NULL代表删除本身)。
2.3 删除任务内部实现流程
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。
三. 动态创建任务
认识:任务的任务控制块以及任务的栈空间所需的内存,均由FreeRTOS从FreeRTOS管理的堆中分配,不需要人为操作。
3.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:
任务句柄。删除任务是通过任务句柄进行操作,任务句柄其实就是任务的任务控制块。在函数内部任务控制块等效于任务句柄。
函数返回值 描述 pdPASS 任务创建成功 errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY 任务创建失败(其中MEMORY,主要是指任务堆栈过大,动态创建是在FreeRTOS管理的堆空间中创建,如果超出了堆空间大小,则会创建失败)
3.2 动态创建任务流程
① 将宏configSUPPORT_DYNAMIC_ALLOCATION 配置为 1;
② 定义函数的入口参数(可以提前定义,任务创建时代入);
③ 编写任务函数(任务需要实现的功能,在任务函数中完成);
注: 使用xTaskCreat()函数创建的任务会立即进入就绪态,由任务调度器调度运行。比如说创建了很多任务,这些任务创建后会立刻进入就绪态,由任务调度器在就绪态中找到任务优先级最高的去执行。
3.3 动态创建任务的内部实现方式(简易描述)
① 申请任务堆栈和任务控制块的内存(只需要定义所申请的大小,之后由FreeRTOS自动地去申请);
② TCB结构体成员赋值(TCB,任务控制块。类似于人的身份证,每个任务都有自己的任务控制块,任务控制块存取任务的一些特征,比如任务名、优先级、状态等);
③ 添加新任务到就绪列表中(任务进入到就绪态)。
3.4 示例代码
/*START任务的基本配置*/
#define START_STK_SIZE 128
#define START_TASK_PRIO 1
TaskHandle_t start_task_handler;
void StartTask_fun(void);
void Demo()
{
xTaskCreate((TaskFunction_t ) StartTask_fun, //任务函数
(char * ) "StartTask_fun", //任务名字
(uint16_t ) START_STK_SIZE, //堆栈大小
(void * ) NULL, //任务参数
(UBaseType_t ) START_TASK_PRIO, //任务优先级
(TaskHandle_t * ) &start_task_handler);//任务句柄
vTaskStartScheduler(); //开启任务调度
}
/*任务函数*/
void StartTask_fun()
{
while(1)
{
printf("hello freertos\r\n");
}
}
四. 静态创建任务
认识:任务的任务控制块以及任务的栈空间所需的内存,需要用户分配提供。(人为地定义内存,比如说定义一个数组当作任务栈空间)
4.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 /* 任务控制块指针,由用户分配 */
);
参数介绍
其他参数与动态创建任务的类似。有区别的为以下两个:
puxStackBuffer:
任务堆栈。一般为数组,由用户自己定义(这里和动态创建有区别,动态创建只需要指定任务堆栈大小ulStackDepth,剩下的是由FreeRTOS根据任务大小,自动去申请堆大小,而静态需要给定数组地址)。
pxTaskBuffer:
任务控制块指针。指向任务控制块的地址,任务控制块保留了很多信息,信息需要内存来保存,这部分内存也需要用户自己分配。
函数返回值
描述 NULL 用户没有提供相应的内存,任务创建失败。(比如说任务堆栈puxStackBuffer参数并没有指定,那么就会创建失败) 其他值 任务句柄,任务创建成功
4.2 静态创建任务流程
注:静态创建任务函数xTaskCreatStatic()创建的任务会立刻进入就绪态,由任务调度器进行调度。
① 需将宏configSUPPORT_STATIC_ALLOCATION配置为1;
② 定义空闲任务和定时器任务的任务堆栈及TCB
注:
空闲任务是得必须有,在RTOS中CPU是不能停止的,在空闲时需要执行空闲任务。
定时器任务是可选的,当使能了软件定时器功能,这个任务就需要被创建,如果没有被使能,就不需要创建软件定时器任务,
这两个任务都是静态创建,用户都需要分配任务堆栈和任务控制块
③ 实现两个接口函数
第一个是空闲任务的内存赋值vApplicationGetldleTaskMemory();
第二个是定时器的内存赋值vApplicationGetTimerTaskMemory();
由于在第2步任务堆栈、任务控制块需要赋值给结构体成员,就通过这两个函数来实现。
④ 定义函数入口参数
⑤ 编写任务函数
4.3静态创建任务的内部实现方式(简易描述)
① TCB结构体成员赋值(将任务的一些特征赋值给任务控制块TCB)
② 添加新任务到就绪列表中
4.4 示例代码
/*定义空闲任务(必须)*/
StaticTask_t idle_task_tcb;
StackType_t idle_task_stack[configMINIMAL_STACK_SIZE];
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer = &idle_task_tcb;
*ppxIdleTaskStackBuffer = idle_task_stack;
*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
/*定义定时器任务(可选)*/
StaticTask_t timer_task_tcb;
StackType_t timer_task_stack[configTIMER_TASK_STACK_DEPTH];
void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer,
uint32_t *pulTimerTaskStackSize )
{
*ppxTimerTaskTCBBuffer = &timer_task_tcb;
*ppxTimerTaskStackBuffer = timer_task_stack;
*pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}
/*START任务的基本配置*/
#define START_STK_SIZE 128
#define START_TASK_PRIO 1
TaskHandle_t start_task_handler;
StackType_t start_task_stack[START_STK_SIZE];
StaticTask_t start_task_tcb;
void StartTask_fun(void);
void Demo()
{
start_task_handler = xTaskCreateStatic( (TaskFunction_t ) StartTask_fun,
(char * ) "StartTask_fun",
(uint32_t ) START_STK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIO,
(StackType_t * ) start_task_stack,
(StaticTask_t * ) &start_task_tcb );
vTaskStartScheduler(); //开启任务调度
}
void StartTask_fun()
{
while(1)
{
printf("hello freertos\r\n");
}
}