自用純C語言實現任務調度(可用於STM32、C51等單片機)

来源:https://www.cnblogs.com/pie-o/archive/2023/04/09/17300699.html
-Advertisement-
Play Games

前言 這個任務調度模塊的實現是形成於畢設項目中的,用在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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、為什麼要確定付費客戶特征? 先講個案例,以 Shopify 網站為例進行分析。該網站提供了許多功能,圍繞著潛在客戶在全生命周期中所需的業務需求,包括從創建業務開始、賺取收益等整個閉環鏈上所需的任何工具,如: 開始做生意:Business name generator 線上工具、功能變數名稱選擇頁面、Bu ...
  • 前言 一、人物簡介 第一位閃亮登場,有請今後會一直教我們C語言的老師 —— 自在。 第二位上場的是和我們一起學習的小白程式猿 —— 逍遙。 二、構成和表達方式 位運算符是一組用於在二進位數之間進行操作的運算符 | 運算符 | 名稱 | 示例 | | : : | : : | : : | | & | 位 ...
  • CommunityToolkit.Mvvm包(又名MVVM 工具包,以前名為 Microsoft.Toolkit.Mvvm)是一個現代、快速且模塊化的 MVVM 庫。它支持:.NET Standard 2.0、 .NET Standard 2.1 和 .NET 6(UI Framework 不支持) ...
  • 首先就是要下載Visual Studio,具體可以登錄官網查看下載教程。 首次打開Visual Studio,就會顯示新建的頁面,若是使用過的則根據以下操作進行: 以下界面則與新使用的新建界面一致:選擇ASP.NET Web,點擊下一步 填寫項目名稱,選擇項目存放的位置,選擇框架,點擊創建 在這裡選 ...
  • 在本文中,我們將介紹如何使用 .NET Core 中的中間件來自定義規範響應,以便在 API 調用時返回統一的格式和錯誤信息。中間件是一種可以在請求和響應管道中執行邏輯的軟體組件,它可以對請求或響應進行修改、攔截或處理。我們將使用一個簡單的示例來演示如何創建和使用自定義規範響應的中間件。 首先,我們 ...
  • 1. 在Blazor中使用Chart.js 首先,從Chart.js官方網站下載Chart.js庫文件。 推薦下載這個構建好的版本https://cdnjs.com/libraries/Chart.js,最新版是v4.2.1 在Blazor項目中把剛剛下載好的Chart.js放到wwwroot目錄下 ...
  • 雲伺服器開啟埠 CentOS開啟埠 防火牆添加埠 firewall-cmd --zone=public --add-port=埠/tcp --permanent 命令含義: –zone #作用域 –add-port=1935/tcp #添加埠,格式為:埠/通訊協議 –permanent ...
  • Ubuntu系統Flameshot使用問題 系統:Ubuntu22.04 問題:使用Flameshot,每次都會先截取整個屏幕,提示需要先分享,再使用Flameshot的功能 安裝Flameshot sudo apt install flameshot 先說解決方案 開機用戶登錄時,右下角有設置桌面 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...