FreeRTOS任务特性
1 任务特性
2 任务状态(4种)
3 任务优先级
3 任务实现
在使用 FreeRTOS 的过程中,我们要使用函数 xTaskCreate()或 xTaskCreateStatic()来创建任务,需要创建任务函数。什么是任务函数?任务函数就是完成本任务工作的函数。我这个任务要干嘛?要做什么?要完成什么样的功能都是在这个任务函数中实现的。
void vATaskFunction(void *pvParameters)
{
for (;;)
{
/* 任务应用程序 */
vTaskDelay();
}
/*不能从任务函数中返回或退出,除非调用函数 vTaskDelete(NULL)删除任务。*/
vTaskDelete(NULL);
}
4 任务堆栈
堆栈大小:
可以看出 StackType_t 类型的变量为 4 个字节,那么任务的实际堆栈大小就应该是我们所定义的 4 倍。
函数实现:
任务创建和删除
1 任务创建 API 函数
动态创建任务函数 xTaxkCreate()
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint16_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
此函数用来创建一个任务,任务需要 RAM 来保存与任务有关的状态信息(任务控制块),任务也要一定的 RAM 来作为任务堆栈。如果使用函数 xTaskCreate()来创建任务,那么这些所需的 RAM 就会自动的从 FreeRTOS 的堆中分配,因此必须提供内存管理文件,默认我们使用heap_4.c 这个内存管理文件,而且宏 configSUPPORT_DYNAMIC_ALLOCATION 必须为 1。如果使用函数 xTaskCreateStatic()创建的话这些 RAM 就需要用户来提供。新创建的任务默认就是就绪态的,如果当前没有比它更高优先级的任务运行那么此任务就会立即进入运行态开始运行,不管在任务调度器启动前还是启动后,都可以创建任务。
在 FreeRTOS 源码中(task.h),给出了函数使用方法:
参数说明:
静态创建任务函数 xTaskCreateStatic()
#if (configSUPPORT_STATIC_ALLOCATION == 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)
创建静态任务要复杂一些(要手动定义任务栈数组),如下所示:
删除任务函数 vTaskDelete()
删除任务函数的注意事项:
实验1 动态创建和删除任务
目标:实现两个任务,任务1运行5次后删除任务2
程序主要部分如下所示:
void start_task(void *pvParameters);
void task1_task(void *pvParameters);
void task2_task(void *pvParameters);
#define START_TASK_SIZE 120
#define START_TASK_PRIO 1
TaskHandle_t Start_Task_Handle;
#define TASK1_TASK_SIZE 120
#define TASK1_TASK_PRIO 2
TaskHandle_t Task1_Handle;
#define TASK2_TASK_SIZE 120
#define TASK2_TASK_PRIO 3
TaskHandle_t Task2_Handle;
// 创建开始任务
xTaskCreate((TaskFunction_t ) start_task,
(char * ) "start_task",
(uint16_t ) START_TASK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIO,
(TaskHandle_t * ) &Start_Task_Handle);
vTaskStartScheduler(); // 开启调度器
void start_task(void *pvParameters)
{
// 创建任务1
xTaskCreate((TaskFunction_t ) task1_task,
(char * ) "task1_task",
(uint16_t ) TASK1_TASK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_TASK_PRIO,
(TaskHandle_t * ) &Task1_Handle);
// 创建任务2
xTaskCreate((TaskFunction_t )task2_task,
(char * )"task2_task",
(uint16_t )TASK2_TASK_SIZE,
(void * )NULL,
(UBaseType_t )TASK2_TASK_PRIO,
(TaskHandle_t * )&Task2_Handle);
// 删除开始任务
vTaskDelete(Start_Task_Handle);
}
void task1_task(void *pvParameters)
{
uint16_t cnt = 0;
for (;;)
{
cnt++;
LED_G_TOGGLE;
printf("Task1 running %d\r\n", cnt);
// 如果任务1执行5次,则删除任务2
if (cnt == 5)
{
printf("Delete task2!!!\r\n");
vTaskDelete(Task2_Handle);
}
vTaskDelay(500);
}
}
void task2_task(void *pvParameters)
{
uint16_t cnt = 0;
for (;;)
{
cnt++;
printf("Task2 running %d\r\n", cnt);
vTaskDelay(500);
}
}
程序执行结果如下:
静态创建任务
1 使用静态方法创建任务的时候需要将宏configSUPPORT_STATIC_ALLOCATION设置为1,在文件 FreeRTOSConfig.h 中设置,如下所示:
然后编译,有一个提示错误:
查看源码 task.c ,这个函数在外部声明,task.c 中引入并调用
2 实现这个 GetIdleTaskMemory() 函数,如下所示:
3 创建开始任务框架;
#define START_TASK_PRIO 1 // 开始任务优先级
#define START_TASK_SIZE 128 // 开始任务堆栈大小
StackType_t Start_Task_Stack[START_TASK_SIZE]; // 开始任务堆栈
StaticTask_t StartTaskTCB; // 开始任务控制块
TaskHandle_t Start_Task_Handle; // 开始任务句柄
void start_task(void *pvParameters); // 开始任务函数
// 创建开始任务
Start_Task_Handle = xTaskCreateStatic((TaskFunction_t ) start_task,
(char * ) "start_task",
(uint32_t ) START_TASK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIO,
(StackType_t * ) Start_Task_Stack,
(StaticTask_t * ) &StartTaskTCB);
void start_task(void *pvParameters)
{
for (;;)
{
}
}
3 创建2个任务,要求任务1执行5次后删除任务2,任务创建过程和开始任务类似。
// 任务1
#define TASK1_TASK_PRIO 2
#define TASK1_TASK_SIZE 128
StackType_t Task1_Stack[TASK1_TASK_SIZE];
StaticTask_t Task1TaskTCB;
TaskHandle_t Task1_Handle;
void task1_task(void *pvParameters);
// 任务2
#define TASK2_TASK_PRIO 3
#define TASK2_TASK_SIZE 128
StackType_t Task2_Stack[TASK2_TASK_SIZE];
StaticTask_t Task2TaskTCB;
TaskHandle_t Task2_Handle;
void task2_task(void *pvParameters);
void start_task(void *pvParameters)
{
// 创建任务1
Task1_Handle = xTaskCreateStatic((TaskFunction_t ) task1_task,
(char * ) "task1_task",
(uint32_t ) TASK1_TASK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_TASK_PRIO,
(StackType_t * ) Task1_Stack,
(StaticTask_t * ) &Task1TaskTCB);
// 创建任务2
Task2_Handle = xTaskCreateStatic((TaskFunction_t ) task2_task,
(char * ) "task2_task",
(uint32_t ) TASK2_TASK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK2_TASK_PRIO,
(StackType_t * ) Task2_Stack,
(StaticTask_t * ) &Task2TaskTCB);
vTaskDelete(Start_Task_Handle); // 删除开始任务
}
void task1_task(void *pvParameters)
{
uint16_t count1 = 0;
for (;;)
{
count1++;
LED_B_TOGGLE;
printf("Task1 running %d\n", count1);
if (count1 == 5)
{
vTaskDelete(Task2_Handle); // 任务1执行5次,则删除任务2
printf("delete task2!!!\n");
}
vTaskDelay(1000);
}
}
void task2_task(void *pvParameters)
{
uint16_t count2 = 0;
for (;;)
{
count2++;
printf("Task2 running %d\n", count2);
vTaskDelay(1000);
}
}
执行结果如下所示:
非常注意:
一开始创建2个任务时,没有特别注意任务优先级,都设置成了1,如下所示:
此时,得到的结果很奇怪,如下:
原因很简单,同一优先级下有多个任务,任务调度器采用时间片。
任务挂起和恢复
任务挂起和恢复的作用:
设计的实验:
1 包含3个任务,其中两个打印任务,一个按键1任务;
按键1任务中用于挂起 / 恢复任务1;
按键2配置为外部中断,在中断服务函数中恢复任务1;
程序如下所示:
// 按键任务
void key_task(void *pvParameters)
{
uint8_t key_status = 0, flag = 0;
for (; ;)
{
key_status = key_scan(KEY1_GPIO_Port, KEY1_Pin);
if (key_status)
{
flag = !flag;
printf("按键1按下\r\n");
if (flag)
{
printf("按键1挂起任务1\r\n");
vTaskSuspend(Task1_Handle);
}
else
{
printf("按键1恢复任务1\r\n");
vTaskResume(Task1_Handle);
}
}
vTaskDelay(10);
}
}
// 中断服务函数
void EXTI15_10_IRQHandler(void)
{
BaseType_t YieldRequired;
if (__HAL_GPIO_EXTI_GET_IT(KEY2_Pin) != 0x00u)
{
YieldRequired = xTaskResumeFromISR(Task1_Handle); // 恢复任务1
printf("按键2外部中断 -- 中断服务函数中恢复任务1\r\n");
if (YieldRequired == pdTRUE)
{
printf("要恢复的任务优先级 > 当前任务优先级,执行上下文切换\r\n");
portYIELD_FROM_ISR(YieldRequired); // 在中断服务函数中,进行任务切换
}
__HAL_GPIO_EXTI_CLEAR_IT(KEY2_Pin);
}
}
实验结果如下所示: