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、总结:
静态创建的流程:

  1. 修改配置。
  2. 引入#include "FreeRTOS.h"#include "task.h"
  3. 空闲任务与定时器任务堆栈函数的实现。
    • 静态任务中需要实现vApplicationGetIdleTaskMemory()vApplicationGetTimerTaskMemory(),这两个函数是用户设定的空闲(Idle)任务与定时器(Timer)任务的堆栈大小,必须由用户自己分配,而不能是动态分配。
    • 先定义好空闲、定时器任务栈和空闲、定时器任务控制块,再实现函数这两个函数。
  4. 定义任务:句柄、堆栈、控制块、任务。
  5. 静态创建任务与启动。
  6. 静态创建方式需要自己去定义一大堆的东西。

代码全貌,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
如果有理解错的地方、写错的地方,欢迎━(*`∀´*)ノ亻!指出。

  • 0
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值