STM32移植FreeRTOS系列十四:时间片调度实验(文末附代码)

目录

1、实验原理

2、时间片调度实验

1、实验目的

2、实验设计

3、实验程序与分析

4、程序运行结果分析


基于STM32F103RCT6

1、实验原理

FreeRTOS 支持多个任务同时拥有一个优先级,一个任务运行一个时间片(一个时 钟节拍的长度)后让出 CPU 的使用权,让拥有同优先级的下一个任务运行。FreeRTOS 中的这种调度方法就是时间片调度。

图 9.6.1 展示了运行在同一优先级下的执行时间图,在优先级 N 下有 3 个就绪的任务。

1、任务 3 正在运行。

2、这时一个时钟节拍中断(滴答定时器中断)发生,任务 3 的时间片用完,但是任务 3 还 没有执行完。

3、FreeRTOS 将任务切换到任务 1,任务 1 是优先级 N 下的下一个就绪任务。

4、任务 1 连续运行至时间片用完。

5、任务 3 再次获取到 CPU 使用权,接着运行。

6、任务 3 运行完成,调用任务切换函数 portYIELD()强行进行任务切换放弃剩余的时间片, 从而使优先级 N 下的下一个就绪的任务运行。

7、FreeRTOS 切换到任务 1。

8、任务 1 执行完其时间片。

1、同等优先级任务,轮流执行;时间片流转

2、一个时间片大小,取决为滴答定时器中断频率

3、注意没有用完的时间片不会再使用,下次任务Task3得到执行还是按照一个时间片的时钟节拍运行

要使用时间片调度的话宏 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();
    {
        if( xTaskIncrementTick() != pdFALSE )
        {
            portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
        }
    }
    vPortClearBASEPRIFromISR();
}

查看函数 xTaskIncrementTick()会发现有如下条件编译语句:

BaseType_t xTaskIncrementTick(void)
{
	TCB_t *pxTCB;
	TickType_t xItemValue;
	BaseType_t xSwitchRequired = pdFALSE;
	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 ) )
}
return xSwitchRequired;
}

(1)、当宏 configUSE_PREEMPTION 和宏 configUSE_PREEMPTION 都为 1 的时候下面的 代码才会编译。所以要想使用时间片调度的话这这两个宏都必须为 1,缺一不可!

(2)、判断当前任务所对应的优先级下是否还有其他的任务。

(3)、如果当前任务所对应的任务优先级下还有其他的任务那么就返回 pdTRUE。 从上面的代码可以看出,如果当前任务所对应的优先级下有其他的任务存在,那么函数 xTaskIncrementTick() 就 会 返 回 pdTURE , 由 于 函 数 返 回 值 为 pdTURE 因 此 函 数 xPortSysTickHandler()就会进行一次任务切换。

2、时间片调度实验

1、实验目的

学习使用 FreeRTOS 的时间片调度。 

2、实验设计

本实验设计三个任务:start_task、task1_task 和 task2_task ,其中 task1_task 和 task2_task 的任务优先级相同,都为 2,这三个任务的任务功能如下:

start_task:用来创建其他 2 个任务。

task1_task :控制 LED0 灯闪烁,并且通过串口打印 task1_task 的运行次数。

task2_task :通过串口打印 task2_task 的运行次数。

3、实验程序与分析

为了观察方便,将系统的时钟节拍频率设置为 20,也就是将宏 configTICK_RATE_HZ 设置 为 20:

#define configTICK_RATE_HZ (20) 

 这样设置以后滴答定时器的中断周期就是 50ms 了,也就是说时间片值为 50ms,这个时间 片还是很大的,不过大一点我们到时候观察的时候方便。

任务设置

//任务优先级
#define START_TASK_PRIO		1
//任务堆栈大小	
#define START_STK_SIZE 		128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define TASK1_TASK_PRIO		2
//任务堆栈大小	
#define TASK1_STK_SIZE 		128  
//任务句柄
TaskHandle_t Task1Task_Handler;
//任务函数
void task1_task(void *pvParameters);

//任务优先级
#define TASK2_TASK_PRIO		2
//任务堆栈大小	
#define TASK2_STK_SIZE 		128  
//任务句柄
TaskHandle_t Task2Task_Handler;
//任务函数
void task2_task(void *pvParameters);

(1)和(2)、任务 task1_task 和 task2_task 的任务优先级设置为相同的,这里都设置为 2。

main函数

int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4	 
	delay_init();	    				//延时函数初始化	 
	uart_init(115200);					//初始化串口
	LED_Init();		  					//初始化LED
	LCD_Init();							//初始化LCD
	

	//创建开始任务
    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);   
    //创建TASK2任务
    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)
{
	u32 task1_num=0;
	while(1)
	{
		task1_num++;					//任务1执行次数加1 注意task1_num1加到255的时候会清零!!
		LED0=!LED0;
		taskENTER_CRITICAL();           //进入临界区
		printf("任务1已经执行:%d次\r\n",task1_num);
		taskEXIT_CRITICAL();            //退出临界区
		delay_xms(10);					//延时10ms,模拟任务运行10ms,此函数不会引起任务调度
	}
}

//task2任务函数
void task2_task(void *pvParameters)
{
	u32 task2_num=0;
	while(1)
	{
		task2_num++;					//任务2执行次数加1 注意task2_num1加到255的时候会清零!!
    //LED1=!LED1;
		taskENTER_CRITICAL();           //进入临界区
		printf("任务2已经执行:%d次\r\n",task2_num);
		taskEXIT_CRITICAL();            //退出临界区
		delay_xms(10);					//延时10ms,模拟任务运行10ms,此函数不会引起任务调度
	}
}

调用函数 delay_xms()延时 10ms。在一个时间片内如果任务不主动放弃 CPU 使用权的 话那么就会一直运行这一个任务,直到时间片耗尽。在 task1_task 任务中我们通过串口打印字 符串的方式提示 task1_task 在运行,但是这个过程对于 CPU 来说执行速度很快,不利于观察, 所以这里通过调用函数 delay_xms()来默认任务占用 10ms 的 CPU。函数 delay_xm()不会引起任 务调度,这样的话相当于 task1_task 的执行周期>10ms,基本可以看作等于 10ms,因为其他的 函数执行速度还是很快的。一个时间片的长度是 50ms,任务执行所需的时间以 10ms 算,理论 上在一个时间片内 task1_task 可以执行 5 次,但是事实上很少能执行 5 次,基本上是 4 次。

4、程序运行结果分析

 不管是 task1_task 还是 task2_task 都是连续执行 4,5 次,和前面程 序设计的一样,说明在一个时间片内一直在运行一个任务,当时间片用完后就切换到下一个任 务运行。

 

链接:https://pan.baidu.com/s/1hyUr3eqajQpuSdiBg2EFng?pwd=rtos 
提取码:rtos

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值