FreeRTOS时间片调度

一、FreeRTOS时间片调度概述

FreeRTOS支持多个任务同时拥有一个优先级,这些任务的调度就可以使用时间片来进行调度。在FreeRTOS中允许一个任务允许一个时间片(一个时钟节拍的长度)后让出CPU的使用权,让拥有同优先级的下个任务运行。下图展示了运行在同一优先级的执行时间图。其中的task1、task2、task3是同一优先级N就绪的任务
在这里插入图片描述

  • 1、任务3正在运行。
  • 2、这时一个时钟节拍中断(滴答定时器中断)发生,任务3的时间片用完,但是任务3还没有执行完。
  • 3、FreeRTOS将任务切换到任务1,任务1是优先级N下的下一个就绪任务。
  • 4、任务1连续运行至时间片用完。
  • 5、任务3再次获取到CPU使用权,接着运行。
  • 6、任务3运行完成,调用任务切换函数portYIELD()强行进行任务切换放弃剩余的时间片,从而使优先级N下的下一个就绪的任务运行。
  • 7、FreeRTOS切换到任务1。
  • 8、任务1执行完其时间片。

二、开启时间片调度

要使用时间片调度的话宏configUSE_PREEMPTION和宏configUSE_TIME_SLICING必须为1。时间片的长度由宏configTICK_RATE_HZ来确定,一个时间片的长度就是滴答定时器的
中断周期,比如configTICK_RATE_HZ为1000,那么一个时间片的长度就是1ms。时间片调度发生在滴答定时器的中断服务函数中,前面讲解滴答定时器中断服务函数的时候说了在中断服务函数SysTick_Handler()中会调用FreeRTOS的API函数xPortSysTickHandler(),而函数xPortSysTickHandler()会引发任务调度,但是这个任务调度是有条件的,函数xPortSysTickHandler()如下:

void xPortSysTickHandler( void )
{
	vPortRaiseBASEPRI();
	{
		/* Increment the RTOS tick. */
		if( xTaskIncrementTick() != pdFALSE )
		{
			portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
		}
	}
	vPortClearBASEPRIFromISR();
}

上述代码中只有函数xTaskIncrementTick()的返回值不为pdFALSE的时候就会进行任务调度!其中xTaskIncrementTick()函数代码如下:

BaseType_t xTaskIncrementTick( void )
{
TCB_t * pxTCB;
TickType_t xItemValue;
BaseType_t xSwitchRequired = pdFALSE;
	traceTASK_INCREMENT_TICK( xTickCount );
	if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
	{

		#if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) )                                                      (1)
		{
			if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 )           (2)
			{
				xSwitchRequired = pdTRUE;                      (3)
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */

		#if ( configUSE_TICK_HOOK == 1 )
		{
	
			if( uxPendedTicks == ( UBaseType_t ) 0U )
			{
				vApplicationTickHook();
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* configUSE_TICK_HOOK */
	}
	else
	{
		++uxPendedTicks;

		/* The tick hook gets called at regular intervals, even if the
		scheduler is locked. */
		#if ( configUSE_TICK_HOOK == 1 )
		{
			vApplicationTickHook();
		}
		#endif
	}

	#if ( configUSE_PREEMPTION == 1 )
	{
		if( xYieldPending != pdFALSE )
		{
			xSwitchRequired = pdTRUE;
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	#endif /* configUSE_PREEMPTION */

	return xSwitchRequired;
}
  • (1)、当宏configUSE_PREEMPTION和宏configUSE_PREEMPTION都为1的时候下面的代码才会编译。所以要想使用时间片调度的话这这两个宏都必须为1,缺一不可!
  • (2)、判断当前任务所对应的优先级下是否还有其他的任务。
  • (3)、如果当前任务所对应的任务优先级下还有其他的任务那么就返回pdTRUE。从上面的代码可以看出,如果当前任务所对应的优先级下有其他的任务存在,那么函数xTaskIncrementTick0就会返回pdTURE,由于函数返回值为pdTURE,因此函数xPortSysTickHandler()就会进行一次任务切换。
    三、实验程序展示
int main(void)
{ 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
	delay_init(168);		//初始化延时函数
	uart_init(115200);     	//初始化串口
	LED_Init();		        //初始化LED端口
	LCD_Init();
	POINT_COLOR = RED;
	LCD_ShowString(30,10,200,16,16,"LIU YI NIU BI");
	LCD_ShowString(30,40,200,16,16,"2021/10/18");
	//创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}
//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
    //创建task1任务
    xTaskCreate((TaskFunction_t )task1_task,     	
                (const char*    )"task1_task",   	
                (uint16_t       )TASK1_STK_SIZE, 
                (void*          )NULL,				
                (UBaseType_t    )TASK1_TASK_PRIO,	
                (TaskHandle_t*  )&TASK1Task_Handler);   
    //创建LED1任务
    xTaskCreate((TaskFunction_t )task2_task,     
                (const char*    )"task2_task",   
                (uint16_t       )TASK2_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )TASK2_TASK_PRIO,
                (TaskHandle_t*  )&TASK2Task_Handler);        
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
}
//TASK1任务函数
void task1_task(void *pvParameters){
	u8 task1_num=0;
	while(1){
		task1_num++;
		LED0=!LED0;
		taskENTER_CRITICAL();//进入临界区
		printf("任务1已经执行:%d次\r\n",task1_num);
		taskEXIT_CRITICAL();//退出临界区
//		delay_xms(10);
	
	}
}
//TASK2任务函数
void task2_task(void *pvParameters){
	u8 task2_num=0;
	while(1){
		task2_num++;
		LED1=!LED1;
		taskENTER_CRITICAL();//进入临界区
		printf("任务2已经执行:%d次\r\n",task2_num);
		taskEXIT_CRITICAL();//退出临界区
//		delay_xms(10);
	
	}
}

通过串口调试助手可以看到打印的信息,就知道执行的过程。在这里插入图片描述
不管是task1_task还是task2_task都是连续执行4、5次,和前面程序设计的一样,说明在一个时间片内一直在运行一个任务,当时间片用完后就切换到下一个任务运行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

留小乙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值