本文是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上:程序下载地址