九,FreeRTOS之——空闲任务钩子函数

声明:本专栏参考韦东山,野火,正点原子以及其他博主的FreeRTOS教程,如若侵权请告知,马上删帖致歉,个人总结,如有不对,欢迎指正。

空闲任务钩子函数

转:空闲任务钩子函数详解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

注:在使空闲任务钩子函数时也会占用cpu资源,应该合理使用空闲任务钩子函数

实验一:不断创建任务

圈重点

  • 首先创建了一个任务 Flag1_Task

在这里插入图片描述

  • 任务一创建了Flag2_Task,这里注意优先级Flag1_Task优先级为1,Flag2_Task优先级为2,被创建成功后就可以抢占执行了。这里的任务一加了延时额

在这里插入图片描述
被创建的任务Flag2_Task创建成功后就自杀了(Flag1_Task:我就可劲儿救你,你就可劲儿自杀)
在这里插入图片描述
实验结果:任务会一直创建(这里我还是等了好一会儿,确保实验正确性)如果你想问我的串口为什么完整的打印了出来,没错,我加大了轮转调度的时间。在这里插入图片描述

接着实验二

实验二:不断创建任务

这里只做了一个小改变,将任务Flag1_Task中的延时函数屏蔽掉
在这里插入图片描述
来看实验结果,任务创建一段时间之后就挂掉了,原因是堆栈空间不足引起的
在这里插入图片描述
所以到这里可以得到什么结论,任务的空间清理必须由其他任务来释放,第一个实验加了延时函数,有时间去清理,第二个实验,没有延时函数,没有时间去清理。

  • 再来看看空闲任务的钩子函数

实验三:让优先级为0的任务也可以执行

现在发现了一个问题,实验二的基础上就是我在设置Flag1_Task的优先级任务为0的时候会一直创建Flag1_Task,不会失败,没有加延时函数,证明在同优先级下(优先级为0)还有一个空闲任务在暗中清理空间???

转:解释
转:解释

查阅资料后,在启动调度器时候创建了优先级为0空闲任务函数
在这里插入图片描述
来找到这个任务入口函数,看看干了什么

static portTASK_FUNCTION( prvIdleTask, pvParameters )
{
	/* Stop warnings. */
	( void ) pvParameters;

	/** THIS IS THE RTOS IDLE TASK - WHICH IS CREATED AUTOMATICALLY WHEN THE
	SCHEDULER IS STARTED. **/

	for( ;; )
	{
		/* See if any tasks have deleted themselves - if so then the idle task
		is responsible for freeing the deleted task's TCB and stack. */
		prvCheckTasksWaitingTermination();

		#if ( configUSE_PREEMPTION == 0 )
		{
			/* If we are not using preemption we keep forcing a task switch to
			see if any other task has become available.  If we are using
			preemption we don't need to do this as any task becoming available
			will automatically get the processor anyway. */
			taskYIELD();
		}
		#endif /* configUSE_PREEMPTION */

		#if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) )
		{
			/* When using preemption tasks of equal priority will be
			timesliced.  If a task that is sharing the idle priority is ready
			to run then the idle task should yield before the end of the
			timeslice.

			A critical region is not required here as we are just reading from
			the list, and an occasional incorrect value will not matter.  If
			the ready list at the idle priority contains more than one task
			then a task other than the idle task is ready to execute. */
			if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( UBaseType_t ) 1 )
			{
				taskYIELD();
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) */

		#if ( configUSE_IDLE_HOOK == 1 )
		{
			extern void vApplicationIdleHook( void );

			/* Call the user defined function from within the idle task.  This
			allows the application designer to add background functionality
			without the overhead of a separate task.
			NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES,
			CALL A FUNCTION THAT MIGHT BLOCK. */
			vApplicationIdleHook();
		}
		#endif /* configUSE_IDLE_HOOK */

		/* This conditional compilation should use inequality to 0, not equality
		to 1.  This is to ensure portSUPPRESS_TICKS_AND_SLEEP() is called when
		user defined low power mode	implementations require
		configUSE_TICKLESS_IDLE to be set to a value other than 1. */
		#if ( configUSE_TICKLESS_IDLE != 0 )
		{
		TickType_t xExpectedIdleTime;

			/* It is not desirable to suspend then resume the scheduler on
			each iteration of the idle task.  Therefore, a preliminary
			test of the expected idle time is performed without the
			scheduler suspended.  The result here is not necessarily
			valid. */
			xExpectedIdleTime = prvGetExpectedIdleTime();

			if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
			{
				vTaskSuspendAll();
				{
					/* Now the scheduler is suspended, the expected idle
					time can be sampled again, and this time its value can
					be used. */
					configASSERT( xNextTaskUnblockTime >= xTickCount );
					xExpectedIdleTime = prvGetExpectedIdleTime();

					if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
					{
						traceLOW_POWER_IDLE_BEGIN();
						portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );
						traceLOW_POWER_IDLE_END();
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				( void ) xTaskResumeAll();
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* configUSE_TICKLESS_IDLE */
	}
}

打扰了,打扰了,看不懂,大概知道提供了空的函数声明,这个函数让我们自己去写,也就是可以在这个函数里面添加功能但是切记不能做什么,这里看上面,不回顾了------这个他们把它叫做钩子函数----怎么就叫钩子函数呢,真有趣。
在这里插入图片描述

接下来就看看实验四,我们把这个空闲任务钩子函数用起来看看

实验四:空闲任务钩子函数

/**
   FreeRTOS v9.0.0 + STM32 动态创建任务
   实验平台:韦东山 STM32F103ZE开发板 
**/ 
 
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
/* 开发板硬件bsp头文件 */
#include "bsp_led.h"
#include "bsp_usart.h"

/* 任务句柄 */
static TaskHandle_t AppTaskCreate_Handle = NULL;  /* 起始任务  句柄 */
static TaskHandle_t Task1_Handle = NULL;  /* Task1 句柄*/
static TaskHandle_t Task2_Handle = NULL;  /* Task2 句柄*/


/* 函数声明 */
static void AppTaskCreate(void);/* 用于创建起始任务 */
static void Flag1_Task(void* pvParameters);/* Flag1_Task任务函数 */
static void Flag2_Task(void* pvParameters);/* Flag2_Task任务函数 */


static void BSP_Init(void);/* 用于初始化板载相关资源 */

/*****************************************************************
  * @brief  主函数
  * @param  无
  * @retval 无
  * @note   第一步:开发板硬件初始化 
            第二步:创建APP应用任务
            第三步:启动FreeRTOS,开始多任务调度
  ****************************************************************/

int main(void)
{	
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */

  /* 开发板硬件初始化 */
  BSP_Init();
  printf("这是一个STM32F103ZE开发板-FreeRTOS-队列集实验!\r\n");
	
	 /* 创建AppTaskCreate任务 */
  xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate,  /* 任务入口函数 */
                        (const char*    )"AppTaskCreate",/* 任务名字 */
                        (uint16_t       )512,  /* 任务栈大小 */
                        (void*          )NULL,/* 任务入口函数参数 */
                        (UBaseType_t    )2, /* 任务的优先级 */
                        (TaskHandle_t*  )&AppTaskCreate_Handle);/* 任务控制块指针 */ 
  /* 启动任务调度 */           
  if(pdPASS == xReturn)
    vTaskStartScheduler();   /* 启动任务,开启调度 */
  else
    return -1;  
  
  while(1);   /* 正常不会执行到这里 */    
}


/***********************************************************************
  * @ 函数名  : AppTaskCreate
  * @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
  * @ 参数    : 无  
  * @ 返回值  : 无
  **********************************************************************/
static void AppTaskCreate(void)
{
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  
  taskENTER_CRITICAL();           //进入临界区
  
  /* 创建Flag1_Task任务 */
  xReturn = xTaskCreate((TaskFunction_t )Flag1_Task, /* 任务入口函数 */
                        (const char*    )"Flag1_Task",/* 任务名字 */
                        (uint16_t       )512,   /* 任务栈大小 */
                        (void*          )NULL,	/* 任务入口函数参数 */
                        (UBaseType_t    )1,	    /* 任务的优先级 */
                        (TaskHandle_t*  )&Task1_Handle);/* 任务控制块指针 */
  if(pdPASS == xReturn)
    printf("创建Flag1_Task任务成功!\r\n");
	
//	 /* 创建Flag2_Task任务 */
//  xReturn = xTaskCreate((TaskFunction_t )Flag2_Task, /* 任务入口函数 */
//                        (const char*    )"Flag2_Task",/* 任务名字 */
//                        (uint16_t       )512,   /* 任务栈大小 */
//                        (void*          )NULL,	/* 任务入口函数参数 */
//                        (UBaseType_t    )2,	    /* 任务的优先级 */
//                        (TaskHandle_t*  )&Task2_Handle);/* 任务控制块指针 */
//  if(pdPASS == xReturn)
//    printf("创建Flag2_Task任务成功!\r\n");
	
	

  vTaskDelete(NULL); //删除AppTaskCreate任务
 // vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
  
  taskEXIT_CRITICAL();            //退出临界区
}

/**********************************************************************
  * @ 函数名  : LED_Task
  * @ 功能说明: LED_Task任务主体
  ********************************************************************/
uint8_t flag1;
uint8_t flag2;
uint8_t IdleHook;

/*任务一不断创建任务2消耗堆栈空间*/
static void Flag1_Task(void* parameter)
{
	static int  xReturn;

	 while (1)
    {
			flag1=1;
			flag2=0;
			IdleHook=0;
			xReturn = xTaskCreate(Flag2_Task, /* 任务入口函数 */
                        "Flag2_Task",   /* 任务名字 */
                        64,    /* 任务栈大小 */
                        NULL,	  /* 任务入口函数参数 */
                        2,	    /* 任务的优先级 */
                        &Task2_Handle); /* 任务控制块指针 */
    if(pdPASS == xReturn)
     printf("创建Flag2_Task任务成功!\r\n");
		else
		 printf("创建Flag2_Task任务失败!\r\n");
			
		  vTaskDelay(1);   /* 延时1个tick */
    }
}


/*任务2不断删除本身*/
static void Flag2_Task(void* parameter)
{
	
  	while (1)
    {
			  flag1=0;
			  flag2=1;
			  IdleHook=0;
			  printf("Flag2_Task running!\r\n");
			  vTaskDelete(NULL);/*自杀*/
			  printf("已经自杀了,我能打印我就诈尸了!\r\n");
			  //vTaskDelay(1);   /* 延时1个tick */
		}
}


/*空闲任务钩子函数,运行钩子函数清理垃圾空间*/
void vApplicationIdleHook( void )
{
	
	 flag1=0;
	 flag2=0;
	 IdleHook=1;
   printf("IdleHook running!\r\n");
}


/***********************************************************************
  * @ 函数名  : BSP_Init
  * @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
  * @ 参数    :   
  * @ 返回值  : 无
  *********************************************************************/
static void BSP_Init(void)
{
	/*
	 * STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
	 * 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
	 * 都统一用这个优先级分组,千万不要再分组,切忌。
	 */
	NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
	
	/* LED 初始化 */
	LED_GPIO_Config();

	/* 串口初始化	*/
	USART_Config();
  
}

/********************************END OF FILE****************************/

在使用钩子函数之前,可以看到要打开一个宏定义开关才可以使用
configUSE_IDLE_HOOK = 1 时候用钩子函数有效

运行钩子函数前提条件:

  1. 打开宏定义configUSE_IDLE_HOOK
  2. 钩子函数的任务优先级为0,你得考虑让优先级为0的任务也执行到。

不懂看当前实验源码……
在这里插入图片描述

运行效果(有点乱,但是可以看出钩子函数正在运行)
在这里插入图片描述

马上十点了,回宿舍……
git仓库源码地址:https://gitee.com/he-dejiang/free-rtos.git

  • 5
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值