FreeRTOS系列|任务挂起和恢复

1. 任务挂起和恢复API函数
  • vTaskSuspend()函数:将任务置于挂起状态
void vTaskSuspend(TaskHandle_t xTaskToSuspend)
参    数:xTaskToSuspend 要挂起的任务的任务句柄
返 回 值:无

  • vTaskResume()函数:将任务从挂起状态恢复到就绪态
void vTaskResume(TaskHandle_t xTaskToResume)
参    数:xTaskToResume 要恢复的任务的任务句柄
返 回 值:无

2. 任务挂起和恢复函数源码分析
  • 任务挂起函数源码分析
void vTaskSuspend(TaskHandle_t xTaskToSuspend){
	TCB_t *pxTCB;
	/* 进入临界段 */
	taskENTER_CRITICAL();
	{
		/* 获取任务控制块,若为NULL则挂起自身 */
		pxTCB = prvGetTCBFromHandle(xTaskToSuspend);
		/* 将任务从就绪列表中移除 */
		if(uxListRemove(&(pxTCB->xStateListItem)) == (UBaseType_t)0){
			taskRESET_READY_PRIORITY(pxTCB->uxPriority);
		}
		else{
			mtCOVERAGE_TEST_MARKER();
		}
		/* 查看任务是否在等待某个事件,如是则将其从事件列表中移除 */
		if(listLIST_ITEM_CONTAINER(&(pxTCB->xEventListItem))!=NULL){
			(void) uxListRemove(&(pxTCB->xEventListItem));
		}
		else{
			mtCOVERAGE_TEST_MARKER();
		}
		/* 将任务添加到挂起任务列表表尾 */
		vListInsertEnd(&xSuspendedTaskList, &(pxTCB->xStateListItem));
	}
	/* 退出临界段 */
	taskEXIT_CRITICAL();
	
	if(xSchedulerRunning != pdFALSE){	//判断调度器是否开启
		/* 重新计算还要多长时间执行下一个任务 */
		taskENTER_CRITICAL();
		{
			prvResetNextTaskUnblockTime();
		}
		taskEXIT_CRITICAL();
	}
	else{
		mtCOVERAGE_TEST_MARKER();
	}

	if(pxTCB == pxCurrentTCB){
		if(xSchedulerRunning != pdFALSE){
			/* 若刚挂起的是正在运行的任务,且任务调度器运行正常,则强制进行一次任务切换 */
			configASSERT( uxSchedulerSuspended == 0 );
			portYIELD_WITHIN_API();
		}
		else{
			/* 若任务调度器没有开启,则读取当前任务挂起列表的长度,判断所有任务是否都被挂起*/
			if(listCURRENT_LIST_LENGTH(&xSuspendedTaskList) == uxCurrentNumberOfTasks){
				/* 若所有任务都被挂起,把当前的任务控制块赋值为NULL	*/
				pxCurrentTCB = NULL;
			}
			else{
				/* 若还有没被挂起的任务,则获取下一个要运行的任务 */
				vTaskSwitchContext();
			}
		}
	}
	else{
		mtCOVERAGE_TEST_MARKER();
	}
}

  • 任务恢复函数源码分析
void vTaskResume(TaskHandle_t xTaskToResume){
	/* 获取要恢复的任务控制块 */
	TCB_t * const pxTCB = (TCB_t *) xTaskToResume;
	configASSERT( xTaskToResume );

	/* 任务控制块不能为NULL和当前任务	*/
	if(( pxTCB != NULL ) && ( pxTCB != pxCurrentTCB )){
		/* 进入临界段 */
		taskENTER_CRITICAL();
		{
			/* 判断任务是否被挂起 */
			if(prvTaskIsTaskSuspended(pxTCB) != pdFALSE){
				/* 从挂起列表中移除 */
				(void) uxListRemove(&( pxTCB->xStateListItem));
				/* 添加到就绪列表中 */
				prvAddTaskToReadyList( pxTCB );
				/* 要恢复的任务优先级高于当前正在运行的任务优先级 */
				if(pxTCB->uxPriority >= pxCurrentTCB->uxPriority){
					/* 完成一次任务切换 */
					taskYIELD_IF_USING_PREEMPTION();
				}
				else{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		/* 退出临界段 */
		taskEXIT_CRITICAL();
	}
	else{
		mtCOVERAGE_TEST_MARKER();
	}
}

3. 任务挂起和恢复函数实例

使用STM32CubeMX可以非常方便的将FreeRTOS移植到工程中。本实验就是利用STM32CubeMX移植FreeRTOS,并创建三个任务:

Led_Task:D2指示灯闪烁
Usart_Task:每隔1s向串口输出字符串
Key_Task:按下K_UP,挂起Led_Task任务;按下K_DOWN,恢复Led_Task任务

3.1 STM32CubeMX设置
  • RCC设置外接HSE,时钟设置为72M
  • PC1设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平
  • PA0设置为GPIO输入模式、下拉模式;PE2/PE3/PE4设置为GPIO输入模式、上拉模式
  • USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位
  • 激活FreeRTOS,添加任务,设置任务名称、优先级、堆栈大小、函数名称等参数
    在这里插入图片描述在这里插入图片描述
  • 使用FreeRTOS操作系统,一定要将HAL库的Timebase Source从SysTick改为其他定时器,选好定时器后,系统会自动配置TIM
    在这里插入图片描述
  • 输入工程名,选择路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码
3.2 MDK-ARM软件编程
  • 创建按键驱动文件key.c和key.h,参考按键输入例程
  • 添加Led_Task、Usart_Task和Key_Task任务函数代码
void Led_Task(void const * argument){
  /* USER CODE BEGIN Led_Task */
  /* Infinite loop */
  for(;;){
	HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_RESET);
    osDelay(500);  //1ms时基
	HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_SET);
    osDelay(500);  //1ms时基
  }
  /* USER CODE END Led_Task */
}

void Usart_Task(void const * argument){
  /* USER CODE BEGIN Usart_Task */
  /* Infinite loop */
  for(;;){
	printf("UsartTask is Runing!\r\n");
    osDelay(1000);
  }
  /* USER CODE END Usart_Task */
}

void KeyTask(void const * argument){
  /* USER CODE BEGIN KeyTask */
	uint8_t key = 0;	
  /* Infinite loop */
  for(;;){
    key = KEY_Scan(0);	
	switch(key){
		case KEY_UP_PRES:
			vTaskSuspend(Led_TaskHandle);
			printf("Led_Task被挂起!\r\n");
			key = 0;
			break;
		case KEY_DOWN_PRES:
			vTaskResume(Led_TaskHandle);
			printf("Led_Task被恢复!\r\n");
			key = 0;
			break;
	}
	osDelay(10);
  }
  /* USER CODE END KeyTask */
}

3.3 下载验证

编译无误下载到开发板后,D2指示灯闪烁表示程序正常运行。打开串口调试助手,可以看到串口每隔1s输出相应字符;按下K_UP按键,Led_Task被挂起,D2指示灯停止闪烁,按下K_DOWN按键,Led_Task任务恢复,D2指示灯开始闪烁
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实验报告三:(1)设计 Task0、Task1 两个任务任务 Task0 不断地挂起自己,再被任务 Task1 解挂,两个任务不断地切换执行,并输出两个任务在各个时刻的状态。Task0 的主要功能是显示 Task1 的状态,Task1 的主要功能是显示 Task0 的状态。整个应用的运行流程如图 1 所示,其描述如下:在 main 函数创建起始任务 TaskStart,其优先级为 0。TaskStart 任务主要完成创建 2 个应用任务 Task0、Task1,并挂起自己(不再被其它任务唤醒)。之后整个系统的运行流程如下:t1 时刻,Task0 开始执行,它运行到 t2 时刻挂起自己;t2 时刻,系统调度处于就绪状态的优先级最高任务 Task1 执行,它在 t3 时刻唤醒 Task0,后者由于优先级较高而抢占 CPU;Task0 执行到 t4 时刻又挂起自己,内核调度 Task1执行;Task1 运行至 t5 时刻再度唤醒 Task0; 注意:图中的栅格并不代表严格的时间刻度,而仅仅表现各任务启动和执行的相对先后关系。 (2)设计 MyTask、YouTask、KeyTask 三个任务:MyTask 任务输出 M;YouTask 任务输出 Y,并输出 MyTask 任务的状态;KeyTask 任务从键盘接收字符 Y 或 N,当接收 Y 时挂起 MyTask 任务,当接收 N 时恢复 MyTask 任务。 (3)设计 KeyTask 任务,当从键盘输入+号时动态创建任务,最多可以创建 10 个任务,这 10个任务都执行一个函数 MyTask,要求优先级是(PRIO_BASE+0,1,2,3,4,5,6,7,8,9),还要向不同的任务传递不同的参数(0,1,2,3,4,5,6,7,8,9)给 MyTask 函数,优先级为(PRIO_BASE+0,1,2,3,4,5,6,7,8,9)的任务分别输出数字(0,1,2,3,4,5,6,7,8,9)。当从键盘输入-号时动态的删除刚创建的一个任务,最多可删除 10 个任务。提示:多个任务可以执行一个函数。运行结果如下图所示。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值