FreeRTOS学习(八)

8 任务调度与移植

基本所有在FreeRTOS的main函数中,都是在任务创建之后,后面紧接着调用一个vTaskStartScheduler()函数。这个函数就是用来开启任务调度器的,在tasks.c文件中有函数的源码定义。

  • 8.1 任务调度开启函数

      void vTaskStartScheduler( void )
      {
         BaseType_t xReturn;
         xReturn = xTaskCreate( prvIdleTask, "IDLE", 
         					   usIdleTaskStackSize, 
         					   ( void * ) NULL,
         					   (tskIDLE_PRIORITY|portPRIVILEGE_BIT), &xIdleTaskHandle ); ················· ①
      	#if ( configUSE_TIMERS == 1 )
      	{
      		if( xReturn == pdPASS )
      		{
      			xReturn = xTimerCreateTimerTask();················②
      		}
      		else
      		{
      			mtCOVERAGE_TEST_MARKER();
      		}
      	}                      
      	if( xReturn == pdPASS )
      	{
      		portDISABLE_INTERRUPTS();·······················③
      
      		#if ( configUSE_NEWLIB_REENTRANT == 1 )
      		{
      		    _impure_ptr = &( pxCurrentTCB->xNewLib_reent );
      		}
      		xNextTaskUnblockTime = portMAX_DELAY;
      		xSchedulerRunning = pdTRUE; ························④
      		xTickCount = ( TickType_t ) 0U; 
      
      		if( xPortStartScheduler() != pdFALSE )···················⑤
      		{
      				//如果调度器启动成功的话,是不会运行到这里的,函数 不会有返回值的	
      		}
      		else
      		{
      			//除非调用vTaskEndScheduler(),否则不会运行到这里
      		}
      	}
      	else
      	{
      	    //内核没有启动成功才会运行到这里,
      	    //往往是因为创建空闲任务或者定时器任务的时候
      	    //没有足够的内存configASSERT( xReturn );
      	}
      	//防止编译报错
      	( void ) xIdleTaskHandle;
      }
    

①创建空闲任务(动态创建)
②创建定时器服务
③关闭中断
④ xSchedulerRunning默认为pdFALSE,这里设为pdTRUE表示调度开始运行,别的地方会以此变量当做标志位来进行操作
⑤调用函数xPortStartScheduler()来初始化跟调度器启动有关的硬件

  • 8.2 FreeRTOS移植相关函数
    特别要注意的是:
    xPortStartScheduler() 的函数所涉及的都是移植相关的东西,要移植的文件port.c很多也都是为它服务的。
    xPortStartScheduler函数原型:
    在这里插入图片描述
    prvSetupTimerInterrupt() 函数是用来设置滴答定时器的定时周期,并且使能滴答定时器的中断。我们所移植的代码中,这个函数提供的是一个滴答定时器的初始化接口函数,也就是vPortSystick_Init()。
    uxCriticalNesting 表示临界区的嵌套层数,在初始化的时候将它的值设置为0,没有什么代码进入临界区。
    vPortStartFirstTask() 就是触发一次TRAP 1中断,TRAP 1中断也就是执行一个汇编函数portRESTORE_CONTEXT();

这里先来看看vPortSystick_Init函数的使用, vPortSystick_Init函数中的代码就是进行3个初始化,两个TRAP和滴答定时器的初始化。裁剪后的代码,如下图所示:
在这里插入图片描述

  • 8.2.1 滴答定时器的初始化
    vPortSystick_Init() 函数本身就是为了初始化滴答定时器,在初始化的时候调用了systick_init()函数,同时传递了两个参数ulClkLoadCnt、xPortSysTickHandler。看到systick_init()函数就会知道这两个值的作用:
    在这里插入图片描述
    ulClkLoadCnt是滴答定时器每次重新加载的值,由这个值减到会是一个周期,触发一次滴答中断,运行xPortSysTickHandler函数。ulClkLoadCnt在vPortSystick_Init函数中计算出来的值,正好是每1ms进入一次中断,也就是说系统滴答的周期是1ms。在xPortSysTickHandler函数中会将滴答的计数值进行加一。

  • 8.2.2 TRAP函数的使用
    在vPortSystick_Ini() 函数中还初始化了两个TRAP,在移植中这两个TRAP的使用也是非常关键的。TRAP1中断就是调用portRESTORE_CONTEXT这个宏函数来进行恢复下文。触发TRAP0中断最终会调用vPortYieldHandler函数,这是一个进行上下文切换的函数,代码如下:
    在这里插入图片描述
    这个函数一共就三行,功能很明确。保存上文,选择要切换的下文,进行切换。
    在这里插入图片描述
    在进入中断函数的时候,就会将一部分寄存去入栈,这里通过一些减法操作将剩下的保存uxCriticalNesting、EPSR、EPC的寄存器也入栈,在我们切换的时候,需要将之前任务这些寄存器都保存起来。

在执行完vTaskSwitchContext函数之后,下一个要执行任务的TCB会被保存在pxCurrentTCB中。这里的代码将要运行的下一个任务的堆栈进行出栈操作,实现的是将下个要运行的任务进行出栈的操作。
vTaskSwitchContext函数会将处于就绪状态的最高优先级的任务选中,将它的TCB保存在pxCurrentTCB中,代码简化后如下:
在这里插入图片描述
这里的if判断语句,因为如果调度器挂起就不能进行切换。调用函数taskSELECT_HIGHEST_PRIORITY_TASK()获得下一个要运行的任务。
选择完要切换的任务,在这里进行切换:
在这里插入图片描述
这里进行的主要是将下一个要运行的任务进行一些出栈的操作,将相应的寄存器出栈,切换到下文。

  • 8.2.3 任务栈的初始化
    我们在进行任务创建的时候,会在我们的移植层对创建的任务进行栈的初始化:
    在这里插入图片描述
    1:栈顶为空
    2:然后是R15存放我们的PC指针,
    3:将R13-R1都初始化为0,这里主要是为了将这些寄存器预留出来,以方便后面我们进行使用,
    4:R0初始化为pvParameters,作为任务的输入参数,通常函数会调用R0~R3作为输入参数,R0也可以用作返回结果,如果返回值是64位,则R1也会用作返回结果。
    5:然后是临界嵌套的计数值
    6:寄存器xPSR,这里是用作EPSR寄存器在遇到异常情况时被用来保存当前处理器执行的内容。
    7:最后是EPC,异常保留程序计数器的存储
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值