一、任务创建及删除介绍
- 任务的创建和删除本质就是调用FreeRTOS的API函数(指可供用户调用的函数,用于管理和控制操作系统的各种功能)
- 下面是任务创建和删除的API函数和描述
xTaskCreat();
xTaskCreateStatic();
vTaskDelete();
1.1 动态创建任务
- 动态创建任务是指任务的控制块以及栈空间所需的内存,均由FreeRTOS自动从FreeRTOS管理的堆中分配
BaseType_t xTaskCreate
(
TaskFunction_t pxTaskCode,
const char *const pcName,
const configSTACK_DEPTH_TYPE usStackDepth,
void *const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *const pxCreatedTask
)
- 实现动态创建任务流程:将宏configSUPPORT_DYNAMIC_ALLOCATION配置成1 — 定义6个函数入口参数 — 编写任务函数
- 此函数创建的任务会立刻进入就绪态,由任务调度器运行
1.2 静态创建任务
- 静态创建任务是指任务的控制块以及栈空间所需的内存,需要用户分配提供
TaskHandle_t xTaskCreateStatic
(
TaskFunction_t pxTaskCode,
const char *const pcName,
const uint32_t usStackDepth,
void *const pvParameters,
UBaseType_t uxPriority,
StackType_t *const puxStackBuffer,
StackTask_t *const pxTaskBuffer
)
- 实现静态创建任务流程:将宏configSUPPORT_STATIC_ALLOCATION配置成1 — 定义空闲任务&定时器任务的任务堆栈及TCB — 实现两个接口函数(Getldle和GetTimer) — 定义函数入口参数 — 编写任务函数
- 此函数创建的任务会立刻进入就绪态,由任务调度器运行
1.3 任务删除
- 当删除任务函数的传入的参数为NULL时,表示删除任务自身(当前正在运行的任务)
- 空闲任务负责释放被删除任务中由系统分配(动态任务创建)的内存
- 由用户自己申请的内存(静态任务创建),则由用户在任务被删除前提前释放,否则会导致内存泄露
void vTaskDelete(TaskHandle_t xTaskToDelete);
- 实现任务删除流程:将宏INCLUDE_vTaskDelete配置成1 — 入口参数输入需要删除的任务句柄(NULL代表删除任务本身)
- 此函数创建的任务会立刻进入就绪态,由任务调度器运行
二、实战编程
2.1 任务创建和删除(动态方法)
- 创建四个任务,Start_task(用来创建其他三个任务)、task1(LED0每500ms闪烁一次)、task2(LED1每500ms闪烁一次)、task3(判断按键按下,按下则删除task1)
- 此处Start_task只需要执行一次就要删除,否则动态创建任务,会导致自动分配堆栈不够
- 动态创建任务步骤:宏configSUPPORT_DYNAMIC_ALLOCATION配置成1 — 定义6个函数入口参数 — 编写任务函数
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#define START_TASK_STACK_SIZE 128
#define START_TASK_PRIO 1
TaskHandle_t start_task_handle;
#define TASK1_STACK_SIZE 128
#define TASK1_PRIO 2
TaskHandle_t task1_handle;
#define TASK2_STACK_SIZE 128
#define TASK2_PRIO 3
TaskHandle_t task2_handle;
#define TASK3_STACK_SIZE 128
#define TASK3_PRIO 4
TaskHandle_t task3_handle;
void task1( void * pvParameters )
{
while(1)
{
LED0_Turn();
vTaskDelay(500);
}
}
void task2( void * pvParameters )
{
while(1)
{
LED0_Turn();
vTaskDelay(500);
}
}
void task3( void * pvParameters )
{
uint8_t KeyNum = 0;
while(1)
{
KeyNum = Key_GetNum();
if(KeyNum == 1)
{
vTaskDelete(task1_handle);
}
vTaskDelay(10);
}
}
void Start_task( void * pvParameters )
{
xTaskCreate(task1,
"task1",
TASK1_STACK_SIZE,
NULL,
TASK1_PRIO,
&task1_handle
);
xTaskCreate(task2,
"task2",
TASK2_STACK_SIZE,
NULL,
TASK2_PRIO,
&task2_handle
);
xTaskCreate(task3,
"task3",
TASK3_STACK_SIZE,
NULL,
TASK3_PRIO,
&task3_handle
);
vTaskDelete(start_task_handle);
}
void freertos_demo()
{
xTaskCreate(Start_task,
"Start_task",
START_TASK_STACK_SIZE,
NULL,
START_TASK_PRIO,
&start_task_handle
);
vTaskStartScheduler();
}
- 主函数main.c就不需要while循环函数了,只需要初始化外设、调用FreeRTOS入口函数即可
int main()
{
freertos_demo();
}
- 进入入口函数,一创建Start_task任务,任务调度器就开启,然后才开始执行Start_task任务
- 按顺序首先创建task1,task1优先级>Start_task,在最开始运行task1,阻塞500ms将cpu交给其他任务执行。然后创建task2,task2优先级>task1,运行task2,阻塞500ms释放,最后创建task3,优先级最高,运行task3,阻塞10ms,当全部阻塞时,删除Start_task任务
- 如果想首先运行优先级最高的task3,可以在创建task123前,进入临界区,即关闭中断,还必须退出临界区后才会开始任务调度
- 加上临界区时,整个任务流程应该是task3 task2 task1 task3…task3…task2 …task3…task3…task3… task1…
- 运行流程以task3居多,由于task3只阻塞10ms,当task3阻塞时,task2运行,task3和task2阻塞都阻塞时,task1运行
void Start_task( void * pvParameters )
{
taskENTER_CRITICAL();
xTaskCreate(task1,
"task1",
TASK1_STACK_SIZE,
NULL,
TASK1_PRIO,
&task1_handle
);
xTaskCreate(task2,
"task2",
TASK2_STACK_SIZE,
NULL,
TASK2_PRIO,
&task2_handle
);
xTaskCreate(task3,
"task3",
TASK3_STACK_SIZE,
NULL,
TASK3_PRIO,
&task3_handle
);
vTaskDelete(start_task_handle);
taskEXIT_CRITICAL();
}
2.2 任务创建和删除(静态方法)
- 创建四个任务,Start_task(用来创建其他三个任务)、task1(LED0每500ms闪烁一次)、task2(LED1每500ms闪烁一次)、task3(判断按键按下,按下则删除task1)
- 实现静态创建任务流程:宏configSUPPORT_STATIC_ALLOCATION配置成1 — 定义空闲任务和定时器任务的任务堆栈及TCB(Getldle空闲任务和GetTimer定时器任务) — 定义函数入口参数 — 编写任务函数
#define configSUPPORT_STATIC_ALLOCATION 1
#define START_TASK_STACK_SIZE 128
#define START_TASK_PRIO 1
StackType_t start_task_stack[START_TASK_STACK_SIZE]
StackTask_t start_task_tcb;
TaskHandle_t start_task_handle;
#define TASK1_STACK_SIZE 128
#define TASK1_PRIO 2
StackType_t task1_stack[TASK1_STACK_SIZE]
StackTask_t task1_tcb;
TaskHandle_t task1_handle;
#define TASK2_STACK_SIZE 128
#define TASK2_PRIO 3
StackType_t task2_stack[TASK2_STACK_SIZE]
StackTask_t task2_tcb;
TaskHandle_t task2_handle;
#define TASK3_STACK_SIZE 128
#define TASK3_PRIO 4
StackType_t task3_stack[TASK3_STACK_SIZE]
StackTask_t task3_tcb;
TaskHandle_t task3_handle;
void task1( void * pvParameters )
{
while(1)
{
LED0_Turn();
vTaskDelay(500);
}
}
void task2( void * pvParameters )
{
while(1)
{
LED0_Turn();
vTaskDelay(500);
}
}
void task3( void * pvParameters )
{
uint8_t KeyNum = 0;
while(1)
{
KeyNum = Key_GetNum();
if(KeyNum == 1)
{
vTaskDelete(task1_handle);
}
vTaskDelay(10);
}
}
void Start_task( void * pvParameters )
{
taskENTER_CRITICAL();
task1_handle = xTaskCreateStatic(task1,
"task1",
TASK1_STACK_SIZE,
NULL,
TASK1_PRIO,
task1_stack,
&task1_tcb
);
task2_handle = xTaskCreateStatic(task2,
"task2",
TASK2_STACK_SIZE,
NULL,
TASK2_PRIO,
task2_stack,
&task2_tcb
);
task3_handle = xTaskCreateStatic(task3,
"task3",
TASK3_STACK_SIZE,
NULL,
TASK3_PRIO,
task3_stack,
&task3_tcb
);
vTaskDelete(start_task_handle);
taskEXIT_CRITICAL();
}
#define configMINIMAL_STACK_SIZE 128
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;
}
#define configUSE_TIMERS 1
#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE*2)
StaticTask_t timer_task_tcb;
StackType_t timer_task_stack[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;
}
void freertos_demo()
{
start_task_handle = xTaskCreateStatic(start_task,
"start_task",
START_TASK_STACK_SIZE,
NULL,
START_TASK_PRIO,
start_task_stack,
&start_task_tcb
)
vTaskStartScheduler();
}
- 动态任务创建不需要返回句柄,在创建时已经定义句柄
- 静态任务创建和动态任务创建不同之处:空闲任务分配内存并配置TCB。定时器任务分配内存并配置TCB。返回任务句柄。任务堆栈和TCB需要手动配置