本文是FreeRTOS复习笔记的第二节,创建和删除任务,使用的开发板是stm32f407VET6,创建两个任务,task1负责闪烁LED,task2负责按键控制,当按键按下时task1停止执行,任务的状态显示到TFT显示屏和串口上。

上一篇文章:【复习笔记】FreeRTOS(一)

文章目录


一、实验目的

本节的学习目的学习FreeRTOS创建任务和删除任务,任务创建和删除本质就是调用FreeRTOS的API函数。
任务创建和删除主要用到3个API函数:

函数 作用
xTaskCreate() 使用动态的方法创建一个任务。所需的内存均由FreeRTOS从FreeRTOS管理的堆中分配,不需要人为操作
xTaskCreateStatic() 使用静态的方法创建一个任务,任务的任务控制块以及任务的栈空间所需的内存需要用户分配提供
xTaskCreateRestricted() 创建一个使用MPU任务进行限制的任务 ,相关内存使用动态内存分配
vTaskDelete() 删除一个任务

这里主要比较一下动态创建任务和静态创建任务
动态创建任务函数包含了6个参数:

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

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

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

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

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

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

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

静态创建任务函数包含了7个参数:

TaskHandle_t xTaskCreateStatic
(
    	TaskFunction_t		    pxTaskCode,			/* 1.指向任务函数的指针 */
    	const char * const		pcName,				/* 2.任务函数名 */
    	const uint32_t			ulStackDepth, 		/* 3.任务堆栈大小注意字为单位 */
    	void * const			pvParameters, 		/* 4.传递的任务函数参数 */
    	UBaseType_t			    uxPriority, 		/* 5.任务优先级 */
    	StackType_t * const		puxStackBuffer, 	/* 6.任务堆栈,一般为数组,由用户分配 */
    	StaticTask_t * const	pxTaskBuffer	    /* 7.任务控制块指针,由用户分配 */
); 	

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

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

然后讲一下任务删除函数:

void vTaskDelete(TaskHandle_t xTaskToDelete);

任务删除函数只有一个入口参数 xTaskToDelete:待删除任务的任务句柄。此函数用于删除已经被创建成功的任务,如果没有被创建,是不能被删除的。被删除的任务将从就绪态任务列表、阻塞态任务列表、 挂起态任务列表和事件列表中移除。

当传入的参数为NULL,则代表删除任务自身(当前正在运行的任务)。比如说创建了两个任务task1、task2,在task1的任务函数中调用的任务删除,传入的参数为task2的任务句柄,则删除task2这个任务,如果传入NULL参数,则删除自身task1任务。空闲任务会负责释放被删除任务中被系统分配的内存。删除自身任务,即函数xTaskDelete()传入NULL时,才会利用空闲任务释放掉被删除任务使用的系统分配的内存,如果是在task1任务中删除task2,则在task1中释放task2所使用的内存空间。

本实验使用的开发板是stm32f407VET6最小系统板,搭载1.44寸TFT。使用动态创建任务的方法创建两个任务,task1负责闪烁LED,task2负责按键控制,当按键按下时task1停止执行,任务的状态显示到TFT显示屏和串口上。

二、测试例程

程序上主要用到xTaskCreate()函数,使用动态的方法创建任务,以及使用vTaskDelete()删除任务。程序上主要是创建两个任务,task1负责闪烁LED,task2负责按键控制,当按键按下时删除任务task1,任务的状态显示到TFT显示屏和串口上。
主函数 main.c代码如下:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "timer.h"
#include "key.h"
#include "GUI.h"
#include "Lcd_Driver.h"
#include "TFT_demo.h"
#include "FreeRTOS.h"
#include "task.h"

#define START_TASK_PRIO		1 //任务优先级	
#define START_STK_SIZE 		128  //任务堆栈大小
TaskHandle_t StartTask_Handler; //任务句柄
void start_task(void *pvParameters); //任务函数

#define TASK1_TASK_PRIO		2 //任务优先级
#define TASK1_STK_SIZE 		128  //任务堆栈大小	
TaskHandle_t TASK1Task_Handler; //任务句柄
void task1_task(void *p_arg); //任务函数

#define TASK2_TASK_PRIO		3 //任务优先级
#define TASK2_STK_SIZE 		128  //任务堆栈大小	
TaskHandle_t TASK2Task_Handler; //任务句柄
void task2_task(void *p_arg); //任务函数

int main(void)
{ 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
	delay_init(168);		//初始化延时函数
	uart_init(115200);     	//初始化串口
	LED_Init();		        //初始化LED端口
	KEY_Init();
	Lcd_Init();	 //1.44寸液晶屏--初始化配置
	Lcd_Clear(GRAY0);//清屏
	//创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}
 

/**
  * @brief  开始任务任务函数
  * @param  None
  * @retval None  
  */
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区

    xTaskCreate((TaskFunction_t )task1_task,     
                (const char*    )"task1_task",   
                (uint16_t       )TASK1_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )TASK1_TASK_PRIO,
                (TaskHandle_t*  )&TASK1Task_Handler);  

	xTaskCreate((TaskFunction_t )task2_task,     
                (const char*    )"task2_task",   
                (uint16_t       )TASK2_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )TASK2_TASK_PRIO,
                (TaskHandle_t*  )&TASK2Task_Handler);  
				
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
}


/**
  * @brief  显示任务1运行,而且LED 0.5s闪烁
  * @param  None
  * @retval None  
  */
void task1_task(void *pvParameters)
{
	Lcd_Clear(GRAY0);

	while(1)
	{
		printf("任务1运行中\r\n");
		Gui_DrawFont_GBK16(6,8,BLUE,BLUE,"task1:runing");
		LED0=~LED0;
        vTaskDelay(500);
    }
	
}	
 
/**
  * @brief  按键按下就删除任务1的执行程序,led停止闪烁
  * @param  None
  * @retval None  
  */
void task2_task(void *pvParameters)
{

	u8 key1;
	Lcd_Clear(GRAY0);
	while(1)
	{
		   key1=KEY_Scan(0);
		if(key1==WKUP_PRES)
		{
            vTaskDelete(TASK1Task_Handler);//任务2删除任务1
			printf("任务1删除了任务2!\r\n");
    	    Gui_DrawFont_GBK16(6,8,BLUE,GRAY0,"task1:stopping");
			Gui_DrawFont_GBK16(0,38,RED,GRAY0,"task2 Delete 1");
		}
		vTaskDelay(500);    
    }
}

按键驱动函数,key.c

#include "key.h"
#include "delay.h" 

/**
  * @brief  按键初始化函数,设置PA0为按键接口,按下就通过电阻接3.3上拉
  * @param  None
  * @retval None  
  */
void KEY_Init(void)
{
	
	GPIO_InitTypeDef  GPIO_InitStructure;
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA,时钟 

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//WK_UP对应引脚PA0--K1按键
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输入模式
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100M
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN ;//下拉
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA0
 
} 

/**
  * @brief  按键处理函数,返回按键值
  * @param  mode:0,不支持连续按;1,支持连续按;
  * @retval 0,没有任何按键按下
  * @retval 1,WKUP按下 --对应K1按键
  */
u8 KEY_Scan(u8 mode)
{	 
	static u8 key_up=1;//按键按松开标志
	if(mode)key_up=1;  //支持连按		  
	if(key_up&&(WK_UP==1))
	{
		delay_ms(10);//去抖动 
		key_up=0;
		if(WK_UP==1) return WKUP_PRES;
	}
	else if(WK_UP==0)key_up=1; 	    
 	return 0;// 无按键按下
}

三、实验效果

实验效果如下:

首先是TFT显示屏上显示“task1:running1”,并且LED引脚电平500ms改变一次,一闪一闪的。当按下按键时,LED停止闪烁,显示屏显示“task1:stopping”下行显示“task2 Delete task1”。如果接上串口,就能看到提示内容。

好了,本节主要是学习和掌握任务创建以及 vTaskDelete() 任务删除函数的使用。
完整程序放在gitee上:程序下载地址