FreeRTOS学习篇二:任务创建与操作流程
1.了解一些基本概念
1、任务:可以将任务看作是一个线程,多个任务就是多个线程,任务之间都是互相独立的,FreeRTOS 中,任务是竞争系统资源的最小运行单位。(在FreeRTOS中,线程(Thread)和任务(Task)的概念是相同的(有的系统叫任务,有的叫线程)。每个任务就是一个线程,有着自己的一个程序执行路径)
2、任务控制块:本质就是一个结构体,这个结构体用于存储着任务的一些信息,比如任务的栈指针、任务名称、任务的形参等。系统对任务的全部操作都可以通过这个任务控制块(结构体)来实现。
3、任务句柄:本质就是一个指向任务控制块的指针,通过指针来对任务控制块进行访问,通常句柄为void*
型指针。
4、内核对象句柄:信号量、消息队列、事件标志组、软件定时器等都属于内核的对象,实际就是在内核代码中定义好的一些全局数据结构,调用FreeRTOS提供的函数去创建这些东西,创建完成后会返回一个指向这些东西的指针 —— 即内核对象句柄。
2.创建任务的两种方式
2.1任务创建方式
1、单任务系统:
-
51、32位等单片机裸机中,如果未使用系统,那都是在main函数中通过
while(1)
死循环来不断依次执行各个处理程序的,对于一些突发处理事件有时会通过中断来完成。这种宏观上串行执行任务中程序的系统,称之为单任务系统,也称为前后台系统(死循环内的程序作为后台程序,而中断服务函数则作为前台程序)。 -
缺点:前后台系统各个任务(应用程序)都是得排队来轮流执行的,因此系统的实时性差,不适合于大一点的嵌入式应用。
实时性:如果有一个任务需要执行,CPU就能马上(在较短时间内)执行该任务,不会有较长的延时,这就是实时性。
实时性可以保证各个任务的及时执行。 -
优点:简单,资源消耗少。
2、多任务系统:宏观上,可以看成是并行地“同时”执行多个任务的系统。(调度器会不断地启动、停止每一个任务,宏观看上去所有的任务都是在同时执行)
3、FreeRTOS中任务的创建,有两种方式:
-
静态创建:任务控制块和任务堆栈都事先定义好,即定义好全局变量(全局的都有静态存储期),任务删除时所占内存不会被释放,实际中使用较少。
(线程占用的内存不可释放)
-
动态创建:动态创建任务控制块和任务堆栈(由FreeRTOS动态分配),任务删除时占用的内存可释放,实际应用中使用最多。
(线程占用的内存可释放)
2.2动态创建
介绍几个函数:
/* 用于创建任务 */
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char* const pcName,
const uint16_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask);
xTaskCreate(函数入口-即函数指针, 函数名称, 任务栈大小, 任务入口函数参数, 优先级, 任务句柄);
/* 用于删除任务:传入任务的句柄,将任务删除 */
void vTaskDelete( TaskHandle_t xTaskToDelete );
/* 进入临界区:调用其进入临界区,taskENTER_CRITICAL()是一个宏 */
taskENTER_CRITICAL()
/* 退出临界区:调用其退出临界区,taskEXIT_CRITICAL()是一个宏 */
taskEXIT_CRITICAL()
/* 启动调度器:调用即可启动调度器 */
void vTaskStartScheduler( void );
以点亮一个LED灯为例,展示动态创建操作的流程,动态创建是最常用的,示例代码都写在main.c里。
流程如下:
①首先就是开启动态支持,在FreeRTOSConfig.h
配置文件中添加以下:
#define configSUPPORT_DYNAMIC_ALLOCATION 1
②定义任务与任务句柄:
/* 定义任务与任务句柄:TaskHandle_t就是`void*` */
static TaskHandle_t AppTaskCreate_Handle = NULL;
static TaskHandle_t LEDTask_Handle = NULL;
static void AppTaskCreate(void* pvParameters);
static void LEDTask(void* pvParameters);
static void AppTaskCreate(void* pvParameters)
{
taskENTER_CRITICAL(); /* 进入临界区 */
BaseType_t xReturn = pdPASS;
xReturn = xTaskCreate(LEDTask,"LEDTask",512,NULL,3,LEDTask_Handle);
if(xReturn == pdPASS)
vTaskDelete(AppTaskCreate_Handle); /* 创建任务完成,任务App就已经结束使命了,删除以释放内存 */
taskEXIT_CRITICAL(); /* 退出临界区 */
}
static void LEDTask(void* pvParameters)
{
while(1){
GPIO_SetBits(GPIOC, GPIO_Pin_13);
vTaskDelay(1500);
GPIO_ResetBits(GPIOC, GPIO_Pin_13);
vTaskDelay(1500);
}
}
③任务创建与FreeRTOS的启动:
/* GPIO初始化 */
static void LED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
int main(void){
LED_Init();
BaseType_t xReturn = pdPASS;
xReturn = xTaskCreate(AppTaskCreate, "AppTaskCreate",512,NULL,2,AppTaskCreate_Handle);
if(xReturn == pdPASS)
vTaskStartScheduler();
else
return -1;
while(1);
}
综合上面两个步骤,main.c全貌如下:
#include "stm32f10x.h" // Device header
#include "FreeRTOS.h"
#include "task.h"
/* GPIO初始化 */
static void LED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
/* 定义任务与任务句柄:TaskHandle_t就是`void*` */
static TaskHandle_t AppTaskCreate_Handle = NULL;
static TaskHandle_t LEDTask_Handle = NULL;
static void AppTaskCreate(void* pvParameters);
static void LEDTask(void* pvParameters);
int main(void){
LED_Init();
BaseType_t xReturn = pdPASS;
xReturn = xTaskCreate(AppTaskCreate, "AppTaskCreate",512,NULL,2,AppTaskCreate_Handle);
if(xReturn == pdPASS)
vTaskStartScheduler();
else
return -1;
while(1);
}
static void AppTaskCreate(void* pvParameters)
{
taskENTER_CRITICAL(); /* 进入临界区 */
BaseType_t xReturn = pdPASS;
xReturn = xTaskCreate(LEDTask,"LEDTask",512,NULL,3,LEDTask_Handle);
if(xReturn == pdPASS)
vTaskDelete(AppTaskCreate_Handle); /* 创建任务完成,任务App就已经结束使命了,删除以释放内存 */
taskEXIT_CRITICAL(); /* 退出临界区 */
}
static void LEDTask(void* pvParameters)
{
while(1){
GPIO_SetBits(GPIOC, GPIO_Pin_13);
vTaskDelay(2000);
GPIO_ResetBits(GPIOC, GPIO_Pin_13);
vTaskDelay(2000);
}
}
通常并不需要操作任务本身,上面可以简化为以下流程:(main.c)
#include "stm32f10x.h" // Device header
#include "FreeRTOS.h"
#include "task.h"
/* GPIO初始化 */
static void LED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
/* 定义任务:TaskHandle_t就是`void*` */
static void LEDTask(void* pvParameters);
int main(void){
LED_Init();
BaseType_t xReturn = pdPASS;
taskENTER_CRITICAL(); /* 进入临界区 */
xReturn = xTaskCreate(LEDTask, "LEDTask",512,NULL,2,NULL); /* 创建任务 */
taskEXIT_CRITICAL(); /* 退出临界区 */
if(xReturn == pdPASS)
vTaskStartScheduler(); /* 启动调度器 */
else
return -1;
while(1);
}
static void LEDTask(void* pvParameters)
{
while(1){
GPIO_SetBits(GPIOC, GPIO_Pin_13);
vTaskDelay(500);
GPIO_ResetBits(GPIOC, GPIO_Pin_13);
vTaskDelay(500);
}
}
这就是动态创建任务的基本使用。
2.3启动与执行流程
上面动态创建任务中,展示的就是两种流程:
- 第一种流程是在一个任务中去创建其它的任务,就像是任务嵌套任务一样,当这个用于创建其它任务的任务创建完成后,就启动调度器使其运行去创建其它任务。
- 而另一种流程是创建完所有的任务,然后再启动调度器调度执行。
这两种流程看自己习惯哪种,选用一种流程使用即可。
这些任务是如何执行的呢?
如果使用的是第二种流程,那么任务创建完成后并不会立刻执行,而是调度器开启后才开始调度执行,执行时优先级高的优先执行,如果是相同优先级的任务,那就是时间片轮转调度执行,任务执行过程中必须出现释放CPU执行权的状态,否则将会把CPU给霸占了。
如果使用的是第一种流程,那么当调度器启动就开始执行任务了,执行时从AppTaskCreate任务开始执行,执行过程中:
- 使用临界区的情况下:当退出临界区,才开始调度执行那些由AppTaskCreate任务创建的任务,执行时也是高优先级的优先执行,同优先级的时间片轮转调度执行。
- 不使用临界区的情况下:
- ①:如果AppTaskCreate任务执行时创建的任务优先级比其高,那么创建的任务就会立刻执行。
- ②:如果AppTaskCreate任务执行时创建的任务优先级比其低,那AppTaskCreate继续向下执行。
- ③:如果AppTaskCreate任务执行时创建的任务优先级等于其本身的,那AppTaskCreate任务与这个同优先级的任务将按时间片轮转调度的方式执行。
2.4静态创建
以点亮一个LED灯为例,展示静态创建操作的流程,了解一下即可。
1、配置:静态创建,需要在FreeRTOSConfig.h
配置文件中设置相关宏。
/* 在`FreeRTOSConfig.h`配置文件添加以下内容: */
#define configSUPPORT_STATIC_ALLOCATION 1 /* 设置为1,即开启静态支持 */
#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE*2) /* 设置定时器服务任务的任务堆栈大小 */
2、创建任务,具体步骤:
①引入#include "FreeRTOS.h"
和#include "task.h"
。
②空闲任务与定时器任务堆栈函数的实现:
/* 空闲任务与定时器任务:先定义好堆栈和控制块,再实现函数 */
static StackType_t Idle_Task_Stack[configMINIMAL_STACK_SIZE]; /* 空闲任务任务堆栈 */
static StackType_t Timer_Task_Stack[configTIMER_TASK_STACK_DEPTH]; /* 定时器任务堆栈 */
static StaticTask_t Idle_Task_TCB; /* 空闲任务控制块 */
static StaticTask_t Timer_Task_TCB; /* 定时器任务控制块 */
/* 函数声明 */
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize);
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize) ;
/* 函数实现 */
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; /* 任务堆栈大小 */
}
③定义任务:
/* 定义任务:句柄、堆栈、控制块、任务 */
static TaskHandle_t AppTaskCreate_Handle; /* 任务句柄 */
static TaskHandle_t LED_Task_Handle;
static StackType_t AppTaskCreate_Stack[128]; /* 任务堆栈 */
static StackType_t LED_Task_Stack[128];
static StaticTask_t AppTaskCreate_TCB; /* 任务控制块 */
static StaticTask_t LED_Task_TCB;
static void LED_Task(void* parameter); /* 任务声明*/
static void AppTaskCreate(void);
/* 任务函数实现: */
static void LED_Task(void* parameter)
{
while(1){
GPIO_SetBits(GPIOC, GPIO_Pin_13);
vTaskDelay(500);
GPIO_ResetBits(GPIOC, GPIO_Pin_13);
vTaskDelay(500);
}
}
/* 任务函数实现,该任务函数用于创建任务 */
static void AppTaskCreate(void)
{
taskENTER_CRITICAL(); /* 进入临界区 */
/* 创建 LED_Task 任务 */
LED_Task_Handle = xTaskCreateStatic((TaskFunction_t)LED_Task, /*任务函数*/
(const char*)"LED_Task", /* 指定要创建的任务的名称,即任务函数名*/
(uint32_t)128, /* 任务堆栈大小*/
(void* )NULL, /* 传递给任务函数的参数*/
(UBaseType_t)4, /* 任务优先级设置*/
(StackType_t*)LED_Task_Stack, /* 任务堆栈*/
(StaticTask_t*)&LED_Task_TCB); /* 任务控制块*/
if (NULL != LED_Task_Handle) /* 创建成功 */
/* 创建完成 */;
vTaskDelete(AppTaskCreate_Handle); /*删除 AppTaskCreate 任务*/
taskEXIT_CRITICAL(); /*退出临界区*/
}
④任务创建与FreeRTOS的启动:
/* GPIO初始化 */
static void LED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
LED_Init();
/* 静态创建任务 start */
AppTaskCreate_Handle= xTaskCreateStatic((TaskFunction_t)AppTaskCreate,
(const char*)"AppTaskCreate",(uint32_t)128,(void*)NULL,(UBaseType_t)3,
(StackType_t*)AppTaskCreate_Stack,
(StaticTask_t*)&AppTaskCreate_TCB);
/* 静态创建任务 end */
/* 启动FreeRTOS,即开启任务调度 */
if (NULL != AppTaskCreate_Handle)
vTaskStartScheduler();
while(1);
}
3、总结:
静态创建的流程:
- 修改配置。
- 引入
#include "FreeRTOS.h"
和#include "task.h"
。 - 空闲任务与定时器任务堆栈函数的实现。
- 静态任务中需要实现
vApplicationGetIdleTaskMemory()
与vApplicationGetTimerTaskMemory()
,这两个函数是用户设定的空闲(Idle)任务与定时器(Timer)任务的堆栈大小,必须由用户自己分配,而不能是动态分配。 - 先定义好空闲、定时器任务栈和空闲、定时器任务控制块,再实现函数这两个函数。
- 静态任务中需要实现
- 定义任务:句柄、堆栈、控制块、任务。
- 静态创建任务与启动。
- 静态创建方式需要自己去定义一大堆的东西。
代码全貌,main.c:(实现效果,PC13口的LED闪烁)
#include "stm32f10x.h" // Device header
#include "FreeRTOS.h"
#include "task.h"
/* GPIO初始化 */
static void LED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
/* 空闲任务与定时器任务:先定义好堆栈和控制块,再实现函数 */
static StackType_t Idle_Task_Stack[configMINIMAL_STACK_SIZE]; /* 空闲任务任务堆栈 */
static StackType_t Timer_Task_Stack[configTIMER_TASK_STACK_DEPTH]; /* 定时器任务堆栈 */
static StaticTask_t Idle_Task_TCB; /* 空闲任务控制块 */
static StaticTask_t Timer_Task_TCB; /* 定时器任务控制块 */
/* 定义任务:句柄、堆栈、控制块、任务 */
static TaskHandle_t AppTaskCreate_Handle; /* 任务句柄 */
static TaskHandle_t LED_Task_Handle;
static StackType_t AppTaskCreate_Stack[128]; /* 任务堆栈 */
static StackType_t LED_Task_Stack[128];
static StaticTask_t AppTaskCreate_TCB; /* 任务控制块 */
static StaticTask_t LED_Task_TCB;
static void LED_Task(void* parameter); /* 任务声明*/
static void AppTaskCreate(void);
/* 函数声明 */
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize);
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize) ;
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
LED_Init();
/* 静态创建任务 start */
AppTaskCreate_Handle= xTaskCreateStatic((TaskFunction_t)AppTaskCreate,
(const char*)"AppTaskCreate",(uint32_t)128,(void*)NULL,(UBaseType_t)3,
(StackType_t*)AppTaskCreate_Stack,
(StaticTask_t*)&AppTaskCreate_TCB);
/* 静态创建任务 end */
/* 启动FreeRTOS,即开启任务调度 */
if (NULL != AppTaskCreate_Handle)
vTaskStartScheduler();
while(1);
}
/* 任务函数实现: */
static void LED_Task(void* parameter)
{
while(1){
GPIO_SetBits(GPIOC, GPIO_Pin_13);
vTaskDelay(500);
GPIO_ResetBits(GPIOC, GPIO_Pin_13);
vTaskDelay(500);
}
}
/* 任务函数实现,该任务函数用于创建任务 */
static void AppTaskCreate(void)
{
taskENTER_CRITICAL(); /* 进入临界区 */
/* 创建 LED_Task 任务 */
LED_Task_Handle = xTaskCreateStatic((TaskFunction_t)LED_Task, /*任务函数*/
(const char*)"LED_Task", /* 指定要创建的任务的名称,即任务函数名*/
(uint32_t)128, /* 任务堆栈大小*/
(void* )NULL, /* 传递给任务函数的参数*/
(UBaseType_t)4, /* 任务优先级设置*/
(StackType_t*)LED_Task_Stack, /* 任务堆栈*/
(StaticTask_t*)&LED_Task_TCB); /* 任务控制块*/
if (NULL != LED_Task_Handle) /* 创建成功 */
/* 创建完成 */;
vTaskDelete(AppTaskCreate_Handle); /*删除 AppTaskCreate 任务*/
taskEXIT_CRITICAL(); /*退出临界区*/
}
/* 函数实现 */
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; /* 任务堆栈大小 */
}
END
如果有理解错的地方、写错的地方,欢迎━(*`∀´*)ノ亻!
指出。