FreeRTOS_任务管理_任务创建与删除_学习笔记

上一篇笔记

3 任务管理

3.1 任务的基本形式

任务的基本要素:1.做什么(函数)2.栈 3.优先级
一个任务的形式为:

void ATaskFunc(void *pvParameters)
{
	for(;;) //通常是一个死循环
	{
		/*任务代码*/
	}
	vTaskDelete(NULL);// 如果程序从死循环退出,需要删除自己
}

freertos.c文件由STM32CubeMX软件生成,会自动创建一个默认任务:


  void MX_FREERTOS_Init(void) {
    /* Create the thread(s) */
  	/* creation of defaultTask */
  	defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
  }

函数osThreadNew在文件cmsis_os2.c中被定义。
不同操作系统创建任务的函数不一样,为了统一处理,则创建一个统一接口层,即cmsis_os2。
osThreadNew会自动根据不同的操作系统去调用响应的任务创建函数。

3.2 创建任务

3.2.1 动态分配内存

动态分配栈和任务控制块(Task Control Block, TCB)结构体

BaseType_t xTaskCreate(
			TaskFunction_t pxTaskCode, // 函数指针
			const char * const pcName, // 任务名字
			const configSTACK_DEPTH_TYPE usStackDepth, // 栈大小,单位为word
			void * const pvParameters, // 传入参数
			UBaseType_t uxPriority, // 优先级,越小越优先
			TaskHandle_t * const pxCreateTask); // 句柄

3.2.2 静态分配内存

静态分配,需要事先指定栈和TCB结构体

TaskHandel_t xTaskCreateStatic(
			TaskFunction_t pxTaskCode, // 函数指针
			const char * const pcName, // 任务名字
			const uint32_t ulStackDepth, // 栈大小,单位为word
			void * const pvParameters, // 传入参数
			UBaseType_t uxPriority, // 优先级,越小越优先
			StackType_t * const puxStackBuffer, // 静态分配的栈,就是一个buffer
			StaticTask_t * const pxTaskBuffer); 
			// 静态分配的任务结构体(TCB)的指针, 用它来操作这个任务				

3.2.3 示例1 - 静态创建+动态创建

实现三个任务:1.静态创建任务,调用LED_Test。2.动态创建任务,调用ColorLED_Test。3.在默认任务中调用IRReceiver_Test。
IRReceiver_Test、LED_Test、ColorLED_Test是另外写好的,我们暂时不关心。
在freertos.c模板上进行修改。修改的地方均有中文注释

static StackType_t g_pucStackOfLightTask[128]; // 给光任务静态分配的栈
static StaticTask_t g_TCBofLightTask; // 给光任务静态分配的TCB结构体

osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
  .name = "defaultTask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};

void StartDefaultTask(void *argument);
void MX_FREERTOS_Init(void);

void MX_FREERTOS_Init(void) {
  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

  // 为光任务创建静态任务
  xTaskCreateStatic(Led_Test, "LightTask", 128, NULL, osPriorityNormal, g_pucStackOfLightTask, &g_TCBofLightTask);
  // 动态创建任务
  xTaskCreate(ColorLED_Test, "ColorTask", 128, NULL, osPriorityNormal, NULL);
}

void StartDefaultTask(void *argument)
{
  LCD_Init();
  LCD_Clear();
  
  for(;;)
  {
    IRReceiver_Test();
  }
}

想要创建任务,在void MX_FREERTOS_Init(void){}初始化函数中创建任务即可。
在静态创建任务时,使用了

static StackType_t g_pucStackOfLightTask[128]

来分配栈,那么如何粗略估算要分给他多大的空间呢?
函数要用到的栈空间考虑以下因素:

  1. 函数调用深度:有几级调用n,每次调用,要用到R4~R11共8个寄存器+一个LR,每个占4字节。因此栈的大小计算为n94
  2. 局部变量大小:有的函数中可能会用到很大的局部变量,此时需要将其考虑在内。
  3. 现场需要16*4=64字节

3.2.4 示例2 - 使用同一个函数,创建不同的任务(类似多态)

思路很简单,首先创建一个任务函数

void LcdTask(void * params)
{
	struct TaskPrintInfo * pInfo = params;
	uint32_t cnt = 0;
	while(1)
	{	if (g_LcdCanUse)
		{
			g_LcdCanUse = 0;//很不可靠的保护方法
			int len = LCD_PrintString(pInfo->x, pInfo->y, pInfo->name);
			len += LCD_PrintString(len, pInfo->y, ":");
			LCD_PrintSignedVal(len, pInfo->y, cnt++);
			g_LcdCanUse = 1;
		}
		mdelay(1);
	}
}

让其在指定位置显示指定字符,传入的参数创建如下:

struct TaskPrintInfo
{
	uint8_t x;
	uint8_t y; // 位置
	char name[16]; // 字符
};

然后在MX_FREERTOS_Init中使用函数创建三个任务即可。但由于LCD_PrintString耗时非常长,RTOS很有可能在期间跳出执行另一个任务,导致IIC传输中断。为了避免,添加一个全局变量来保护。
但这种保护很不可靠,更可靠的保护方法后面会介绍。
以下是完整代码:

//添加打印的信息格式
struct TaskPrintInfo
{
	uint8_t x;
	uint8_t y; // 位置
	char name[16]; // 字符
};
//创建三个任务所需要的参数
static struct TaskPrintInfo g_Task1Info = {0,0,"T1"};
static struct TaskPrintInfo g_Task2Info = {0,3,"T2"};
static struct TaskPrintInfo g_Task3Info = {0,6,"T3"};
static int g_LcdCanUse = 1; // lcd能否被使用,这是为了保护IIC不被打断。更高级的保护机制后面会讲
//添加一个任务函数
void LcdTask(void * params)
{
	struct TaskPrintInfo * pInfo = params;
	uint32_t cnt = 0;
	while(1)
	{	if (g_LcdCanUse)
		{
			g_LcdCanUse = 0;//很不可靠的保护方法
			int len = LCD_PrintString(pInfo->x, pInfo->y, pInfo->name);
			len += LCD_PrintString(len, pInfo->y, ":");
			LCD_PrintSignedVal(len, pInfo->y, cnt++);
			g_LcdCanUse = 1;
		}
		mdelay(1);
	}
}
void MX_FREERTOS_Init(void) 
{

	LCD_Init();
  	LCD_Clear();
	//使用同一个函数创建不同的任务
	xTaskCreate(LcdTask, "LCDPrint", 128, &g_Task1Info, osPriorityNormal, NULL);
	xTaskCreate(LcdTask, "LCDPrint", 128, &g_Task2Info, osPriorityNormal, NULL);
	xTaskCreate(LcdTask, "LCDPrint", 128, &g_Task3Info, osPriorityNormal, NULL);
}

3.3 删除任务

删除任务需要知道任务句柄。删除操作的函数原型为:
void vTaskDelete( TaskHandle_t xTaskToDelete); 需要传入一个任务句柄
有两种删除方式:
自杀:vTaskDelete(NULL);
他杀:vTaskDelete(pvTaskCode)

3.3.1 示例3 用遥控控制灯亮灭

osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
  .name = "defaultTask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};
void StartDefaultTask(void *argument);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
void MX_FREERTOS_Init(void) {
  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
}

void StartDefaultTask(void *argument)
{

	ColorLED_Set(0)
	
	  LCD_Init();
	  LCD_Clear();
	
  IRReceiver_Init();
	TaskHandle_t xColorLedHandle=NULL;
	LCD_PrintString(0,0,"waiting control");
  for(;;)
  {
		uint8_t dev,data;
		//读取红外遥控器
		if (!IRReceiver_Read(&dev, &data)){
			if(data == 0xa8)
			{
				if(xColorLedHandle == NULL){
					LCD_ClearLine(0, 0);
					LCD_PrintString(0, 0, "Police!Freeze!");
					xTaskCreate(ColorLED_Test, "cled", 128, NULL, osPriorityNormal, &xColorLedHandle);
				}
			}
			else if(data == 0xa2)
			{
				if(xColorLedHandle != NULL){
					LCD_ClearLine(0, 0);
					LCD_PrintString(0, 0, "You can go now");
					vTaskDelete(xColorLedHandle);
					ColorLED_Set(0);
					xColorLedHandle = NULL;
				}
			}
		}
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值