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"); } }
任務函數里面就根據具體情況編寫了;
需要注意的是,實際中,時間片長度該設為多少需要好好調試,因為有時候時間片太短,任務還沒來得及做事情,就被調度了,時間片太長,又會造成讓其它任務等太久,所以實際編程中需要好好調試;