UCOSIII时间片轮转调度
UCOSIII中,相同优先级的任务可以由时间片轮转调度来实现每个任务各自的正常运行,在前面一篇《UCOSIII任务管理相关知识》中有过叙述,在此篇中,主要举例说明。
首先,要使用时间片轮转调度,需要将系统中的宏定义:“OS_CFG_SCHED_ROUND_ROBIN_EN”设为真,这样才能启用时间片轮转调度,代码如下:
#if OS_CFG_SCHED_ROUND_ROBIN_EN //当使用时间片轮转的时候 //使能时间片轮转调度功能,时间片长度为:1*5=5ms OSSchedRoundRobinCfg(DEF_ENABLED,1,&err); #endif
其实,我们可以看到,上面那个条件编译就是个是否需要编译“OSSchedRoundRobinCfg()”这个函数用的,关键是要执行“OSSchedRoundRobinCfg()”这个函数才能打开时间片轮转调度,这个函数原型在UCOSIII源码中“os_core.c”文件中的大概第571行,函数原型在下面就对这个函数进行下说明:
参数一:使能,填写为"DEF_ENABLED"才能使能时间片轮转调度;
参数二:时间片长度,之前的《UCOSIII任务管理相关知识》中提到过这个东西,就是每个任务每次获得CPU使用权后执行的时间,任务也可以自己放弃没有用完的时间片;
参数三:错误码;
那么,这个函数运行后具体有什么效果呢?运行后系统将可以进行时间片轮转调度,主要作用是在同优先级的两个任务中,谁的时间片先到达就先执行谁,下面是例程:
1 #define LED0_TASK_PRIO 4 //任务优先级; 2 #define LED0_STK_SIZE 128 //任务堆栈大小,实际大小是:128*4字节 3 CPU_STK LED0_TASK_STK[LED0_STK_SIZE]; //任务堆栈; 4 OS_TCB Led0TaskTCB; //任务控制块; 5 //声明任务函数(一般将一个任务写成一个函数): 6 void Led0_task(void *p_arg); //注:“p_arg”这个参数基本用不上,但必须得写上 7 8 #define LED1_TASK_PRIO 4 //任务优先级; 9 #define LED1_STK_SIZE 128 //任务堆栈大小,实际大小是:128*4字节 10 CPU_STK LED1_TASK_STK[LED1_STK_SIZE]; //任务堆栈; 11 OS_TCB Led1TaskTCB; //任务控制块; 12 //声明任务函数(一般将一个任务写成一个函数): 13 void Led1_task(void *p_arg); //注:“p_arg”这个参数基本用不上,但必须得写上
上面我们可以看到,两个任务的任务优先级是一样的,这时,系统到底该运行哪个任务,就要通过时间片轮转来调度了;
//任务创建函数,注:一般将其它任务的创建放到一个专门创建任务的函数中; 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*5=5ms OSSchedRoundRobinCfg(DEF_ENABLED,1,&err); #endif OS_CRITICAL_ENTER(); //进入临界区 //创建LED0任务: OSTaskCreate( (OS_TCB *) &Led0TaskTCB, (CPU_CHAR *) "led0 task", (OS_TASK_PTR) Led0_task, (void *) 0, (OS_PRIO) LED0_TASK_PRIO, (CPU_STK *) &LED0_TASK_STK[0], (CPU_STK_SIZE) LED0_STK_SIZE/10, (CPU_STK_SIZE) LED0_STK_SIZE, (OS_MSG_QTY) 0, (OS_TICK) 2, //时间片长度,启用时间片轮转调度时有用,这个参数用来设置此任务时间片长度 (void *) 0, (OS_OPT) OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, (OS_ERR *) &err ); //创建LED1任务: OSTaskCreate( (OS_TCB *) &Led1TaskTCB, (CPU_CHAR *) "led1 task", (OS_TASK_PTR) Led1_task, (void *) 0, (OS_PRIO) LED1_TASK_PRIO, (CPU_STK *) &LED1_TASK_STK[0], (CPU_STK_SIZE) LED1_STK_SIZE/10, (CPU_STK_SIZE) LED1_STK_SIZE, (OS_MSG_QTY) 0, (OS_TICK) 2, //时间片长度,启用时间片轮转调度时有用,这个参数用来设置此任务时间片长度 (void *) 0, (OS_OPT) OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, (OS_ERR *) &err ); //OS_TaskSuspend((OS_TCB*)&StartTaskTCB,&err); //挂起"start_task"任务 OS_CRITICAL_EXIT(); //退出临界区 OSTaskDel((OS_TCB *)0,&err);//删除任务自身;(第一个参数填写0,就是删除任务自身,要是填写别的任务的控制块,那就是删除别的任务) }
上面程序是在之前的《UCOSIII任务创建》中修改的,保留了两个任务“Led0_task”和“Led1_task”,这两个任务在这里创建的时候,注意,第十个参数这时就需要注意了,这是用来设置该任务的时间片长度的,就是说该任务得到CPU使用权之后会运行多长时间后再交出CPU使用权(其实是被系统调度走了)。
下面是两个任务具体原型:
//"Led0_task"任务: void Led0_task(void *p_arg) { OS_ERR err; p_arg = p_arg; //故意使用一下这个参数,否则编译器会警告说此参数没有使用 while(1) { LED0=~LED0; OSTimeDlyHMSM(0,0,0,300,OS_OPT_TIME_HMSM_STRICT,&err); printf("01234\r\n"); } } //"Led1_task"任务: void Led1_task(void *p_arg) { OS_ERR err; p_arg = p_arg; //故意使用一下这个参数,否则编译器会警告说此参数没有使用 while(1) { LED1=~LED1; OSTimeDlyHMSM(0,0,0,300,OS_OPT_TIME_HMSM_STRICT,&err); printf("56789\r\n"); } }
任务函数里面就根据具体情况编写了;
需要注意的是,实际中,时间片长度该设为多少需要好好调试,因为有时候时间片太短,任务还没来得及做事情,就被调度了,时间片太长,又会造成让其它任务等太久,所以实际编程中需要好好调试;