前言 這個任務調度模塊的實現是形成於畢設項目中的,用在STM32中,斷斷續續跨度2個月實現了一些基本功能,可能後面再做其他項目時會一點點完善起來,也會多學習相關知識來強化模塊的實用性和高效性,畢竟用自己自主實現出來的功能還是蠻舒心的。 任務調度模式結構 整體上的結構屬於線性結構,結合鏈表和定時器來實 ...
前言
這個任務調度模塊的實現是形成於畢設項目中的,用在STM32
中,斷斷續續跨度2個月實現了一些基本功能,可能後面再做其他項目時會一點點完善起來,也會多學習相關知識來強化模塊的實用性和高效性,畢竟用自己自主實現出來的功能還是蠻舒心的。
任務調度模式結構
整體上的結構屬於線性結構
,結合鏈表
和定時器
來實現,我使用的是sysTick
這個滴答時鐘,1ms
的頻率,功能比較簡單,容易理解。
分片
分片的模式,主要體現在函數分片
和時間分片
在我之前就有使用在函數中,主要的思路是,把函數功能切片
,分為幾個小部分,每次執行時按次序執行小部分,對於沒有時序要求的函數來說,可以把一個占用CPU
大的功能分攤開來實現,從而避免有些地方耗時長的問題。對於時間分片
,其實就是定時器的一種應用,實際上,函數分片
在執行的時候已經是一種時間分片
了,不過現在加上人為的控制在裡面了。
下麵是函數分片
的一般結構:
void func(char *fos,...){
static char step=0;//順序控制變數,自由度比較高,可亂序,可迴圈,可延遲執行
switch(step){
case 0:{
//...
step++;
break;
}
case 1:{
//...
step++;
break;
}
//...
default:{
//step++;//可以藉助default實現延時的效果,即跳過幾次空白step
break;
}
}
return;
}
其中添加的參數變數*fos
是必要的,因為就是通過傳入每個任務的這個標誌位來判斷是否運行結束,而其他的參數,就得基於具體任務做不一樣的處理了。
輪詢
- 運行框圖
可以看到這個框圖是一個頭尾相連的閉環結構
,從頭節點依次運行到尾節點後再從頭迴圈往複執行下去。
- 輪詢函數
void loop_task(void){
static Task_Obj *tasknode;
tasknode=task_curnode->next;//repoint the curnode to the next
if(tasknode==NULL){//tasknode is null,only the headnode have the attr
return;//express the task space is none
}
else if(tasknode->task_type==TYPE_HEAD){//tasknode is headnode
task_curnode=tasknode;
return;
}
else{
if(tasknode->run_type == RUN_WAIT){
//等待型任務,通過ready標誌來確定是否執行,否則就跳過
if(!tasknode->ready){
if(task_curnode->next !=NULL){
task_curnode=task_curnode->next;
return;
}
}
}
if(tasknode->task_status==STATUS_INIT){
tasknode->tickstart=HAL_GetTick();//獲取tick
tasknode->task_status=STATUS_RUN;
}
else if(tasknode->task_status==STATUS_RUN){
if((HAL_GetTick() - tasknode->tickstart) > (uint32_t)tasknode->task_tick){
tasknode->task_name(&(tasknode->task_fos));//run the step task,transfer the fos
tasknode->tickstart+=(uint32_t)tasknode->task_tick;//update the tickstart
}
}
}
if(tasknode->task_fos==FOS_FLAG){
tasknode->ready=0;
if(tasknode->waittask!=NULL){
//置位該任務綁定的等待的任務準備運行標誌位,標識可以準備運行了
tasknode->waittask->ready=1;
}
//運行結束就刪掉該任務
delete_task(tasknode);
}
else if(tasknode->task_fos==FOC_FLAG){
//迴圈運行該任務
tasknode->task_status=STATUS_INIT;//continue running from start
tasknode->task_fos=0;//RESET fos
}
if(task_curnode->next !=NULL){
if(task_curnode->next->run_type==RUN_FORCE) return;//force-type's task
else task_curnode=task_curnode->next;
}
}
其中有幾個運行態和標誌位
#define FOS_FLAG 99//運行結束標誌
#define FOC_FLAG 100//運行結束後再次執行,相當於迴圈運行
#define TYPE_NOMAL 0//標識一般任務類型
#define TYPE_HEAD 1//標識頭任務類型
#define TYPE_END 2//標識尾任務類型
#define RUN_NORMAL 0//一般輪詢模式
#define RUN_FORCE 1//強制運行該任務,運行結束才繼續下一個任務
#define RUN_WAIT 2//等待指定的任務結束,才可以被運行
#define STATUS_INIT 0//任務的準備階段,用於獲取起始時間
#define STATUS_RUN 1//任務運行階段
#define STATUS_UNVAILED 2//無效狀態
運行時對時間間隔tick
的把握還有點問題,這個等待後面有機會優化下。
調度實現
- 任務鏈表結構
typedef struct TASK_CLASS{
void (*task_name)(char *taskfos,...);//任務函數
int task_tick;//任務的時間分片間隔
uint32_t tickstart;//起始時間點,每次執行完須加上一個tick
char task_fos;//運行結束標誌
char task_type;//任務類型變數
char task_status;//任務狀態
char run_type;//運行狀態
char ready;//準備運行標誌位
struct TASK_CLASS *next;//下一任務
struct TASK_CLASS *waittask;//等待執行的任務
} Task_Obj;
-
添加任務
- add_task
void add_task(void (*taskname)(char *,...),int tasktick,int runtype){//可變參,這裡未做處理 Task_Obj *tasknode,*tmpnode; char i; tasknode = (Task_Obj*)malloc(sizeof(Task_Obj)); tasknode->task_name=taskname; tasknode->task_tick=tasktick; tasknode->task_fos=0; tasknode->task_status=STATUS_INIT;//initial status tasknode->task_type=TYPE_END; //set the new node to endnode tasknode->run_type=runtype; tasknode->next=&task_headnode;//the endnode point to the headnode tmpnode=&task_headnode; if(task_num==0){ tmpnode->next=tasknode; task_num++; return; } for(i=0;i<task_num;i++){ tmpnode=tmpnode->next;//reach the endnode } tmpnode->task_type=TYPE_NOMAL;//turn the last endnode to the normal node tmpnode->next=tasknode; task_num++; }
- add_wait_task
void add_wait_task(void (*taskname)(char *),void (*waitname)(char *),int tasktick){ Task_Obj *tmpnode,*tasknode; char i,pos; tmpnode=&task_headnode; for(i=0;i<task_num;i++){ tmpnode=tmpnode->next;//reach the endnode if(tmpnode->task_name==taskname){ pos=i;//獲取要等待任務的位置 break; } } tasknode = (Task_Obj*)malloc(sizeof(Task_Obj)); tasknode->task_name=waitname; tasknode->task_tick=tasktick; tasknode->task_fos=0; tasknode->task_status=STATUS_INIT;//initial status tasknode->task_type=TYPE_END; //set the new node to endnode tasknode->run_type=RUN_WAIT;//任務為等待運行 tasknode->ready=0; tasknode->next=&task_headnode;//the endnode point to the headnode tmpnode->waittask=tasknode;//獲取新建的等待執行的任務地址,在運行結束後把等待執行的任務的準備運行標誌位置1 tmpnode=&task_headnode; if(task_num==0){ tmpnode->next=tasknode; task_num++; return; } for(i=0;i<task_num;i++){ tmpnode=tmpnode->next;//reach the endnode } tmpnode->task_type=TYPE_NOMAL;//turn the last endnode to the normal node tmpnode->next=tasknode; task_num++; }
-
刪除任務
- delete_task(局限性大,只針對當前運行的任務而言)
void delete_task(Task_Obj *taskobj){ if(task_curnode->task_type==TYPE_HEAD && task_num < 2){//if curnode is headnode,and tasknum=1 task_curnode->next=NULL; } else{ task_curnode->next=taskobj->next;//repoint the curnode next } free(taskobj);//free the space of where the taskobj pointed task_num--; }
- delete_task_withname(刪除指定任務名的任務)
void delete_task_withname(void (*taskname)(char *)){ Task_Obj *tmpnode,*tmpnode2; char i,pos; tmpnode=&task_headnode; for(i=0;i<task_num;i++){ tmpnode=tmpnode->next;//reach the endnode if(tmpnode->task_name==taskname){ pos=i; break; } } if(i==task_num) return; tmpnode=&task_headnode; for(i=0;i<pos+1;i++){ tmpnode2=tmpnode; tmpnode=tmpnode->next; } if(tmpnode->next==NULL){//if tmpnode is endnode tmpnode2->next=&task_headnode; } else{ tmpnode2->next=tmpnode->next;//repoint the curnode next } task_num--; free(tmpnode); }
-
初始化任務空間
void non_task(char *taskfos){
return;
}
void init_taskspace(void){
task_headnode.task_name=non_task;
task_headnode.task_type=TYPE_HEAD;
task_headnode.task_status=STATUS_UNVAILED;
task_headnode.next=NULL;
task_curnode=&task_headnode;//頭節點是沒有任務需要執行的
task_num=0;
}
- 調用實例
add_task(task1,500,RUN_NORMAL);//500ms執行一次task1任務
add_wait_task(task1,task2,500);//task2等待task1結束才會執行,運行的時間間隔為500ms
delete_task_withname(task1);//刪除task1任務
while(1){
//...
loop_task();//任務輪詢
}
結語
整體實現說難不難,說簡單不簡單,但也是我第一次嘗試這種偏向系統級應用的代碼,而且都沒有參照任何其他的資料和代碼,完全以自己的對任務的理解和具體項目的需求來一點點實現,希望後面會把這個調度的代碼進一步完善成一個通用型的調度方式,也方便後面項目的使用了。
本文來自博客園,作者:pie_thn,轉載請註明原文鏈接:https://www.cnblogs.com/pie-o/p/17300699.html