uC/OS-III 任務詳解(四)
uC/OS系統的任務一般都放在最開始介紹,我放在第四章主要是對模糊的概念作清晰的講解。
從使用者的角度來看,uC/OS-III 中的任務可以分為5 種狀態,分別是休眠態、就緒態、執行態、掛起態和中斷態,如下表所示。
任務狀態之間的具體切換情況如下圖所示。
也就是說,任務有五個狀態,分別是休眠態、就緒態、執行態、等待態、中斷服務態。即任務還沒使用函式OSTaskCreate()建立時就是屬於休眠態,而一但使用函式OSTaskCreate()建立了任務,並且函式OSStart()之前已經被執行的話,那麼任務就屬於就緒態了。然後系統會根據就緒表裡任務的優先順序來只執行最高的任務,而這個被執行的任務也從就緒態變為執行態,這個時刻就是這個優先順序最高的任務的CPU獨享moment(也就是獨自擁有CPU的使用權),直到這個任務被被切換成其他狀態(使用OSTimeDly()、OSSemPend()等函式能將執行態的函式變為等待態),那麼就緒表裡優先順序最高的任務就會得到CPU的使用權執行任務,依次迴圈。
所以我這麼分類,如果一個正在執行的任務M(即正在執行優先順序最高的任務),它就只有兩種情況,A.一直迴圈執行下去 B.改變自己任務狀態。 改變狀態也分為 b1.自己任務呼叫延時類函式或者等待訊號值類函式的主動改變; b2.被一個優先順序更高的就緒任務H打斷的被動改變(也就是隻要有優先順序更高的就緒任務,那麼在下一個時鐘節拍到來的的時候,任務H就會剝奪任務1的CPU)。
主動改變的話可以是除執行態之外的任意狀態,被動改變就是任務變為掛起態。
還有一種情況就是當兩個任務的優先順序一樣怎麼辦?這裡使用的是時間片輪轉排程,就要在檔案中使能 OSSchedRoundRobinCfg(DEF_ENABLED,1,&err) 函式,如下圖所示。
然後在app.C檔案的初始函式中初始化OSSchedRoundRobinCfg(DEF_ENABLED,1,&err) ,這個1代表1*5ms的時間,意思是相同的最高優先順序時每個任務輪流執行5ms。
例子如下:
#define LED0_TASK_PRIO 4 //任務優先順序; #define LED0_STK_SIZE 128 //任務堆疊大小,實際大小是:128*4位元組 CPU_STK LED0_TASK_STK[LED0_STK_SIZE]; //任務堆疊; OS_TCB Led0TaskTCB; //任務控制塊; //宣告任務函式(一般將一個任務寫成一個函式): void Led0_task(void *p_arg); //注:“p_arg”這個引數基本用不上,但必須得寫上 #define LED1_TASK_PRIO 4 //任務優先順序; #define LED1_STK_SIZE 128 //任務堆疊大小,實際大小是:128*4位元組 CPU_STK LED1_TASK_STK[LED1_STK_SIZE]; //任務堆疊; OS_TCB Led1TaskTCB; //任務控制塊; //宣告任務函式(一般將一個任務寫成一個函式): 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,就是刪除任務自身,要是填寫別的任務的控制塊,那就是刪除別的任務) }
&n