时间片轮转法:主要用于分时系统中的进程调度。为了实现轮转调度,系统把所有就绪进程按照先入先出的原则排成一个队列的队首进程,让CPU上运行一个时间片的时间。时间片是一个小小的时间单位,通常为5~10ms数量级。当进程用完分给他的时间片后,系统的计时器发出时钟中断,调度程序便停止该进程的运行,把它放在就绪列队的末尾,把CPU分给就绪队列的队首进程,同样也让它运行一个时间片,如此往复。
如果想要使用UCOSIII的时间片轮调度的话不仅要将宏OS_CFG_SCHED_ROUND_ROBIN_EN置1,还需要调用函数OSSchedRoundRobinCfg(),
位置:
函数原型如下:
- void OSSchedRoundRobinCfg (CPU_BOOLEAN en,
- OS_TICK dflt_time_quanta,
- OS_ERR *p_err)
当使用时间片轮转的时候,使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms
- #if OS_CFG_SCHED_ROUND_ROBIN_EN //当使用时间片轮转的时候
- //使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms
- OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);
- #endif
当任务想要放弃本次时间片的时候,就可以调用OSSchedRoundRobinYield()函数,函数原型如下:
在任务设计之初就应该考虑下面三个方面的的因素:
中断服务函数:
在中断服务函数中不能使用挂起当前任务的操作,不允许调用任何会阻塞运行API函数接口(延时)。中断服务函数最好是精简短小,快进快出。一般在中断服务函数中只做标记事件发生,然后通知任务,让对应的任务区处理,因为中断服务函数的优先级高于任何优先级任务,如果中断任务处理时间过长会导致系统整个任务无法正常运行。
普通任务:
在任务中不允许出现死循环(此处的死循环指的是没有阻塞机制的任务循环体),如一个任务中只有就绪态而没有阻塞态,势必会影响到其他低优先级的任务的执行,所以在进行任务设计的时候,就应该保证任务在不活跃的时候,任务可以进入阻塞态以交出 CPU 使用权。(OSTimeDlyHMSM()延时函数)
空闲任务:
用户可以通过空闲任务钩子方式,在空闲任务上钩入自己的功能函数。通常这个空闲任务钩子能够完成一些额外的特殊功能,例如系统运行状态的指示,系统省电模式等。空闲任务是唯一一个不允许出现阻塞情况的任务,因为 μC/OS 需要保证系统永远都有一个可运行的任务。
实验要求:
- 主函数创建任务1负责创建两个相同的优先级的任务2和任务3执行完任务函数后删除自身。
- 任务2负责每隔1s打印一次abcde,任务3每隔1s打印一次fghij
- 通过串口打印值来观察时间片轮转。
本实验继承前一篇博客http://t.csdnimg.cn/RRZqJ的工程在之前的工程上修改:
任务1:
在错误码变量声明完之后,加入下边的代码当宏OS_CFG_SCHED_ROUND_ROBIN_EN置1的时候执行调用函数OSSchedRoundRobinCfg(),实现时间片轮转调度。
#if OS_CFG_SCHED_ROUND_ROBIN_EN OSSchedRoundRobinCfg(DEF_ENABLED,1,&err2_3); #endif将创建任务的时间片长度修改为适当时间,这是我修改为3,时间片:3*5=15ms
实验现象:
实验源码:
#include "led.h" #include "delay.h" #include "sys.h" #include "usart.h" #include "includes.h" //UCOSIII中以下优先级用户程序不能使用,ALIENTEK //将这些优先级分配给了UCOSIII的5个系统内部任务 //优先级0:中断服务服务管理任务 OS_IntQTask() //优先级1:时钟节拍任务 OS_TickTask() //优先级2:定时任务 OS_TmrTask() //优先级OS_CFG_PRIO_MAX-2:统计任务 OS_StatTask() //优先级OS_CFG_PRIO_MAX-1:空闲任务 OS_IdleTask() //创建任务1 //定义任务优先级 #define TASK_1_PRIO 3 //定义任务控制块 OS_TCB TASK_1_TCB; //定义任务堆栈大小 #define TASK_1_STK_SIZE 128 //定义任务堆栈 CPU_STK TASK_1_STK[TASK_1_STK_SIZE]; //定义任务函数 void TASK_1(void *arg); //创建任务2 //定义任务优先级 #define TASK_2_PRIO 4 //定义任务控制块 OS_TCB TASK_2_TCB; //定义任务堆栈大小 #define TASK_2_STK_SIZE 128 //定义任务堆栈 CPU_STK TASK_2_STK[TASK_2_STK_SIZE]; //定义任务函数 void TASK_2(void *arg); //创建任务3 //定义任务优先级 #define TASK_3_PRIO 4 //定义任务控制块 OS_TCB TASK_3_TCB; //定义任务堆栈大小 #define TASK_3_STK_SIZE 128 //定义任务堆栈 CPU_STK TASK_3_STK[TASK_3_STK_SIZE]; //定义任务函数 void TASK_3(void *arg); int main(void) { OS_ERR err1;//错误码变量 CPU_SR_ALLOC();//定义临界区需要的变量 //硬件初始化 delay_init(); //延时初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组配置 uart_init(115200); //串口波特率设置 OSInit(&err1);//初始化UCOSIII OS_CRITICAL_ENTER();//进入临界区代码 //创建开始任务1 OSTaskCreate((OS_TCB * )&TASK_1_TCB, //任务控制块 (CPU_CHAR * )"main TASK1", //任务名字 (OS_TASK_PTR )TASK_1, //任务函数 (void * )0, //传递给任务函数的参数 (OS_PRIO )TASK_1_PRIO, //任务优先级 (CPU_STK * )&TASK_1_STK[0], //任务堆栈基地址 (CPU_STK_SIZE)TASK_1_STK_SIZE/10, //任务堆栈深度限位 (CPU_STK_SIZE)TASK_1_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 * )&err1); //存放该函数错误时的返回值 OS_CRITICAL_EXIT();//退出临界区代码 OSStart(&err1);//开启UCOSIII while(1); } void TASK_1(void *arg) { OS_ERR err2_3;//错误码变量 CPU_SR_ALLOC();//定义临界区需要的变量 #if OS_CFG_SCHED_ROUND_ROBIN_EN OSSchedRoundRobinCfg(DEF_ENABLED,1,&err2_3); #endif OS_CRITICAL_ENTER();//进入临界区代码 //创建开始任务2 OSTaskCreate((OS_TCB * )&TASK_2_TCB, //任务控制块 (CPU_CHAR * )"main TASK2", //任务名字 (OS_TASK_PTR )TASK_2, //任务函数 (void * )0, //传递给任务函数的参数 (OS_PRIO )TASK_2_PRIO, //任务优先级 (CPU_STK * )&TASK_2_STK[0], //任务堆栈基地址 (CPU_STK_SIZE)TASK_2_STK_SIZE/10, //任务堆栈深度限位 (CPU_STK_SIZE)TASK_2_STK_SIZE, //任务堆栈大小 (OS_MSG_QTY )0, //任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息 (OS_TICK )3, //当使能时间片轮转时的时间片长度,为0时为默认长度, (void * )0, //用户补充的存储区 (OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项 (OS_ERR * )&err2_3); //存放该函数错误时的返回值 //创建开始任务3 OSTaskCreate((OS_TCB * )&TASK_3_TCB, //任务控制块 (CPU_CHAR * )"main TASK3", //任务名字 (OS_TASK_PTR )TASK_3, //任务函数 (void * )0, //传递给任务函数的参数 (OS_PRIO )TASK_3_PRIO, //任务优先级 (CPU_STK * )&TASK_3_STK[0], //任务堆栈基地址 (CPU_STK_SIZE)TASK_3_STK_SIZE/10, //任务堆栈深度限位 (CPU_STK_SIZE)TASK_3_STK_SIZE, //任务堆栈大小 (OS_MSG_QTY )0, //任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息 (OS_TICK )3, //当使能时间片轮转时的时间片长度,为0时为默认长度, (void * )0, //用户补充的存储区 (OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项 (OS_ERR * )&err2_3); //存放该函数错误时的返回值 OS_CRITICAL_EXIT();//退出临界区代码 //任务一执行完函数之后删掉自身 OSTaskDel((OS_TCB *)0,&err2_3); } //任务2主体 void TASK_2(void *arg) { int num = 0;//任务2运行次数 OS_ERR err2; while(1) { num++; printf("任务2运行次数:%d\r\n",num); printf("abcde\r\n"); OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err2);//延时1S } } //任务3主体 void TASK_3(void *arg) { int num = 0;//任务2运行次数 OS_ERR err3; while(1) { num++; printf("任务3运行次数:%d\r\n",num); printf("fghij\r\n"); OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err3);//延时500ms } }