FreeRTOS学习笔记--------任务管理

Tick中断

在默认调度算法下,对于同优先级的任务,它们轮流执行,轮流的时间基准由tick中断决定,由定时器产生固定间隔的中断。修改FreeRTOSConfig.h文件中的:

#define configTICK_RATE_HZ			( ( TickType_t ) 1000 )

可以修改中断产生的时间间隔。1000Hz表示间隔为1ms。实验结果如下图所示:

任务状态与转换

我们可以简单把任务状态分为运行和非运行,其中非运行可以分为

  • 阻塞状态(blocked)
  • 暂停状态(suspended)
  • 就绪状态(ready)

完整的状态转换图如下所示

由上图可知,进入运行状态,必须先处于就绪状态。

阻塞状态与暂停状态的区别在于,进入阻塞状态后需要等待某件事件发生才能退出,而进入暂停状态后需要由其他任务调用vTaskResume()函数才能退出。

在FreeRTOS中只能通过调用以下函数进入暂停状态:

void vTaskSuspend( TaskHandle_t xTaskToSuspend );

参数xTaskToSuspend表示要暂停的任务,如果是NULL,则表示暂停自己。 退出必须通过其他任务调用vTaskResume函数或者中断调用xtaskResumeFromISR函数。并且由官方源码可知,退出暂停状态的函数参数不能为NULL。

任务状态实验

创建三个优先级相同的任务进行实验,部分代码如下:

void vmainTask1Function( void * param)
{
	UBaseType_t flag = 0;
	TickType_t t = 0;
	TickType_t start = xTaskGetTickCount();
	while(1) {
		t = xTaskGetTickCount();
	  task1Run = 1;
	  task2Run = 0;
		task3Run = 0;
		printf("1");
		
		if(!flag && (t > start + 10)) {
			flag = 1;
			vTaskSuspend(xTask3Handle);
		}
		else if(t > start + 20) {
			vTaskResume(xTask3Handle);
		}
	}
}

void vmainTask2Function( void * param)
{
	while(1) {
	  task1Run = 0;
	  task2Run = 1;
		task3Run = 0;
		printf("2");
		vTaskDelay(pdMS_TO_TICKS(10));
	}
}

void vmainTask3Function( void * param)
{
	int num = (int)(param);
	while(1) {
	  task1Run = 0;
	  task2Run = 0;
		task3Run = 1;
		printf("%d", num);
	}
}

实验现象如下图所示:

空闲任务及其钩子函数

空闲任务由任务调度器vTaskStartScheduler函数创建,优先级为0,任务状态只有就绪状态和运行状态(由空闲任务的作用决定),如果处于阻塞状态或者暂停状态,程序将会崩溃。创建空闲任务的官方源码如下所示:

xIdleTaskHandle = xTaskCreateStatic( prvIdleTask,
                                     configIDLE_TASK_NAME,
                                     ulIdleTaskStackSize,
                                     ( void * ) NULL,       /*lint !e961.  The cast is not redundant for all compilers. */
                                     portPRIVILEGE_BIT,     /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */
                                     pxIdleTaskStackBuffer,
                                     pxIdleTaskTCBBuffer ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */


xReturn = xTaskCreate( prvIdleTask,
                       configIDLE_TASK_NAME,
                       configMINIMAL_STACK_SIZE,
                       ( void * ) NULL,
                       portPRIVILEGE_BIT,  /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */
                       &xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */

 portPRIVILEGE_BIT表示优先级,其值为0。

空闲任务的作用之一释放被删除任务的内存(参考文章:FreeRTOS学习笔记-------任务创建与删除),如果希望空闲任务实现此功能,其必须得到执行。

空闲任务每执行一次,就会执行一次钩子函数,钩子函数的作用包括:

  • 执行一些后台的、需要连续执行的函数
  • 测量系统的空闲时间
  • 让系统进入省电模式

根据官方源码可知,如果使用钩子函数(在使用的文件中实现vApplicationIdleHook函数),需要在FreeRTOSConfig.h中配置configUSE_IDLE_HOOK。

        #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 */

任务调度算法

调度算法就是确定哪个处于就绪状态的任务可以切换成运行状态。调度算法的行为主要体现在两方面:高优先级的任务先运行、同优先级的就绪态任务如何被选中。调度算法要确保同优先级的就绪态任务,能"轮流"运行,策略是"轮转调度"(Round Robin Scheduling)。轮转调度并不保证任务的运行时间是公平分配的,我们还可以细化时间的分配方法。

在FreeRTOS.h和FreeRTOSConfig.h中,官方源码如下:

#ifndef configUSE_PREEMPTION
    #error Missing definition:  configUSE_PREEMPTION must be defined in FreeRTOSConfig.h as either 1 or 0.  See the Configuration section of the FreeRTOS API documentation for details.
#endif

............

#ifndef configIDLE_SHOULD_YIELD
    #define configIDLE_SHOULD_YIELD    1
#endif

............

#ifndef configUSE_TIME_SLICING
    #define configUSE_TIME_SLICING    1
#endif



#define configUSE_PREEMPTION		  1

由官方源码可知,默认调度算法是高优先级的任务可以先执行(抢占,configUSE_PREEMPTION),同优先级的任务轮流执行(时间片轮转,configUSE_TIME_SLICING),空闲任务让步于用户任务(configIDLE_SHOULD_YIELD)。

对于空闲任务让步于用户任务的官方源码如下:

        #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 ) ) */

 configIDLE_SHOULD_YIELD分别为1和0的实验现象如下:

若修改调度算法,主要通过修改FreeRTOSConfig.h中的configUSE_PREEMPTION、configUSE_TIME_SLICING、configIDLE_SHOULD_YIELD选项,0表示不允许,1表示允许。

有关具体的任务调度分析文章:FreeRTOS学习笔记------任务调度机制-CSDN博客

  • 19
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值