目录
一、FreeRTOS任务创建与删除有关函数
1.1、创建/删除任务的API函数
1.1.1、动态创建任务
1.1.2、静态创建任务
1.1.3、删除任务
二、FreeRTOS任务创建与删除(动态方法)
2.1、实例
三、FreeRTOS任务创建与删除(静态方法)
四、临界区与任务调度器
4.1、临界区是什么
4.2、临界区使用
4.3、任务调度器
任务的创建和删除本质上就是调用FreeRTOS的API函数
函数名:xTaskCreate(xxx参数),作用:动态方式创建任务;
动态创建任务分析:
1)任务的任务控制块以及任务的栈空间所需的内存,均由FreeRTOS从FreeRTOS管理的堆中分配
2)函数存在返回值,当返回paPASS则表示任务创建成功,当返回
errCOULD_ NOT_ ALLOCATE REQUIRED_ MEMORY表示任务创建失败
3)实现动态创建任务流程只需三步:
1、将宏configSUPPORT_DYNAMIC_ALLOCATION配置为1;
2、定义函数入口参数;
3、编写任务函数。
此函数创建的任务会立即进入就绪态,有任务调度器调度运行,内部实现三步:
1、申请堆栈内存与任务控制块内存;
2、TCB结构体成员赋值;
3、添加新任务到就绪列表中。
动态任务创建函数原型:
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
const configSTACK_DEPTH_TYPE usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
参数解释:
pxTaskCode,含义:指向任务函数的指针
pcName,含义:任务名称
usStackDepth,含义:栈堆大小
pvParameters,含义:传递给任务的参数
uxPriority,含义:任务优先级
pxCreatedTask,含义:任务句柄
函数名:xTaskCreateStatic(xxx参数),作用:静态方式创建任务
静态创建任务分析:
1)任务的任务控制块以及任务的栈空间所需内存,需要用户自己分配提供
2)函数存在返回值,返回值为NULL则表示用户没有提供相应的内存,任务创建失败,反正为其他值,则表示任务句柄,任务创建成功
3)静态创建任务流程五大步:
1、需将宏configSUPPORT_ STATIC_ ALLOCATION 配置为1
2、定义空闲任务&定时器任务的任务堆栈及TCB
3、实现两个接口函数:vApplicationGetTimerTaskMemory()、vApplicationGetldleTaskMemory()4、定义函数入口参数
5、编写任务函数
此函数创建的任务会立即进入就绪态,由任务调度器调度运行,内部实现为:
1、TCB结构体成员赋值(因为内存已经提前设置好了,无需申请内存)
2、添加新任务到就绪列表中
静态任务创建原型:
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(TaskHandle_t xTaskToDelete),作用:删除任务
形参:xTaskToDelete,描述:待删除任务的任务句柄
删除任务函数分析:
1)被删除的任务将从就绪态任务列表、阻塞态任务列表、挂起态任务列表和事件列表中移除
2)当传入的参数为NULL时,则任务删除任务自身(当前正在运行任务)
3)空闲任务会负责释放被删除任务中由系统分配的内存,但是用户在任务删除前申请的内存,需要用户在任务被删除前提前释放,否则将导致内存泄露
4)使用只需两步:
1、使用删除任务函数,需将宏INCLUDE_ _vTaskDelete 配置为1
2、入口参数输入需要删除的任务句柄(NULL代表删除本身)5)任务删除内部实现过程:
1、获取所要删除任务的控制块,通过传入的任务句柄,判断所需要删除哪个任务,NULL代表删除自身
2、将被删除任务,移除所在列表,将该任务在所在列表中移除,包括:就绪、阻塞、挂起、事件等列表
3、删除任务自身,需先添加到等待删除列表,内存释放将在空闲任务执行,判断所需要删除的任务,删除其他任务,释放内存,任务数量-1
4、 更新下个任务的阻塞时间,更新下一个任务的阻塞超时时间,以防被删除的任务就是下一个阻塞超时的任务
说明:
1)下列实例实现了创建了一个开始任务,并在开始任务中创建了任务1和任务2,并在创建完成两个任务后删除自身
2)任务1实现了led0的每隔100ms闪烁,任务1实现了led1的每隔100ms闪烁
3)移植测试要注意对应的配置是否一致,直接复制代码测试基本上都会报错
#include "stm32f10x.h"
#include "stdio.h"
#include "string.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "queue.h"
#include "timers.h"
#include "event_groups.h"
#include "led.h"
#include "usart.h"
//初始任务 用于创建其他任务,创建后自动删除
#define INIT_TASK_PRIO 1 /* 任务优先级 */
#define INIT_STK_SIZE 256 /* 任务堆栈大小 */
TaskHandle_t INITTask_Handler; /* 任务句柄 */
void init_task(void *pvParameters); /* 任务函数 */
//LED0灯闪烁
#define LED0_TASK_PRIO 2 /* 任务优先级 */
#define LED0_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t LED0Task_Handler; /* 任务句柄 */
void led0_task(void *pvParameters); /* 任务函数 */
//LED1灯闪烁
#define LED1_TASK_PRIO 3 /* 任务优先级 */
#define LED1_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t LED1Task_Handler; /* 任务句柄 */
void led1_task(void *pvParameters); /* 任务函数 */
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
LED_Init(); //LED初始化
USART1_Init(115200); //USART1初始化
LED0_H;
LED1_H;
//初始任务
xTaskCreate((TaskFunction_t )init_task, //任务函数
(const char* )"init_task", //任务名称
(uint16_t )INIT_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )INIT_TASK_PRIO, //任务优先级
(TaskHandle_t* )&INITTask_Handler); //任务句柄
vTaskStartScheduler();
}
//初始任务任务函数
void init_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建LED0任务
xTaskCreate((TaskFunction_t )led0_task,
(const char* )"led0_task",
(uint16_t )LED0_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED0_TASK_PRIO,
(TaskHandle_t* )&LED0Task_Handler);
//创建LED1任务
xTaskCreate((TaskFunction_t )led1_task,
(const char* )"led1_task",
(uint16_t )LED1_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED1_TASK_PRIO,
(TaskHandle_t* )&LED1Task_Handler);
vTaskDelete(INITTask_Handler); //删除初始任务
taskEXIT_CRITICAL(); //退出临界区
}
void led0_task(void *pvParameters)
{
while(1)
{
LED0_L;
vTaskDelay(100);
LED0_H;
vTaskDelay(100);
printf("LED0 Test\r\n");
}
}
void led1_task(void *pvParameters)
{
while(1)
{
LED1_L;
vTaskDelay(100);
LED1_H;
vTaskDelay(100);
printf("LED1 Test\r\n");
}
}
说明:
1)静态创建任务比状态创建任务多的步骤,如下:
1、需要实现两个接口函数-->空闲任务接口必须实现,定时器任务不是必须的,不使用则关闭对应的宏
2)其它步骤与创建动态任务一致
实例暂无
1、代码的临界区
代码的临界区也称为临界区,指处理时不可分割的代码,运行这些代码不允许被打断。一旦这部分代码开始执行,则不允许任何中断打入(这不是绝对的,如果中断不调用任何包含临界区的代码,也不访问任何临界区使用的共享资源,这个中断可能可以执行)。为确保临界区代码的执行,在进入临界区之前要关中断,而临界区代码执行完成以后要立即开中断。
实例:当我们创建任务时,每创建一个任务,这个任务就会被任务调度器调度到运行态。但是我们希望所有的任务创建好了,按照指定的优先级进行运行,这种情况下就需要使用临界区。
使用思路:在创建任务前,使用FreeRTOS的内置函数-->taskENTER_CRITICAL()进入到临界区,在之后被创建的任务,就保持在就绪态,任务调度器无法进行任务的调度,当所有的任务完成创建时,我们使用FreeRTOS的内置函数-->taskEXIT_CRITICAL()退出临界区。此时所有被创建的任务都是就绪态,但当我们使用抢占式时,任务就回按照优先级进行运行,达到我们的目的。
函数:vTaskStartScheduler(),作用:开启任务调度
怎么使用:在创建任务后,调用该函数即可,每次程序运行只需调用一次(在第一个创建的任务函数后面调用该函数即可)
当我们创建任务,任务并不会自动的进行排序,我们需要使用FreeRTOS内置的函数-->vTaskStartScheduler()帮助我们把创建的任务按照优先级进行排序,程序才会有序运行。