注意:任务优先级与中断优先级不一样
- 任务优先级:数值越大,越先被执行
- 中断优先级:数值越小,越先被执行
一、动态创建任务
1.1.实验设计
创建四个任务,任务名字分别是 start_task、task1、task2、task3:
- start_task:用来创建其他三个任务
- task1:实现 LED0 每500ms闪烁一次
- task2:实现 LED1 每500ms闪烁一次
- task3:判断按键 KEY0 是否按下,按下则删除 task1
1.2.软件设计
以下代码设计在 freertos_demo.c 文件里实现:
- 首先在 FreeRTOSConfig.h 文件,将宏
configSUPPORT_DYNAMIC_ALLOCATION
配置为1,支持动态申请内存 - 定义入口函数参数,并在上面声明该函数、进行堆栈大小和任务优先级的宏定义,记得在主函数里面调用
freertos_demo( );
,以下代码实现:
void freertos_demo(void)
{
xTaskCreate((TaskFunction_t) start_task,
(char*) "start_task",
(uint16_t) START_TASK_STACK_SIZE,
(void*) NULL,
(UBaseType_t) START_TASK_PRIO,
(TaskHandle_t*) &start_task_handler);
vTaskStartScheduler(); //开启任务调度器
}
- 声明每个任务的配置包括:任务句柄、任务优先级、堆栈大小、创建任务
//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);
- 编写任务函数
//编写start_task的任务函数,它的里面又定义task1、task2、task3
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区,为什么要进入临界区
//因为在定义入口函数参数的时候开启了任务调度器,当任务调度器开启的时候,任务立刻创建,立刻与第一个任务进行优先级比较,优先级的作用在开始的时候就发挥不出来,所以需要进去临界区,等待全部任务创建完成,就进行调度,优先级高的先进性
xTaskCreate((TaskFunction_t) task1,
(char*) "task1",
(uint16_t) TASK1_STACK_SIZE,
(void*) NULL,
(UBaseType_t) TASK1_PRIO,
(TaskHandle_t*) &task1_handler);
xTaskCreate((TaskFunction_t) task2,
(char*) "task2",
(uint16_t) TASK2_STACK_SIZE,
(void*) NULL,
(UBaseType_t) TASK2_PRIO,
(TaskHandle_t*) &task2_handler);
xTaskCreate((TaskFunction_t) task3,
(char*) "task3",
(uint16_t) TASK3_STACK_SIZE,
(void*) NULL,
(UBaseType_t) TASK3_PRIO,
(TaskHandle_t*) &task3_handler);
vTaskDelete(start_task_handler);//删除start_task任务,释放空间
taskEXIT_CRITICAL(); //退出临界区
}
编写 task1、task2、task3 代码:
void task1(void *pvParameters)
{
while(1)
{
printf("task1正在运行");
LED0_TOGGLE();
vTaskDelay(500);
}
}
void task2(void *pvParameters)
{
while(1)
{
printf("task2正在运行");
LED1_TOGGLE();
vTaskDelay(500);
}
}
void task3(void *pvParameters)
{
uint8_t key = 0;
while(1)
{
printf("task3正在运行");
key = key_scan(0);
if(key == KEY0)
{
vTaskDelete(task1_handler);
printf("删除成功");
task1_handler = NULL; //避免空指针的出现
}
vTaskDelay(10);
}
}
第3个点设置了各个任务的优先级,其中 task3 的优先级最大,按理来说最先运行,task1 的优先级较为小,在三个任务中最后运行,可通过串口助手证明 task1 反而最先运行;这是因为在入口函数里面调用了任务调度函数vTaskStartScheduler( );
,start_task
函数首先创建 task1 ,所以就先运行 task1,按这样的运行逻辑,task3 成为最后一个运行的任务;为了解决这个问题,就要在start_task
函数里调用进入临界区taskENTER_CRITICAL( );
和退出临界区taskEXIT_CRITICAL( );
,等待所有任务创建完成,再进行优先级排序,这样操作就可以体现优先级的作用了。
二、静态创建任务
2.2.实验设计
静态创建任务的实验设计与动态创建任务一样,task1、task2、task3 这三个任务内容不变,编写空闲任务内存分配、编写软件定时器内存分配、修改入口函数、start_task
函数、各个任务的声明和宏定义即可。
2.3.软件设计
- 首先在 FreeRTOSConfig.h 文件,将宏
configSUPPORT_STATIC_ALLOCATION
配置为1,支持静态申请内存 - 定义空闲任务
vApplicationGetdleTaskMemory( );
(这个是必须的,不要让CUP停下来)、定时器任务的任务堆栈vApplicationGetTimerTaskMemory( );
及TCB(软件定时器的宏定义默认是为1的,如果为0就不需要为定时器分配内存)
//空闲任务配置
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;
}
- 定义函数入口参数
//返回一个句柄
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();
}
编写 task1、task2、task3 任务函数,与动态创建任务的内容一样。以上就是动态创建和静态创建任务的使用方法,还有删除任务的方法。