STM32 uC/OS_II 實踐 之 任務調度過程理解及查詢式事件
/*******************************************************************************
* Function Name : main
* Description : 主函數,對系統以及硬件初始化,建立主函數并開啟系統
* Input : None
* Output : None
* Return : None
*******************************************************************************/
int main(void)
{
CPU_IntDis(); // 禁止CPU中斷 連接到匯編
OSInit(); // uCOS系統初始化
BSP_Init(); // 硬件初始化
OSTaskCreate //建立主任務, 優先級最高 建立這個任務另外一個用途是為了以后使用統計任務
(
(void (*) (void *)) App_TaskStart, //指向任務代碼的指針
(void *) 0,//任務開始執行時,傳遞給任務的參數的指針
(OS_STK *) &App_TaskStartStk[APP_TASK_START_STK_SIZE - 1], //分配給任務的堆棧的棧頂指針,從(INT8U) APP_TASK_START_PRIO //分配給任務的優先級
);
OSTimeSet(0);
OSStart();
return(0);
}
在開始 uC/OS_II 的調度之前,我們需要調用函數OSInit(),他負責建立任務控制塊鏈表,就緒任務表等數據結構,然后初始化全局變量。然后把需要用的外部設備進行初始化,主要是時鐘初始化,中斷嵌套初始化,端口初始化,調用函數BSP_Init(),uC/OS_II規定在任務調度開始前至少有一個任務已經建立,所以我們建立一個任務APP_TaskStart,并且給這個任務分配優先級以及堆棧等資源這是必須的啦,然后我們用OSTimeSet(0)函數初始化系統的時鐘節拍數后,就調用OSStart()函數開始任務調度,任務就會從所有建立的任務里最高優先級開始執行。
大家還記得剛才建立了一個APP_TaskStart任務,在系統開始任務調度的時候,系統里除了默認的優先級最低的空閑任務外只有這一個任務被注冊了,自然就會運行這個任務,我們先來看下他的相關源代碼來自文件task.c:
/*******************************************************************************
* Function Name : App_TaskStart
* Description : 主任務
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void App_TaskStart(void* p_arg)
{
(void) p_arg;
OS_CPU_SysTickInit(); // 初始化系統心跳
#if (OS_TASK_STAT_EN > 0)
OSStatInit(); // 統計任務初始化函數
#endif
App_TaskCreate(); // 創建新的用戶任務
while(1)
{
LED4_HIGH;
OSTimeDlyHMSM(0,0,1,0);
LED4_LOW;
OSTimeDlyHMSM(0,0,1,0);
}
}
/*******************************************************************************
* Function Name : App_TaskCreate
* Description : 建立用戶任務
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void App_TaskCreate(void)
{
//===================================================================// 測試任務1
OSTaskCreateExt(
Task_Test1,// 指向任務代碼的指針,也就是任務函數名
(void *)0,// 任務開始執行時傳遞給任務的參數
(OS_STK *)&Task_Test1Stk[Task_Test1_STK_SIZE-1],//分配給任務堆棧的棧頂指針,自頂向下
Task_Test1_PRIO,// 分配給任務的優先級
Task_Test1_PRIO,// 預備給以后版本的標識符,現在同任務優先級
(OS_STK *)&Task_Test1Stk[0], // 指向任務堆棧的棧底指針,用于堆棧的檢驗
Task_Test1_STK_SIZE, // 指定堆棧的容量,用于堆棧檢驗
(void *)0, // 指向用戶附加數據域的指針,用來擴展任務控制塊
OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR // 任務選項:使能堆棧檢測 和 創建任務時清空堆棧
);
//===================================================================// 測試任務2
OSTaskCreateExt(
Task_Test2,
(void *)0,
(OS_STK *)&Task_Test2Stk[Task_Test2_STK_SIZE-1],
Task_Test2_PRIO,
Task_Test2_PRIO,
(OS_STK *)&Task_Test2Stk[0],
Task_Test2_STK_SIZE,
(void *)0,
OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR
);
//===================================================================// 測試任務3
OSTaskCreateExt(
Task_Test3,
(void *)0,
(OS_STK *)&Task_Test3Stk[Task_Test3_STK_SIZE-1],
Task_Test3_PRIO,
Task_Test3_PRIO,
(OS_STK *)&Task_Test3Stk[0],
Task_Test3_STK_SIZE,
(void *)0,
OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR
);
}
同時給出任務的優先級及堆棧大小等信息來自文件app_cfg.h
#define APP_TASK_START_PRIO 10
#define Task_Test1_PRIO 7
#define Task_Test2_PRIO 8
#define Task_Test3_PRIO 9
//任務堆棧大小
#define APP_TASK_START_STK_SIZE 64
#define Task_Test1_STK_SIZE 128
#define Task_Test2_STK_SIZE 128
#define Task_Test3_STK_SIZE 128
可以看到,任務APP_TaskStart的優先級最低,所以在這個任務里創建其他的任務的時候他就會被更高優先級的任務把CPU的占有權搶去,在uC/OS_II里每建立一個任務后都會產生一次任務的調度,如果這個建立的任務優先級更高,則系統就會去執行這個剛創立的任務,如果低就只能等著了。所以在建立Task_Test1任務后,就會跳轉執行此任務,現在我們來看下這三個測試任務的源代碼來自文件app.c
/*******************************************************************************
* Function Name : Task_Test1
* Description : 任務1
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void Task_Test1(void* p_arg)
{
(void) p_arg ;
while(1)
{
if(KEY_WKUP == 0)
{
LED1_HIGH;
}
else
{
LED1_LOW;
}
OSTimeDlyHMSM(0,0,0,100); // 延時,為其他低優先級的任務執行留有空間
}
}
/*******************************************************************************
* Function Name : Task_Test2
* Description : 任務2
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void Task_Test2(void* p_arg)
{
(void) p_arg ;
while(1)
{
LED2_HIGH;
OSTimeDlyHMSM(0,0,0,500);
LED2_LOW;
OSTimeDlyHMSM(0,0,0,500);
}
}
/*******************************************************************************
* Function Name : Task_Test3
* Description : 任務3
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void Task_Test3(void* p_arg)
{
(void) p_arg ;
while(1)
{
LED3_HIGH;
OSTimeDlyHMSM(0,0,0,500);
LED3_LOW;
OSTimeDlyHMSM(0,0,0,500);
}
}
剛才說到哪里了?對,現在開始執行Task_Test1的任務了,很顯然,這個任務是對一個按鍵的檢測,檢測完成后進入一個100ms的延時函數,在uC/OS_II里延時函數的作用要比以前的大得多,在uC/OS_II里任務中調用了延時函數時,該任務就被退出了任務就緒表,然后調用一次系統調度OS_Sched(),重新尋找最高優先級的任務去執行,這個時候咱們的系統只注冊了兩個任務,這樣就只能繼續執行任務APP_TaskStart了,剛才在這個函數里建立第一個測試任務就被搶去了CPU的占用權,現在回來繼續創建Task_Test2第二個測試任務,這第二個測試任務優先級也比開始任務高,所以自然的它又被人搶去了CPU的占用權,在第二個測試任務里大家又會看到有延時函數,功能肯定是相同的,以此類推第三個測試任務也就清晰的多了。這時還有一個問題就是延時結束后系統操作,剛才測試任務1進入延時后,不會被系統調用,他的延時變量OSTCBDly是隨著系統的心跳進行遞減的,如果有兩個任務同時在延時中只要他們的任務控制塊里的延時變量不是0就會在心跳中斷服務函數里減1,等到他被減為0,系統會把這個任務重新放到任務就緒表里,并且運行一次系統調度函數OS_Sched(),通過這種機制來保證系統始終在運行著最高優先級的任務。這樣系統的調度問題就解釋完了,在后面關于中斷和信號量使用時,他們對系統的執行順序也是有影響的,他們是如何參與到系統的調度里,就看后面的講解了。
剛才我們說了延時函數的作用,如果我把上面的代碼里黃色高亮的部分注視掉會有什么樣的效果,結果就是其他的3個任務都無法運行了,系統將一直處理任務Task_Test1,再加入我們如果把黃色高亮部分的延時參數調大一些,調整到1s,結果也不令人滿意,雖然我給了提起任務充足的運行時間,但是由于每檢測一次我都會等待很長時間,導致我的檢測精度大大降低,按鍵變的極為難用。這時候我們就應該思考,在uC/OS_II這樣一個實時的嵌入式操作系統里面,最可怕的就是無目的的等待,所以我們盡量使用中斷這樣方式去處理接口信息,中斷和實時系統是相得益彰的。
評論