FreeRTOS的任务可以认为是一系列独立的任务的集合。
每个任务在自己的环境中运行,并且每个时刻只有一个任务在运行,但从宏观上看,所有的任务都在同时执行;
不同任务的切换与任务本身无关,这是由调度器来实现的。调度器负责在任务切入、切出时保存上下文环境(寄存器值、栈内容);
所以每个任务都有自己独立的栈空间,任务切出时,其执行环境会被保存在该任务的栈空间中,当任务再次运行时,就能从栈中恢复上次的运行环境。
任务越多,需要的栈空间就越大,一个系统能运行多少个任务,取决于系统可用的SRAM。
任务采用抢占式调度机制,高优先级的任务可打断低优先级任务,低优先级任务必须在高优先级任务阻塞后才能得到调度,
FreeRTOS也支持时间片轮转调度方式,只不过时间片的调度不允许抢占任务的CPU使用权。
任务通常运行在一个死循环中,也不会退出,如果不需要某个任务,可以调用删除任务API函数将其删除。
任务里面的延时函数必须使用 FreeRTOS 里面提供的延时函数,并不能使用我们裸机编程中的那种延时。
这两种的延时的区别:
FreeRTOS 里面的延时是阻塞延时,即调用 vTaskDelay()函数的时候,当前任务会被起,调度器会切换到其它就绪的任务,从而实现多任务。
如果还是使用裸机编程中的那种延时,那么整个任务就成为了一个死循环,如果恰好该任务的优先级是最高的,那么系统永远都是在这个任务中运行,比它优先级更低的任务无法运行,根本无法实现多任务。
FreeRTOS中提供的任务调度器是基于优先级的全抢占调度:在系统中除了中断处理函数、调度器上锁部分的代码和禁止中断的代码不可抢占之外,
系统的其他部分都是可以抢占的。
FreeRTOS内核中采用两种方法寻找最高优先级的任务,一般和特殊。
FreeRTOS内核中允许创建相同优先级的任务。相同优先级的任务采用时间片轮转方式进行调度(分时调度器),
时间片轮转调度仅在当前系统中无更高优先级就绪任务存在的情况下才有效。
任务调度的原则:一旦任务状态发生改变,并且当前运行的任务优先级小于优先级队列组中任务最高优先级时,立刻进行任务切换,(除非当前系统正处于中断处理程序中或禁止任务切换的状态)。
首先一定要有一个空闲任务,空闲任务是 FreeRTOS 系统自己启动的一个任务,优先级最低。当整个系统都没有就绪任务的时候,系统必须保证有一个任务在运行,空闲任务就是为这个设计的。当用户任务延时到期,又会从空闲任务切换回用户任务。如果当系统中始终有一个在运行的任务时,可以不加空闲任务。
一个任务的三要素是任务主体函数,任务栈,任务控制块。FreeRTOS 里面有一个叫动态任务创建函数 xTaskCreate(),它将任务主体函数,任务栈(动态的)和任务控制块(动态的)这三者联系在一起,让任务可以随时被系统启动。
static void LED_Task(void* parameter)
{
while(1)
{
LED1_ON;
vTaskDelay(500);
printf("LED_Task Running,LED1_ON\r\n");
LED1_OFF;
vTaskDelay(500);
printf("LED_Task Running,LED1_OFF\r\n");
}
}
static StackType_t LED_Task_Stack[128];
static StaticTask_t LED_Task_TCB;
stm32中断优先级分组为4,即4位都用来表示抢占优先级,范围为0~15,优先级只需要分组一次即可,以后如果有其他的任务需要用到中断时,都统一用这个优先级分组,不能再分组。
static void BSP_Init(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
LED_GPIO_Config();
USART_Config();
}
static void AppTaskCreate(void)
{
BaseType_t xReturn = pdPASS;
taskENTER_CRITICAL(); //进入临界区
/* 创建LED_Task任务 */
xReturn = xTaskCreate((TaskFunction_t )LED_Task, /* 任务入口函数 */
(const char* )"LED_Task", /* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL, /* 任务入口函数参数 */
(UBaseType_t )2, /* 任务的优先级 */
(TaskHandle_t* )&LED_Task_Handle); /* 任务控制块指针 */
if(pdPASS == xReturn)/* 创建成功 */
printf("LED_Task任务创建成功!\n");
else
printf("LED_Task任务创建失败!\n");
vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
taskEXIT_CRITICAL(); //退出临界区
}
int main(void)
{
BaseType_t xReturn = pdPASS;
BSP_Init(); //硬件初始化
/* 创建 AppTaskCreate 任务 */
xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate, /* 任务入口函数 */
(const char* )"AppTaskCreate", /* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL, /* 任务入口函数参数 */
(UBaseType_t )1, /* 任务的优先级 */
(TaskHandle_t* )&AppTaskCreate_Handle); /* 任务控制块指针 */
if(pdPASS == xReturn) /* 创建成功 */
vTaskStartScheduler(); /* 启动任务,开启调度 */
else
return -1;
while(1); /* 正常不会执行到这里 */
}
整个main.c文件全貌
/*动态创建单个任务*/
/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"
/* 开发板硬件bsp头文件 */
#include "bsp_led.h"
#include "bsp_usart.h"
static TaskHandle_t AppTaskCreate_Handle = NULL; /* 定义创建任务句柄 */
static TaskHandle_t LED_Task_Handle = NULL; /* 定义LED任务句柄 */
static void AppTaskCreate(void); /* 用于创建任务 */
static void LED_Task(void* pvParameters); /* LED_Task任务实现 */
static void BSP_Init(void); /* 用于初始化板载相关资源 */
int main(void)
{
BaseType_t xReturn = pdPASS;
BSP_Init();
/* 创建 AppTaskCreate 任务 */
xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate, /* 任务入口函数 */
(const char* )"AppTaskCreate", /* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL, /* 任务入口函数参数 */
(UBaseType_t )1, /* 任务的优先级 */
(TaskHandle_t* )&AppTaskCreate_Handle); /* 任务控制块指针 */
if(pdPASS == xReturn) /* 创建成功 */
vTaskStartScheduler(); /* 启动任务,开启调度 */
else
return -1;
while(1); /* 正常不会执行到这里 */
}
static void AppTaskCreate(void)
{
BaseType_t xReturn = pdPASS;
taskENTER_CRITICAL(); //进入临界区
/* 创建LED_Task任务 */
xReturn = xTaskCreate((TaskFunction_t )LED_Task, /* 任务入口函数 */
(const char* )"LED_Task", /* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL, /* 任务入口函数参数 */
(UBaseType_t )2, /* 任务的优先级 */
(TaskHandle_t* )&LED_Task_Handle); /* 任务控制块指针 */
if(pdPASS == xReturn)/* 创建成功 */
printf("LED_Task任务创建成功!\n");
else
printf("LED_Task任务创建失败!\n");
vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
taskEXIT_CRITICAL(); //退出临界区
}
static void LED_Task(void* parameter)
{
while(1)
{
LED1_ON;
vTaskDelay(500); /* 延时500个tick */
printf("LED_Task Running,LED1_ON\r\n");
LED1_OFF;
vTaskDelay(500); /* 延时500个tick */
printf("LED_Task Running,LED1_OFF\r\n");
}
}
static void BSP_Init(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
LED_GPIO_Config();
USART_Config();
}
任务使用的栈和任务控制块是预先定义好的全局变量。
静态创建任务函数
static TaskHandle_t xxx_Task_Handle; //创建任务句柄
static StackType_t xxx_Task_Stack[num]; //创建任务堆栈
static StaticTask_t xxx_Task_TCB; //创建任务控制块
//在config中配置
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 ) //任务控制块
创建任务—SRAM 动态内存,任务使用的栈和任务控制块是在创建任务的时候FreeRTOS 动态分配的,一般我们用动态创建。
动态创建任务函数
static TaskHandle_t xxx_Task_Handle = NULL; //要定义任务句柄
//在config中配置
configSUPPORT_DYNAMIC_ALLOCATION == 1
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, //任务函数
const char * const pcName, //任务名称
const uint16_t usStackDepth, //任务堆栈大小
void * const pvParameters, //给任务函数的参数
UBaseType_t uxPriority, //任务优先级
TaskHandle_t * const pxCreatedTask ) //任务控制块指针