UCOS操作系统——时间片轮转调度(五)

UCOS操作系统


前言

我们说过 UCOSIII 是支持多个任务拥有同一个优先级的,这些任务采用时间片轮转调度方法进行任务调度。在 os_cfg.h 文件中有个宏 OS_CFG_SCHED_ROUND_ROBIN_EN,我们要想
使用时间片轮转调度就需要将OS_CFG_SCHED_ROUND_ROBIN_EN 定义为 1
这样 UCOSIII中有关时间片轮转调度的代码才会被编译,否则不能使用时间片轮转调度,这点特别重要!

一、OSSchedRoundRobinCfg()函数开启时间片

OSSchedRoundRobinCfg()函数用来使能或失能 UCOSIII,如果我们要使用时间片轮转调度功能的话不仅要将宏 OS_CFG_SCHED_ROUND_ROBIN_EN 定 义 为 1 ,还需要调用
OSSchedRoundRobinCfg()函数来使能 UCOSIII,OSSchedRoundRobinCfg()函数原型如下:

void OSSchedRoundRobinCfg ( CPU_BOOLEAN en,
 OS_TICK dflt_time_quanta,
 OS_ERR *p_err)

en: 用于设置打开或关闭时间片轮转调度机制,如果为 DEF_ENABLED 表示打开时间片轮转调度,为 DEF_DISABLED 表示关闭时间片轮转调度。
dflt_time_quanta: 设置默认的时间片长度,就是系统时钟节拍个数,比如我们设置系统时钟频率 OSCfg_TickRate_Hz 为 200Hz,那么每个时钟节拍就是 5ms。当我们设置 dflt_time_quanta 为 n 时,时间片长度就是(5n)ms 长,如果我们设置 dflt_time_quanta 为 0 的话,UCOSIII 就会使用系统默认的时间片长度:OSCfg_TickRate_Hz / 10,比如如果 OSCfg_TickRate_Hz 为 200,那么时间片长度为:200/105=100ms。
*p_err: 保存调用此函数后返回的错误码
在这里插入图片描述

二、OSSchedRoundRobinYield()函数放弃时间片

当一个任务想放弃本次时间片,把 CPU 的使用权让给同优先级下的另外一个任务就可以使
用 OSSchedRoundRobinYield()函数,函数原型如下:

void OSSchedRoundRobinYield (OS_ERR *p_err)

*p_err: 用来保存函数调用后返回的错误码。
OS_ERR_NONE 调用成功
OS_ERR_ROUND_ROBIN_1 当前优先级下没有其他就绪任务
OS_ERR_ROUND_ROBIN_DISABLED 未使能时间片轮转调度功能
OS_ERR_YIELD_ISR 在中断调用了本函数。
我们在调用这个后函数遇到最多的错误就是 OS_ERR_ROUND_ROBIN_1,也就是当前优
先级下没有就绪任务了。
也就是说,你现在有几个相同优先级的任务,你设置了时间片,每个任务间隔多久同时执行一次,比如你设置的时间是20ms但是你想实现了某个功能就不执行了,执行下一个,这时候你可以调用这个函数弃用时间片。

三、完整代码

在这里插入图片描述
你在初始化的时候,可以设置相应的时间片

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "includes.h"


//ÈÎÎñÓÅÏȼ¶
#define START_TASK_PRIO		3
//ÈÎÎñ¶ÑÕ»´óС	
#define START_STK_SIZE 		128
//ÈÎÎñ¿ØÖÆ¿é
OS_TCB StartTaskTCB;
//ÈÎÎñ¶ÑÕ»	
CPU_STK START_TASK_STK[START_STK_SIZE];
//ÈÎÎñº¯Êý
void start_task(void *p_arg);

//ÈÎÎñÓÅÏȼ¶
#define TASK1_TASK_PRIO		4
//ÈÎÎñ¶ÑÕ»´óС	
#define TASK1_STK_SIZE 		128
//ÈÎÎñ¿ØÖÆ¿é
OS_TCB Task1_TaskTCB;
//ÈÎÎñ¶ÑÕ»	
CPU_STK TASK1_TASK_STK[TASK1_STK_SIZE];
void task1_task(void *p_arg);

//ÈÎÎñÓÅÏȼ¶
#define TASK2_TASK_PRIO		4
//ÈÎÎñ¶ÑÕ»´óС	
#define TASK2_STK_SIZE 		128
//ÈÎÎñ¿ØÖÆ¿é
OS_TCB Task2_TaskTCB;
//ÈÎÎñ¶ÑÕ»	
CPU_STK TASK2_TASK_STK[TASK2_STK_SIZE];
//ÈÎÎñº¯Êý
void task2_task(void *p_arg);

//Ö÷º¯Êý
int main(void)
{
    
    
	OS_ERR err;
	CPU_SR_ALLOC();
	
	delay_init();  	//ʱÖÓ³õʼ»¯
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//ÖжϷÖ×éÅäÖÃ
	uart_init(115200); 	//´®¿Ú³õʼ»¯
	
	LED_Init();         //LED³õʼ»¯	
	LCD_Init();			//LCD³õʼ»¯	
	
	POINT_COLOR = RED;
	LCD_ShowString(30,10,200,16,16,"ALIENTEK STM32F1");	
	LCD_ShowString(30,30,200,16,16,"UCOSIII Examp 6-3");
	LCD_ShowString(30,50,200,16,16,"Task Round-robin");
	LCD_ShowString(30,70,200,16,16,"ATOM@ALIENTEK");
	LCD_ShowString(30,90,200,16,16,"2015/3/19");
	
	OSInit(&err);		//³õʼ»¯UCOSIII
	OS_CRITICAL_ENTER();//½øÈëÁÙ½çÇø			 
	//´´½¨¿ªÊ¼ÈÎÎñ
	OSTaskCreate((OS_TCB 	* )&StartTaskTCB,		//ÈÎÎñ¿ØÖÆ¿é
				 (CPU_CHAR	* )"start task", 		//ÈÎÎñÃû×Ö
                 (OS_TASK_PTR )start_task, 			//ÈÎÎñº¯Êý
                 (void		* )0,					//´«µÝ¸øÈÎÎñº¯ÊýµÄ²ÎÊý
                 (OS_PRIO	  )START_TASK_PRIO,     //ÈÎÎñÓÅÏȼ¶
                 (CPU_STK   * )&START_TASK_STK[0],	//ÈÎÎñ¶ÑÕ»»ùµØÖ·
                 (CPU_STK_SIZE)START_STK_SIZE/10,	//ÈÎÎñ¶ÑÕ»Éî¶ÈÏÞλ
                 (CPU_STK_SIZE)START_STK_SIZE,		//ÈÎÎñ¶ÑÕ»´óС
                 (OS_MSG_QTY  )0,					//ÈÎÎñÄÚ²¿ÏûÏ¢¶ÓÁÐÄܹ»½ÓÊÕµÄ×î´óÏûÏ¢ÊýÄ¿,Ϊ0ʱ½ûÖ¹½ÓÊÕÏûÏ¢
                 (OS_TICK	  )0,					//µ±Ê¹ÄÜʱ¼äƬÂÖתʱµÄʱ¼äƬ³¤¶È£¬Îª0ʱΪĬÈϳ¤¶È£¬
                 (void   	* )0,					//Óû§²¹³äµÄ´æ´¢Çø
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //ÈÎÎñÑ¡Ïî
                 (OS_ERR 	* )&err);				//´æ·Å¸Ãº¯Êý´íÎóʱµÄ·µ»ØÖµ
	OS_CRITICAL_EXIT();	//Í˳öÁÙ½çÇø	 
	OSStart(&err);      //¿ªÆôUCOSIII
}


//¿ªÊ¼ÈÎÎñº¯Êý
void start_task(void *p_arg)
{
    
    
	OS_ERR err;
	CPU_SR_ALLOC();
	p_arg = p_arg;
	
	CPU_Init();
#if OS_CFG_STAT_TASK_EN > 0u
   OSStatTaskCPUUsageInit(&err);  	//ͳ¼ÆÈÎÎñ                
#endif
	
#ifdef CPU_CFG_INT_DIS_MEAS_EN		//Èç¹ûʹÄÜÁ˲âÁ¿ÖжϹرÕʱ¼ä
    CPU_IntDisMeasMaxCurReset();	
#endif
	
#if	OS_CFG_SCHED_ROUND_ROBIN_EN  //µ±Ê¹ÓÃʱ¼äƬÂÖתµÄʱºò
	 //ʹÄÜʱ¼äƬÂÖתµ÷¶È¹¦ÄÜ,ʱ¼äƬ³¤¶ÈΪ1¸öϵͳʱÖÓ½ÚÅÄ£¬¼È1*5=5ms
	OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);  
#endif		
	
	OS_CRITICAL_ENTER();	//½øÈëÁÙ½çÇø
	//´´½¨TASK1ÈÎÎñ
	OSTaskCreate((OS_TCB 	* )&Task1_TaskTCB,		
				 (CPU_CHAR	* )"Task1 task", 		
                 (OS_TASK_PTR )task1_task, 			
                 (void		* )0,					
                 (OS_PRIO	  )TASK1_TASK_PRIO,     
                 (CPU_STK   * )&TASK1_TASK_STK[0],	
                 (CPU_STK_SIZE)TASK1_STK_SIZE/10,	
                 (CPU_STK_SIZE)TASK1_STK_SIZE,		
                 (OS_MSG_QTY  )0,					
                 (OS_TICK	  )2,  //2¸öʱ¼äƬ£¬¼È2*5=10ms					
                 (void   	* )0,					
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                 (OS_ERR 	* )&err);				
				 
	//´´½¨TASK2ÈÎÎñ
	OSTaskCreate((OS_TCB 	* )&Task2_TaskTCB,		
				 (CPU_CHAR	* )"task2 task", 		
                 (OS_TASK_PTR )task2_task, 			
                 (void		* )0,					
                 (OS_PRIO	  )TASK2_TASK_PRIO,     	
                 (CPU_STK   * )&TASK2_TASK_STK[0],	
                 (CPU_STK_SIZE)TASK2_STK_SIZE/10,	
                 (CPU_STK_SIZE)TASK2_STK_SIZE,		
                 (OS_MSG_QTY  )0,					
                 (OS_TICK	  )2,	//2¸öʱ¼äƬ£¬¼È2*5=10ms					
                 (void   	* )0,				
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, 
                 (OS_ERR 	* )&err);			 
	OS_CRITICAL_EXIT();	//Í˳öÁÙ½çÇø
	OSTaskDel((OS_TCB*)0,&err);	//ɾ³ýstart_taskÈÎÎñ×ÔÉí
}


//task1ÈÎÎñº¯Êý
void task1_task(void *p_arg)
{
    
    
	u8 i,task1_num=0;
	OS_ERR err;
	p_arg = p_arg;
	 
	POINT_COLOR = RED;
	LCD_ShowString(30,130,110,16,16,"Task1 Run:000");
	POINT_COLOR = BLUE;
	while(1)
	{
    
    
		task1_num++;	//ÈÎÎñ1Ö´ÐдÎÊý¼Ó1 ×¢Òâtask1_num1¼Óµ½255µÄʱºò»áÇåÁ㣡£¡
		LCD_ShowxNum(110,130,task1_num,3,16,0x80);	//ÏÔʾÈÎÎñÖ´ÐдÎÊý
		for(i=0;i<5;i++) printf("Task1:01234\r\n");
		LED0 = ~LED0;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //ÑÓʱ1s
		
	}
}

//task2ÈÎÎñº¯Êý
void task2_task(void *p_arg)
{
    
    
	u8 i,task2_num=0;
	OS_ERR err;
	p_arg = p_arg;
	
	POINT_COLOR = RED;
	LCD_ShowString(30,150,110,16,16,"Task2 Run:000");
	POINT_COLOR = BLUE;
	while(1)
	{
    
    
		task2_num++;	//ÈÎÎñ2Ö´ÐдÎÊý¼Ó1 ×¢Òâtask1_num2¼Óµ½255µÄʱºò»áÇåÁ㣡£¡
		LCD_ShowxNum(110,150,task2_num,3,16,0x80);  //ÏÔʾÈÎÎñÖ´ÐдÎÊý
		for(i=0;i<5;i++) printf("Task2:56789\r\n");
		LED1 = ~LED1;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //ÑÓʱ1s
	}
}


在这里插入图片描述
我们可以看到任务 1 和任务 2 各自都运行了 40次,说明时间片轮转调度起作用了,因为任务 1 和任务 2 拥有相同的优先级,如果时间片轮转调度没有起作用的话肯定会出错的。但是我们从图 中我们并不能看出时间片轮转调度执行的细节,这时候我们就需要观察串口输出了
在这里插入图片描述
我们在任务 1 中通过串口循环输出 5 次字符串“Task1:01234”,任务 2 中通过串口循环输出 5 次字符串“Task2:56789”。通过图中可以看出任务 1 和任务 2 运行正常,说明时间片轮转调度正常。
举个例子:
在这里插入图片描述
可以看到位置不对,这是因为时间片不够,10ms到了该执行任务2了,但是任务一还有一个回车没执行,所以就会出现这种情况。增加时间片就可以解决这个问题。这个例子看出来两个优先级相同的任务,通过时间片轮转调度,两个任务可以交替执行。哪个任务先创建就会先执行哪一个。

总结

时间片转调度,在处理优先级相同的任务时有很大作用。

猜你喜欢

转载自blog.csdn.net/qq_51963216/article/details/123885322