FreeRTOS内核学习高级篇-调度器使用

学习资料链接

http://wiki.csie.ncku.edu.tw/embedded/freertos
https://freertos.blog.csdn.net/article/details/51190095

介绍

调度器是FreeRTOS操作系统的核心,主要负责任务切换,即找出最高优先级的就绪任务,并使之获得CPU运行权。调度器并非自动运行的,需要人为启动它。

API函数vTaskStartScheduler()用于启动调度器

void vTaskStartScheduler( void )
{
BaseType_t xReturn;

	/* Add the idle task at the lowest priority. */
	#if ( INCLUDE_xTaskGetIdleTaskHandle == 1 )
	{
#ifdef RTL_PLACE_IDLE_STACK_IN_SRAM
		// it's same function call as original FreeRTOS source except that it use stack in SRAM
		xReturn = xTaskGenericCreate( prvIdleTask, "IDLE", tskIDLE_STACK_SIZE, ( void * ) NULL, ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), &xIdleTaskHandle, (void *)&ucIdleTaskHeap, NULL);
#else
		/* Create the idle task, storing its handle in xIdleTaskHandle so it can
		be returned by the xTaskGetIdleTaskHandle() function. */
		xReturn = xTaskCreate( prvIdleTask, "IDLE", tskIDLE_STACK_SIZE, ( void * ) NULL, ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), &xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */
#endif
	}
	#else
	{
#ifdef RTL_PLACE_IDLE_STACK_IN_SRAM
		// it's same function call as original FreeRTOS source except that it use stack in SRAM
		xReturn = xTaskGenericCreate( prvIdleTask, "IDLE", tskIDLE_STACK_SIZE, ( void * ) NULL, ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), NULL, (void *)&ucIdleTaskHeap, NULL );
#else
		/* Create the idle task without storing its handle. */
		xReturn = xTaskCreate( prvIdleTask, "IDLE", tskIDLE_STACK_SIZE, ( void * ) NULL, ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), NULL );  /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */
#endif
	}
	#endif /* INCLUDE_xTaskGetIdleTaskHandle */

	#if ( configUSE_TIMERS == 1 )
	{
		if( xReturn == pdPASS )
		{
			xReturn = xTimerCreateTimerTask();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	#endif /* configUSE_TIMERS */

	if( xReturn == pdPASS )
	{
		/* Interrupts are turned off here, to ensure a tick does not occur
		before or during the call to xPortStartScheduler().  The stacks of
		the created tasks contain a status word with interrupts switched on
		so interrupts will automatically get re-enabled when the first task
		starts to run. */
		portDISABLE_INTERRUPTS();

		#if ( configUSE_NEWLIB_REENTRANT == 1 )
		{
			/* Switch Newlib's _impure_ptr variable to point to the _reent
			structure specific to the task that will run first. */
			_impure_ptr = &( pxCurrentTCB->xNewLib_reent );
		}
		#endif /* configUSE_NEWLIB_REENTRANT */

		xSchedulerRunning = pdTRUE;
		xTickCount = ( TickType_t ) 0U;

		/* If configGENERATE_RUN_TIME_STATS is defined then the following
		macro must be defined to configure the timer/counter used to generate
		the run time counter time base. */
		portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();

		/* Setting up the timer tick is hardware specific and thus in the
		portable interface. */
		if( xPortStartScheduler() != pdFALSE )
		{
			/* Should not reach here as if the scheduler is running the
			function will not return. */
		}
		else
		{
			/* Should only reach here if a task calls xTaskEndScheduler(). */
		}
	}
	else
	{
		/* This line will only be reached if the kernel could not be started,
		because there was not enough FreeRTOS heap to create the idle task
		or the timer task. */
		configASSERT( xReturn );
	}
}

这个API函数首先创建一个空闲任务,空闲任务使用最低优先级(0级),空闲任务的任务句柄存放在静态变量xIdleTaskHandle中,可以调用API函数xTaskGetIdleTaskHandle()获得空闲任务句柄。

如果任务创建成功,则关闭中断(调度器启动结束时会再次使能中断的),初始化一些静态变量,然后调用函数xPortStartScheduler()来启动系统节拍定时器并启动第一个任务。因为设置系统节拍定时器涉及到硬件特性,因此函数xPortStartScheduler()由移植层提供,不同的硬件架构,这个函数的代码也不相同。

xPortStartScheduler()的实现

任务控制块如何运行

在这里插入图片描述

所以,SVC中断服务函数一开始就使用全局指针pxCurrentTCB获得第一个要启动的任务TCB,从而获得任务的当前堆栈栈顶指针。先将人为入栈的寄存器R4~R11出栈,将最新的堆栈栈顶指针赋值给线程堆栈指针PSP,再取消中断掩蔽。到这里,只要发生中断,就都能够被响应了。

  中断服务函数通过下面两句汇编返回。Cortex-M3架构中,r14的值决定了从异常返回的模式,这里r14最后四位按位或上0x0d,表示返回时从进程堆栈中做出栈操作、返回后进入线程模式、返回Thumb状态。

    orr r14, #0xd       
    bx r14
  执行bx  r14指令后,硬件自动将寄存器xPSR、PC、LR、R12、R3~R0出栈,这时任务A的任务函数指针vTask_A会出栈到PC指针中,从而开始执行任务A。

  至此,任务vTask_A获得CPU执行权,调度器正式开始工作。

任务切换

调度的时候从就绪表中寻找优先级最高的任务

	#define taskSELECT_HIGHEST_PRIORITY_TASK()															\
	{																									\
	    UBaseType_t uxTopPriority = uxTopReadyPriority;														\
																										\
		/* Find the highest priority queue that contains ready tasks. */								\
		while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) )							\
		{																								\
			configASSERT( uxTopPriority );																\
			--uxTopPriority;																			\
		}																								\
																										\
		/* listGET_OWNER_OF_NEXT_ENTRY indexes through the list, so the tasks of						\
		the	same priority get an equal share of the processor time. */									\
		listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) );			\
		uxTopReadyPriority = uxTopPriority;																\
	} /* taskSELECT_HIGHEST_PRIORITY_TASK */

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值