【03】FreeRTOS的任务创建(静态和动态)和删除

目录

1.任务创建和删除的API函数

1.1动态创建任务函数

1.1.1实现动态创建任务流程 

1.1.2任务控制块结构成员介绍 

1.2静态创建任务函数 

 1.2.1实现静态创建任务流程

1.3任务删除函数 

1.3.1删除任务流程

2.任务创建和删除(动态方法)

2.1宏configSUPPORT_DYNAMIC_ALLOCATION置1

2.2定义函数入口参数

2.3编写任务函数

3.任务创建和删除(静态方法)

3.1将宏configSUPPORT_STATIC_ALLOCATION 配置为 1

3.2实现两个接口函数

3.3定义函数入口参数

4.总结


1.任务创建和删除的API函数

        任务的创建和删除本质就是调用FreeRTOS的API函数。

API函数描述
xTaskCreat()动态方式创建任务
xTaskCreatStatic()静态方式创建任务
vTaskDelete删除任务

动态创建任务:任务的任务控制块以及任务的栈空间所需的内存,均由FreeRTOS从FreeRTOS管理的堆中分配。(由FreeRTOS自动分配,不需要人为操作

静态创建任务:任务的任务控制块以及任务的栈空间所需的内存,需要用户分配提供。(人为地定义内存,比如说定义一个数组当作任务栈空间

        二者的区别除了是否需要人为操作去分配空间,还有动态申请的空间由FreeRTOS所管理,静态申请的空间不属于FreeRTOS管理,独立于FreeRTOS。

1.1动态创建任务函数

BaseType_t xTaskCreate
( 	TaskFunction_t 				    pxTaskCode,		/* 指向任务函数的指针 */					 
    const char * const 				pcName, 		/* 任务名字,最大长度configMAX_TASK_NAME_LEN */
	const 	configSTACK_DEPTH_TYPE  usStackDepth, 	/* 任务堆栈大小,注意字为单位 */
	void * const 					pvParameters,	/* 传递给任务函数的参数 */
	UBaseType_t 					uxPriority,		/* 任务优先级,范围:0 ~ configMAX_PRIORITIES - 1 */
	TaskHandle_t * const 			pxCreatedTask 	/* 任务句柄,就是任务的任务控制块 */
)

参数介绍 

pxTaskCode: 指向任务函数的指针。比如创建了一个任务,任务所需要实现的功能全部放在任务函数中,通过此参数指向任务函数。

pcName:任务名字。名字长度由FreeRTOSConfig.h中的宏configMAX_TASK_NAME_LEN决定,默认为16字符长度。

usStackDepth:任务堆栈大小。注意单位是“字”。例如堆大小为100,则实际大小为100×4=400字节。

pvParameters:传递给任务函数的参数。一般用不到,传入空(NULL

uxPriority:任务优先级。每个任务都有自己的优先级,具体范围是0~configMAX_PRIORITIES - 1,宏定义在FreeRTOSConfig中设置。在程序中设置的为32,并且任务优先级越大,任务就越优先

pxCreatedTask:任务句柄。删除任务是通过任务句柄进行操作,任务句柄其实就是任务的任务控制块。在函数内部任务控制块等效于任务句柄

动态任务创建任务返回值BaseType
返回值描述
pdPASS任务创建成功
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY任务创建失败(其中MEMORY,主要是指任务堆栈过大,动态创建是在FreeRTOS管理的堆空间中创建,如果超出了堆空间大小,则会创建失败

1.1.1实现动态创建任务流程 

1、将宏cofigSUPPORT_DYNAMIC_ALLOCATION配置为1(在FreeRTOSCofig.h中配置

2、定义函数的入口参数(可以提前定义,任务创建时代入);

3、编写任务函数(任务需要实现的功能,在任务函数中完成);

        使用xTaskCreat()函数创建的任务会立即进入就绪态,由任务调度器调度运行。比如说创建了很多任务,这些任务创建后会立刻进入就绪态,由任务调度器在就绪态中找到任务优先级最高的去执行

动态创建任务的内部实现方式:

1、申请任务堆栈and任务控制块的内存(只需要定义所申请的大小,之后由FreeRTOS自动地去申请);

2、TCB结构体成员赋值(TCB,任务控制块。类似于人的身份证,每个任务都有自己的任务控制块,任务控制块存取任务的一些特征,比如任务名、优先级、状态等);

3、添加新任务到就绪列表中(任务进入到就绪态)。

1.1.2任务控制块结构成员介绍 

typedef struct tskTaskControlBlock       
{
    volatile StackType_t 	* pxTopOfStack; 				/* 任务栈栈顶,必须为TCB的第一个成员 */
   	ListItem_t 			    xStateListItem;           		/* 任务状态列表项 */      
	ListItem_t 			    xEventListItem;					/* 任务事件列表项 */     
    UBaseType_t 			uxPriority;                		/* 任务优先级,数值越大,优先级越大 */
    StackType_t 			* pxStack;						/* 任务栈起始地址 */
    char 				    pcTaskName[ configMAX_TASK_NAME_LEN ]; 	/* 任务名字 */		
	…
	省略很多条件编译的成员
} tskTCB;

         任务控制块是一个结构体,结构体存在很多的成员变量,就好比任务的身份证,保留了任务的一些特征。

参数介绍

*pxTopOfStack:任务栈顶指针。必须为TCB的第一个成员,因为和任务切换、任务上下文保存、任务恢复都息息相关

xStateListItem:任务状态列表项。任务处于的状态保存在此参数中,比如:就绪态、挂起态、阻塞态、运行态

xEventListItem:任务事件列表项。如果当前正在等待某些事件,就会用到此参数

uxPriority:任务优先级。数值越大,优先级越大当前任务的优先级存在此参数中

*pxStack:任务堆栈的首地址。

pcTaskName[ configMAX_TASK_NAME_LEN ]:任务名字。

1.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	    /* 任务控制块指针,由用户分配 */
); 		

         静态创建任务函数xTaskCreatStatic()和动态创建任务函数xTaskCreat()类似。

 参数介绍

pxTaskCode:指向任务函数的指针。

pcName:任务函数名字。

ulStackDepth:任务堆栈大小。以“字”为单位

pvParameters:传递的任务函数参数。一般为空,即传入“NULL”

uxPriority:任务优先级。

puxStackBuffer:任务堆栈。一般为数组,由用户自己定义(这里和动态创建有区别,动态创建只需要指定任务堆栈大小ulStackDepth,剩下的是由FreeRTOS根据任务大小,自动去申请堆大小,而静态需要给定数组地址)。

pxTaskBuffer:任务控制块指针。指向任务控制块的地址,任务控制块保留了很对信息,信息需要内存来保存,这部分内存也需要用户自己分配

静态创建函数返回值TaskHandle_t
返回值描述
NULL用户没有提供相应的内存,任务创建失败。(比如说任务堆栈puxStackBufer参数并没有指定,那么就会创建失败
其他值任务句柄,任务创建成功

 1.2.1实现静态创建任务流程

1、需将宏cofigSUPPORT_STATIC_ALLOCATION配置为1(在FreeRTOSCofig.h中配置);

2、定义空闲任务and定时器任务的任务堆栈及TCB(空闲任务是必须有,在RTOS中CPU是不能停止的,在空闲时需要执行空闲任务。软件定时器任务是可选的,当使能了软件定时器功能,这个任务就需要被创建,如果没有被使能,就不需要创建软件定时器任务,使能也是在FreeRTOSCofig.h中进行配置。这两个任务都是静态创建,用户都需要分配任务堆栈和任务控制块);

3、实现两个接口函数(第一个是空闲任务的内存赋值vApplicationGetldleTaskMemory(),第二个是软件定时器的内存赋值vApplicationGetTimerTaskMemory()。由于在第2步任务堆栈、任务控制块需要赋值给结构体成员,就通过这两个函数来实现。空闲任务是必须的,软件定时器任务是可选的);

4、定义函数入口参数

5、编写任务函数

        静态创建任务函数xTaskCreatStatic()创建的任务会立刻进入就绪态,由任务调度器进行调度。

静态创建任务内部实现逻辑

1、TCB结构体成员赋值(将任务的一些特征赋值给任务控制块TCB

2、添加新任务到就绪列表中

1.3任务删除函数 

void vTaskDelete(TaskHandle_t xTaskToDelete);

任务删除函数只有一个入口参数 xTaskToDelete:待删除任务的任务句柄。比如给我们创建了一个任务,任务是具有任务句柄的,删除任务就需要将任务句柄传入此函数就可以删除任务了。此函数用于删除已经被创建成功的任务,如果没有被创建,是不能被删除的

        被删除的任务将从就绪态任务列表、阻塞态任务列表、 挂起态任务列表和事件列表中移除(不管任务处于何种状态,只要调用删除函数vTaskDelete(),传入需要删除的任务的句柄,那么都会被删除)。

注意:

1、当传入的参数为NULL,则代表删除任务自身(当前正在运行的任务。比如说创建了两个任务task1、task2,在task1的任务函数中调用的任务删除,传入的参数为task2的任务句柄,则删除task2这个任务,如果传入NULL参数,则删除自身task1任务。

2、空闲任务会负责释放被删除任务中被系统分配的内存(动态创建任务的内存是由系统自己分配的,而静态创建任务是由用户自己定义,这里是针对动态创建。删除自身任务,即函数xTaskDelete()传入NULL时,才会利用空闲任务释放掉被删除任务使用的系统分配的内存,如果是在task1任务中删除task2,则在task1中释放task2所使用的内存空间),但是由用户在任务删除前申请的内存,则需要用户在任务被删除前提前释放,否则将导致内存泄露(这里针对静态创建)。

1.3.1删除任务流程

1、使用删除任务函数,需将宏INCLUDE_vTaskDelete配置为1。(宏在FreeRTOSCofig.h中定义

2、入口参数输入需要删除的任务句柄(NULL代表删除本身)。

删除任务内部实现流程

1、获取所要删除任务的控制块:通过传入的任务句柄,判断所需要删除哪个任务,NULL代表删除自身。(找到所需要删除任务的任务控制块

2、将被删除任务,在所在列表中移除:将该任务在所在列表中移除,包括:就绪、阻塞、挂起、事件等列表。

3、判断所需要删除的任务:删除任务自身,需先添加到等待删除列表,内存释放将在空闲任务执行;删除其他任务,释放内存,任务总数量减1。

4、更新下个任务的阻塞时间:更新下一个任务的阻塞超时时间,以防被删除的任务就是下一个阻塞超时的任务。比如有3个任务,task1、task2、task3,task2及task3进入阻塞态,task2阻塞延时20ms,task3阻塞延时50ms,task2和task3会挂载到阻塞列表中,时间短的task2会放在task3前面,此时肯定是20ms的task2先到,时间到了将task2放入就绪列表,如果在task1中删除的任务刚好为task2,不管task2在何种状态都将被移除,而阻塞时间还是20ms,则需要更新成下一个阻塞时间50ms。

2.任务创建和删除(动态方法)

1、实验目的:学会 xTaskCreate() vTaskDelete()的使用

2、实验设计:将设计四个任务:start_task、task1、task2、task3

四个任务的功能如下:

start_task:用来创建其他的三个任务(start_task只执行一次,执行完以后调用vTaskDelete()函数进行删除,如果重复调用,重复创建将导致FreeRTOS管理的内存堆不够使用)

task1:实现LED0每500ms闪烁一次

task2:实现LED1每1000ms闪烁一次

task3:判断按键KEY0是否按下,按下则删掉task2

        本次实验将根据上次文章02中使用的文件进行

        删除main.c中用不到的变量及函数调用。在测试程序freertos_demo.c中只留下以下内容:freertos_demo()函数是FreeRTOS的入口函数,在此任务中进行任务创建和开启任务调度,开启任务调度需要调用vTaskStartScheduler()函数。

#include "freertos_demo.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"

/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */

/******************************************************************************************************/


/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    

    //vTaskStartScheduler();
}

2.1宏configSUPPORT_DYNAMIC_ALLOCATION置1

        在FreeRTOSConfig.h中将宏configSUPPORT_DYNAMIC_ALLOCATION置1。

#define configSUPPORT_DYNAMIC_ALLOCATION        1                       //支持动态内存申请

2.2定义函数入口参数

        在task.h中250行左右,介绍了xTaskCreat()函数的使用,并且举了示例,可以通过程序介绍进行创建。也可以通过官网:官网链接,显示如下,找到API Reference找到特定函数,函数介绍中有使用示例。

        将函数xTaskCreat()整个复制到freertos_demo()函数中,并加入强制转换,防止报警告。xTaskCreate()函数的参数pxTaskCode为函数名,需创建start_task()函数任务,并且在freertos_demo()函数之前声明此函数的存在(此处并不是完整版函数,只是为了创建任务)。

void start_task()
{
	while(1);
}

pcName和函数名设置相同即可,此处为字符串需要加双引号,“start_task”;

usStackDepth任务堆大小,在freertos_demo.c的开始创建宏。此时的单位是“字”,换算成字节需要乘以4。此处用128是偏大设置,在FreeRTOS存在查找历史剩余最小空间值的API函数uxTaskGetStackHighWaterMark(),根据剩余的大小来设置堆栈大小。

#define START_TASK_STACK_SIZE 128

pvParameters入口参数,入口参数没有,则设置NULL。

uxPriority任务优先级,同样在freertos_demo.c的开始创建宏。

#define START_TASK_PRIO       1

pxCreatedTask任务句柄,同样在freertos_demo.c的开始创建。

TaskHandle_t	start_task_handler

2.3编写任务函数

freertos_demo.c中修改后的xTaskCreate()函数调用如下:

BaseType_t xTaskCreate((TaskFunction_t       )   start_task,
						(char * 			   ) "start_task", 
					    (configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,
						(void *                ) NILL,
						(UBaseType_t           ) START_TASK_PRIO,
						(TaskHandle_t *        ) start_task_handler )

用相同方式创建task1,task2,task3任务函数,并实现所需功能。在start_task任务函数中实现创建task1、task2、task3并删除自身的功能。

修改完(并未完成,可正常显示,需要在main()函数中调用按键的初始化 Key_Init()如下所示:

#include "freertos_demo.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"

/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128
TaskHandle_t    start_task_handler;
void start_task( void * pvParameters );

/* TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128
TaskHandle_t    task1_handler;
void task1( void * pvParameters );

/* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO         3
#define TASK2_STACK_SIZE   128
TaskHandle_t    task2_handler;
void task2( void * pvParameters );

/* TASK3 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK3_PRIO         4
#define TASK3_STACK_SIZE   128
TaskHandle_t    task3_handler;
void task3( void * pvParameters );
/******************************************************************************************************/


/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    
    xTaskCreate((TaskFunction_t         )   start_task,
                (char *                 )   "start_task",
                (configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   START_TASK_PRIO,
                (TaskHandle_t *         )   &start_task_handler );
    vTaskStartScheduler();
}


void start_task( void * pvParameters )
{
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   task2,
                (char *                 )   "task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &task2_handler );
                
    xTaskCreate((TaskFunction_t         )   task3,
                (char *                 )   "task3",
                (configSTACK_DEPTH_TYPE )   TASK3_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK3_PRIO,
                (TaskHandle_t *         )   &task3_handler );
    vTaskDelete(NULL);
}

/* 任务一,实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{
    while(1)
    {
        printf("task1正在运行!\r\n");
        LED0=~LED0;
        vTaskDelay(500);
    }
}

/* 任务二,实现LED1每500ms翻转一次 */
void task2( void * pvParameters )
{
    while(1)
    {
        printf("task2正在运行!\r\n");
        LED1=~LED1;
        vTaskDelay(500);
    }
}

/* 任务三,判断按键KEY0,按下KEY0删除task1 */
void task3( void * pvParameters )
{
    uint8_t key = 0;
    while(1)
    {
        printf("task3正在运行!\r\n");
        key = KEY_Scan(0);
        if(key == KEY0_PRES)
        {
			printf("删除task2!\r\n");
			vTaskDelete(task2_handler);
        }
        vTaskDelay(10);
    }
}


以上程序出现的现象是:

虽然优先级start_task<task1<task2<task3,但是,由于先创建start_task,所以先运行start_task。

在start_task创建task1,task1比start_task优先级高,则允许task1抢占start_task,在task1阻塞时运行start_task,创建task2。

task2又比start_task优先级高,则运行task2,在task2阻塞时运行start_task,创建task3。同理,运行task3,阻塞时删除start_task。

剩下的任务task3任务优先级最高,在task3阻塞时运行task2或者task1,由于task2优先级比task1高,所以一般是运行task2。

按下KEY0,删除task2,则task1会被允许,因为task3中存在延时阻塞。

多次删除task1时,串口会报错:Error:..\FreeRTOS\portable\MemMang\heap_4.c,292

如果一开始高优先级执行(即:想在start_task任务创建完再运行),在进入开始任务start_task后关闭任务调度即可。任务切换是在中断中执行的,FreeRTOS提供了进入临界区的API函数(taskENTER_CRITICAL进入临界区,taskEXIT_CRITICAL退出临界区),用来关闭中断。在task3中增加判断task2是否存在的判断,避免task2被删除了以后,再次删除task2串口打印错误。

完整的freertos_demo.c代码如下

#include "freertos_demo.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"

/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128
TaskHandle_t    start_task_handler;
void start_task( void * pvParameters );

/* TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128
TaskHandle_t    task1_handler;
void task1( void * pvParameters );

/* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO         3
#define TASK2_STACK_SIZE   128
TaskHandle_t    task2_handler;
void task2( void * pvParameters );

/* TASK3 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK3_PRIO         4
#define TASK3_STACK_SIZE   128
TaskHandle_t    task3_handler;
void task3( void * pvParameters );
/******************************************************************************************************/


/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    
    xTaskCreate((TaskFunction_t         )   start_task,
                (char *                 )   "start_task",
                (configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   START_TASK_PRIO,
                (TaskHandle_t *         )   &start_task_handler );
    vTaskStartScheduler();
}


void start_task( void * pvParameters )
{
	taskENTER_CRITICAL();                              /*进入临界区*/
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   task2,
                (char *                 )   "task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &task2_handler );
                
    xTaskCreate((TaskFunction_t         )   task3,
                (char *                 )   "task3",
                (configSTACK_DEPTH_TYPE )   TASK3_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK3_PRIO,
                (TaskHandle_t *         )   &task3_handler );
    vTaskDelete(NULL);
	taskEXIT_CRITICAL();                              /*退出临界区*/
}

/* 任务一,实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{
    while(1)
    {
        printf("task1正在运行!\r\n");
        LED0=~LED0;
        vTaskDelay(500);
    }
}

/* 任务二,实现LED1每500ms翻转一次 */
void task2( void * pvParameters )
{
    while(1)
    {
        printf("task2正在运行!\r\n");
        LED1=~LED1;
        vTaskDelay(1000);
    }
}

/* 任务三,判断按键KEY0,按下KEY0删除task1 */
void task3( void * pvParameters )
{
    uint8_t key = 0;
    while(1)
    {
        printf("task3正在运行!\r\n");
        key = KEY_Scan(0);
        if(key == KEY0_PRES)
        {
			if(task2_handler!=NULL){
				printf("删除task2!\r\n");
				vTaskDelete(task2_handler);
				task2_handler=NULL;
			}
        }
        vTaskDelay(10);
    }
}

3.任务创建和删除(静态方法)

1、实验目的:学会 xTaskCreateStatic() vTaskDelete()的使用

2、实验设计:将设计四个任务:start_task、task1、task2、task3

四个任务的功能如下:

start_task:用来创建其他的三个任务(start_task只执行一次,执行完以后调用vTaskDelete()函数进行删除,如果重复调用,重复创建将导致FreeRTOS管理的内存堆不够使用)

task1:实现LED0每500ms闪烁一次

task2:实现LED1每1000ms闪烁一次

task3:判断按键KEY0是否按下,按下则删掉task2

 本次实验程序基于本篇文章中第二节内容

不同的是任务创建方式,所以删除start_task()函数中任务创建,task1、task2、task3中实现的功能不做改变。删除完如下所示

#include "freertos_demo.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"

/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128
TaskHandle_t    start_task_handler;
void start_task( void * pvParameters );

/* TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128
TaskHandle_t    task1_handler;
void task1( void * pvParameters );

/* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO         3
#define TASK2_STACK_SIZE   128
TaskHandle_t    task2_handler;
void task2( void * pvParameters );

/* TASK3 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK3_PRIO         4
#define TASK3_STACK_SIZE   128
TaskHandle_t    task3_handler;
void task3( void * pvParameters );
/******************************************************************************************************/


/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    

    vTaskStartScheduler();
}


void start_task( void * pvParameters )
{
	taskENTER_CRITICAL();                              /*进入临界区*/

    vTaskDelete(NULL);
	taskEXIT_CRITICAL();                              /*退出临界区*/
}

/* 任务一,实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{
    while(1)
    {
        printf("task1正在运行!\r\n");
        LED0=~LED0;
        vTaskDelay(500);
    }
}

/* 任务二,实现LED1每500ms翻转一次 */
void task2( void * pvParameters )
{
    while(1)
    {
        printf("task2正在运行!\r\n");
        LED1=~LED1;
        vTaskDelay(500);
    }
}

/* 任务三,判断按键KEY0,按下KEY0删除task1 */
void task3( void * pvParameters )
{
    uint8_t key = 0;
    while(1)
    {
        printf("task3正在运行!\r\n");
        key = KEY_Scan(0);
        if(key == KEY0_PRES)
        {
			if(task2_handler!=NULL){
				printf("删除task2!\r\n");
				vTaskDelete(task2_handler);
				task2_handler=NULL;
			}
        }
        vTaskDelay(10);
    }
}

3.1将宏configSUPPORT_STATIC_ALLOCATION 配置为 1

        由于在老版本的FreeRTOSCofig.h中并没有定义宏configSUPPORT_STATIC_ALLOCATION,所以复制2022正点原子FreeRTOS程序中的FreeRTOSCoifig.h中程序。头文件引入路径需稍作修改

#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

/* 头文件 */
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include <stdint.h>

extern uint32_t SystemCoreClock;

/* 基础配置项 */
#define configUSE_PREEMPTION                            1                       /* 1: 抢占式调度器, 0: 协程式调度器, 无默认需定义 */
#define configUSE_PORT_OPTIMISED_TASK_SELECTION         1                       /* 1: 使用硬件计算下一个要运行的任务, 0: 使用软件算法计算下一个要运行的任务, 默认: 0 */
#define configUSE_TICKLESS_IDLE                         0                       /* 1: 使能tickless低功耗模式, 默认: 0 */
#define configCPU_CLOCK_HZ                              SystemCoreClock         /* 定义CPU主频, 单位: Hz, 无默认需定义 */
//#define configSYSTICK_CLOCK_HZ                          (configCPU_CLOCK_HZ / 8)/* 定义SysTick时钟频率,当SysTick时钟频率与内核时钟频率不同时才可以定义, 单位: Hz, 默认: 不定义 */
#define configTICK_RATE_HZ                              1000                    /* 定义系统时钟节拍频率, 单位: Hz, 无默认需定义 */
#define configMAX_PRIORITIES                            32                      /* 定义最大优先级数, 最大优先级=configMAX_PRIORITIES-1, 无默认需定义 */
#define configMINIMAL_STACK_SIZE                        128                     /* 定义空闲任务的栈空间大小, 单位: Word, 无默认需定义 */
#define configMAX_TASK_NAME_LEN                         16                      /* 定义任务名最大字符数, 默认: 16 */
#define configUSE_16_BIT_TICKS                          0                       /* 1: 定义系统时钟节拍计数器的数据类型为16位无符号数, 无默认需定义 */
#define configIDLE_SHOULD_YIELD                         1                       /* 1: 使能在抢占式调度下,同优先级的任务能抢占空闲任务, 默认: 1 */
#define configUSE_TASK_NOTIFICATIONS                    1                       /* 1: 使能任务间直接的消息传递,包括信号量、事件标志组和消息邮箱, 默认: 1 */
#define configTASK_NOTIFICATION_ARRAY_ENTRIES           1                       /* 定义任务通知数组的大小, 默认: 1 */
#define configUSE_MUTEXES                               1                       /* 1: 使能互斥信号量, 默认: 0 */
#define configUSE_RECURSIVE_MUTEXES                     1                       /* 1: 使能递归互斥信号量, 默认: 0 */
#define configUSE_COUNTING_SEMAPHORES                   1                       /* 1: 使能计数信号量, 默认: 0 */
#define configUSE_ALTERNATIVE_API                       0                       /* 已弃用!!! */
#define configQUEUE_REGISTRY_SIZE                       8                       /* 定义可以注册的信号量和消息队列的个数, 默认: 0 */
#define configUSE_QUEUE_SETS                            1                       /* 1: 使能队列集, 默认: 0 */
#define configUSE_TIME_SLICING                          1                       /* 1: 使能时间片调度, 默认: 1 */
#define configUSE_NEWLIB_REENTRANT                      0                       /* 1: 任务创建时分配Newlib的重入结构体, 默认: 0 */
#define configENABLE_BACKWARD_COMPATIBILITY             0                       /* 1: 使能兼容老版本, 默认: 1 */
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS         0                       /* 定义线程本地存储指针的个数, 默认: 0 */
#define configSTACK_DEPTH_TYPE                          uint16_t                /* 定义任务堆栈深度的数据类型, 默认: uint16_t */
#define configMESSAGE_BUFFER_LENGTH_TYPE                size_t                  /* 定义消息缓冲区中消息长度的数据类型, 默认: size_t */

/* 内存分配相关定义 */
#define configSUPPORT_STATIC_ALLOCATION                 1                       /* 1: 支持静态申请内存, 默认: 0 */
#define configSUPPORT_DYNAMIC_ALLOCATION                1                       /* 1: 支持动态申请内存, 默认: 1 */
#define configTOTAL_HEAP_SIZE                           ((size_t)(10 * 1024))   /* FreeRTOS堆中可用的RAM总量, 单位: Byte, 无默认需定义 */
#define configAPPLICATION_ALLOCATED_HEAP                0                       /* 1: 用户手动分配FreeRTOS内存堆(ucHeap), 默认: 0 */
#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP       0                       /* 1: 用户自行实现任务创建时使用的内存申请与释放函数, 默认: 0 */

/* 钩子函数相关定义 */
#define configUSE_IDLE_HOOK                             0                       /* 1: 使能空闲任务钩子函数, 无默认需定义  */
#define configUSE_TICK_HOOK                             0                       /* 1: 使能系统时钟节拍中断钩子函数, 无默认需定义 */
#define configCHECK_FOR_STACK_OVERFLOW                  0                       /* 1: 使能栈溢出检测方法1, 2: 使能栈溢出检测方法2, 默认: 0 */
#define configUSE_MALLOC_FAILED_HOOK                    0                       /* 1: 使能动态内存申请失败钩子函数, 默认: 0 */
#define configUSE_DAEMON_TASK_STARTUP_HOOK              0                       /* 1: 使能定时器服务任务首次执行前的钩子函数, 默认: 0 */

/* 运行时间和任务状态统计相关定义 */
#define configGENERATE_RUN_TIME_STATS                   0                       /* 1: 使能任务运行时间统计功能, 默认: 0 */
#if configGENERATE_RUN_TIME_STATS
#include "./BSP/TIMER/btim.h"
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()        ConfigureTimeForRunTimeStats()
extern uint32_t FreeRTOSRunTimeTicks;
#define portGET_RUN_TIME_COUNTER_VALUE()                FreeRTOSRunTimeTicks
#endif
#define configUSE_TRACE_FACILITY                        1                       /* 1: 使能可视化跟踪调试, 默认: 0 */
#define configUSE_STATS_FORMATTING_FUNCTIONS            1                       /* 1: configUSE_TRACE_FACILITY为1时,会编译vTaskList()和vTaskGetRunTimeStats()函数, 默认: 0 */

/* 协程相关定义 */
#define configUSE_CO_ROUTINES                           0                       /* 1: 启用协程, 默认: 0 */
#define configMAX_CO_ROUTINE_PRIORITIES                 2                       /* 定义协程的最大优先级, 最大优先级=configMAX_CO_ROUTINE_PRIORITIES-1, 无默认configUSE_CO_ROUTINES为1时需定义 */

/* 软件定时器相关定义 */
#define configUSE_TIMERS                                1                               /* 1: 使能软件定时器, 默认: 0 */
#define configTIMER_TASK_PRIORITY                       ( configMAX_PRIORITIES - 1 )    /* 定义软件定时器任务的优先级, 无默认configUSE_TIMERS为1时需定义 */
#define configTIMER_QUEUE_LENGTH                        5                               /* 定义软件定时器命令队列的长度, 无默认configUSE_TIMERS为1时需定义 */
#define configTIMER_TASK_STACK_DEPTH                    ( configMINIMAL_STACK_SIZE * 2) /* 定义软件定时器任务的栈空间大小, 无默认configUSE_TIMERS为1时需定义 */

/* 可选函数, 1: 使能 */
#define INCLUDE_vTaskPrioritySet                        1                       /* 设置任务优先级 */
#define INCLUDE_uxTaskPriorityGet                       1                       /* 获取任务优先级 */
#define INCLUDE_vTaskDelete                             1                       /* 删除任务 */
#define INCLUDE_vTaskSuspend                            1                       /* 挂起任务 */
#define INCLUDE_xResumeFromISR                          1                       /* 恢复在中断中挂起的任务 */
#define INCLUDE_vTaskDelayUntil                         1                       /* 任务绝对延时 */
#define INCLUDE_vTaskDelay                              1                       /* 任务延时 */
#define INCLUDE_xTaskGetSchedulerState                  1                       /* 获取任务调度器状态 */
#define INCLUDE_xTaskGetCurrentTaskHandle               1                       /* 获取当前任务的任务句柄 */
#define INCLUDE_uxTaskGetStackHighWaterMark             1                       /* 获取任务堆栈历史剩余最小值 */
#define INCLUDE_xTaskGetIdleTaskHandle                  1                       /* 获取空闲任务的任务句柄 */
#define INCLUDE_eTaskGetState                           1                       /* 获取任务状态 */
#define INCLUDE_xEventGroupSetBitFromISR                1                       /* 在中断中设置事件标志位 */
#define INCLUDE_xTimerPendFunctionCall                  1                       /* 将函数的执行挂到定时器服务任务 */
#define INCLUDE_xTaskAbortDelay                         1                       /* 中断任务延时 */
#define INCLUDE_xTaskGetHandle                          1                       /* 通过任务名获取任务句柄 */
#define INCLUDE_xTaskResumeFromISR                      1                       /* 恢复在中断中挂起的任务 */

/* 中断嵌套行为配置 */
#ifdef __NVIC_PRIO_BITS
    #define configPRIO_BITS __NVIC_PRIO_BITS
#else
    #define configPRIO_BITS 4
#endif

#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY         15                  /* 中断最低优先级 */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    5                   /* FreeRTOS可管理的最高中断优先级 */
#define configKERNEL_INTERRUPT_PRIORITY                 ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY            ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_API_CALL_INTERRUPT_PRIORITY           configMAX_SYSCALL_INTERRUPT_PRIORITY

/* FreeRTOS中断服务函数相关定义 */
#define xPortPendSVHandler                              PendSV_Handler
#define vPortSVCHandler                                 SVC_Handler

/* 断言 */
#define vAssertCalled(char, int) printf("Error: %s, %d\r\n", char, int)
#define configASSERT( x ) if( ( x ) == 0 ) vAssertCalled( __FILE__, __LINE__ )

/* FreeRTOS MPU 特殊定义 */
//#define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0
//#define configTOTAL_MPU_REGIONS                                8
//#define configTEX_S_C_B_FLASH                                  0x07UL
//#define configTEX_S_C_B_SRAM                                   0x07UL
//#define configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY            1
//#define configALLOW_UNPRIVILEGED_CRITICAL_SECTIONS             1

/* ARMv8-M 安全侧端口相关定义。 */
//#define secureconfigMAX_SECURE_CONTEXTS         5

#endif /* FREERTOS_CONFIG_H */
#define configSUPPORT_STATIC_ALLOCATION                 1                       /* 1: 支持静态申请内存, 默认: 0 */

3.2实现两个接口函数

        此时编译程序发现会出现两个报错。vApplicationGetIdleTaskMemory()和vApplicationGetTimerTaskMemory()函数未创建。先在freertos_demo.c中创建一下空闲任务和定时器任务。空闲任务和软件定时器任务栈大小都在FreeRTOSConfig.h中定义。定时器任务是可选的,在FreeRTOSConfig.h中选择是否使能宏configUSE_TIMERS,默认使能。

/*空闲任务 配置*/
StaticTask_t idle_task_tcb;
StackType_t idle_task_stack[configMINIMAL_STACK_SIZE];
/*软件定时器任务 配置*/
StaticTask_t timer_task_tcb;
StackType_t timer_task_stack[configTIMER_TASK_STACK_DEPTH];
/*空闲任务内存分配*/
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;
}

3.3定义函数入口参数

静态创建任务API函数除了最后两个参数需要重新创建外,其他参数和动态创建任务函数相同,并且返回任务句柄,任务句柄非空即为创建成功

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128
TaskHandle_t    start_task_handler;
StackType_t 	start_task_stack[START_TASK_STACK_SIZE];
StaticTask_t    start_task_tcb;
void freertos_demo(void)
{    
	start_task_handler=xTaskCreateStatic(  (TaskFunction_t	) 	start_task,
										(char *			)  	"start_task",
										(uint32_t		) 	START_TASK_STACK_SIZE,
										(void *			) 	NULL,
										(UBaseType_t	) 	START_TASK_PRIO,
										(StackType_t *	)  	start_task_stack,
										(StaticTask_t *	)  	&start_task_tcb );
    vTaskStartScheduler();
}

 同理,创建task1、task2、task3。完整的freertos_demo代码如下

#include "freertos_demo.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"

/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128
TaskHandle_t    start_task_handler;
StackType_t 	start_task_stack[START_TASK_STACK_SIZE];
StaticTask_t    start_task_tcb;
void start_task( void * pvParameters );

/* TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128
TaskHandle_t    task1_handler;
StackType_t 	task1_stack[TASK1_STACK_SIZE];
StaticTask_t    task1_tcb;
void task1( void * pvParameters );

/* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO         3
#define TASK2_STACK_SIZE   128
TaskHandle_t    task2_handler;
StackType_t 	task2_stack[TASK2_STACK_SIZE];
StaticTask_t    task2_tcb;
void task2( void * pvParameters );

/* TASK3 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK3_PRIO         4
#define TASK3_STACK_SIZE   128
TaskHandle_t    task3_handler;
StackType_t 	task3_stack[TASK3_STACK_SIZE];
StaticTask_t    task3_tcb;
void task3( void * pvParameters );
/******************************************************************************************************/
/*空闲任务 配置*/
StaticTask_t idle_task_tcb;
StackType_t idle_task_stack[configMINIMAL_STACK_SIZE];
/*软件定时器任务 配置*/
StaticTask_t timer_task_tcb;
StackType_t timer_task_stack[configTIMER_TASK_STACK_DEPTH];
/*空闲任务内存分配*/
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;
}
/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    
	start_task_handler=xTaskCreateStatic(  (TaskFunction_t	) 	start_task,
										(char *			)  	"start_task",
										(uint32_t		) 	START_TASK_STACK_SIZE,
										(void *			) 	NULL,
										(UBaseType_t	) 	START_TASK_PRIO,
										(StackType_t *	)  	start_task_stack,
										(StaticTask_t *	)  	&start_task_tcb );
    vTaskStartScheduler();
}


void start_task( void * pvParameters )
{
	taskENTER_CRITICAL();                              /*进入临界区*/
	task1_handler=xTaskCreateStatic(    (TaskFunction_t	) 	task1,
										(char *			)  	"task1",
										(uint32_t		) 	TASK1_STACK_SIZE,
										(void *			) 	NULL,
										(UBaseType_t	) 	TASK1_PRIO,
										(StackType_t *	)  	task1_stack,
										(StaticTask_t *	)  	&task1_tcb );
	task2_handler=xTaskCreateStatic(    (TaskFunction_t	) 	task2,
										(char *			)  	"task2",
										(uint32_t		) 	TASK2_STACK_SIZE,
										(void *			) 	NULL,
										(UBaseType_t	) 	TASK2_PRIO,
										(StackType_t *	)  	task2_stack,
										(StaticTask_t *	)  	&task2_tcb );
	task3_handler=xTaskCreateStatic(  	(TaskFunction_t	) 	task3,
										(char *			)  	"task3",
										(uint32_t		) 	TASK3_STACK_SIZE,
										(void *			) 	NULL,
										(UBaseType_t	) 	TASK3_PRIO,
										(StackType_t *	)  	task3_stack,
										(StaticTask_t *	)  	&task3_tcb );
    vTaskDelete(NULL);
	taskEXIT_CRITICAL();                              /*退出临界区*/
}

/* 任务一,实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{
    while(1)
    {
        printf("task1正在运行!\r\n");
        LED0=~LED0;
        vTaskDelay(500);
    }
}

/* 任务二,实现LED1每1000ms翻转一次 */
void task2( void * pvParameters )
{
    while(1)
    {
        printf("task2正在运行!\r\n");
        LED1=~LED1;
        vTaskDelay(1000);
    }
}

/* 任务三,判断按键KEY0,按下KEY0删除task1 */
void task3( void * pvParameters )
{
    uint8_t key = 0;
    while(1)
    {
        printf("task3正在运行!\r\n");
        key = KEY_Scan(0);
        if(key == KEY0_PRES)
        {
			if(task2_handler!=NULL){
				printf("删除task2!\r\n");
				vTaskDelete(task2_handler);
				task2_handler=NULL;
			}
        }
        vTaskDelay(10);
    }
}

下载到开发板中,现象和动态创建任务一致。 

4.总结

1、在实际的应用中,动态方式创建任务是比较常用的,除非有特殊的需求,一般都会使用动态方式创建任务;

2、动态创建相对简单,更为常用;

3、静态创建:可将任务堆栈放置在特定的内存位置,并且无需关心对内存分配失败的处理;

4、临界区保护,保护那些不想被打断的程序段,关闭freertos所管理的中断,中断无法打断,滴答中断和PendSV中断无法进行不能实现任务调度。

 

 

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

花落指尖❀

您的认可是小浪宝宝最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值