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了,但是任务一还有一个回车没执行,所以就会出现这种情况。增加时间片就可以解决这个问题。这个例子看出来两个优先级相同的任务,通过时间片轮转调度,两个任务可以交替执行。哪个任务先创建就会先执行哪一个。
总结
时间片转调度,在处理优先级相同的任务时有很大作用。