唐 飛,楊 偉
(安慶師范大學物理與電氣工程學院,安徽安慶246133)
在工業控制領域,單片機作為一種微型化、低成本、高可靠性的微型計算機,有著廣泛的應用。隨著現代控制系統的復雜程度不斷提升,要求控制系統的計算機對其控制的多個任務能有效地進行管理。傳統的單片機系統程序基于單任務機制的前后臺模式運行,程序各功能模塊按一定的順序構成整體,并且按順序一個個循環執行。對外設的響應一般采用中斷機制,外部設備提出中斷請求,系統響應中斷,在中斷服務程序中完成外設的處理工作。系統的各模塊之間是一種線性關系,這種程序的單任務運行機制流程簡潔、易于控制。然而程序只能按單一的順序執行,缺乏靈活性,各模塊的時間協調也比較復雜,難以動態地調整執行順序或頻率,對于復雜的任務難以勝任[1-2]。為此,需要將多任務運行機制引入單片機的程序設計中,對比人們進行了很多相關的研究和實驗。
劉玉宏詳細研究了51單片機的實時內核的任務管理、任務同步機制、存儲器管理和時鐘控制的設計[3];趙虎通過分析單片機控制系統的功能結構及軟硬件組成,將時間片輪轉技術運用于單片機控制系統的設計中,實現了多任務、多優先級并發運行[4];高國勝針對目前單片機系統應用的實際需求,提出了單片機系統實時多任務機制3種實現方式:基于定時中斷、任務狀態和外部中斷[5];阿占文通過時間片輪詢方式,實現了單片機數據采集的串口通信、頻率信號采集和A/D轉換、D/A和開關量輸出的多任務操作[6];張利軍研究了小型實時操作系統RTX51的基本功能及其在單片機系統中的應用方法,并給出了一個RTX51的應用實例[7-11]。這些研究工作對多任務機制在單片機上的應用進行了一系列的探討,但是也存在著一定的局限性。很多研究使用匯編語言進行程序設計,繁瑣冗雜,移植性差;有的研究將單片機中的多個定時器都用于時間片輪換任務,占用系統資源過多,影響了單片機的任務執行能力;有的研究在單片機上引入商業性的實時多任務操作系統,不僅需要繳納高額的版權費,而且由于單片機本身的片上資源十分有限,操作系統內核占用其資源過多,嚴重擠占了應用程序的運行空間。
STM32系列是一類高性能、低成本、低功耗的為嵌入式應用設計的基于ARM架構的32位單片機的總稱,具有強大的軟件支持,特別適合應用于復雜的嵌入式系統。因此,尋求一種簡單的辦法在STM32系統上實現程序的多任務機制運行,具有十分重要的意義[12]。
程序單任務運行機制中,程序按任務的順序執行,而在多任務運行機制下,宏觀上允許在同一時間內運行多個程序模塊。多任務機制使用了一定的任務調度策略,宏觀上允許多個任務共享單個CPU,實際微觀上CPU某一時刻只為單個任務提供服務。任務調度機制保證CPU在不同任務之間的切換速度十分迅速,普通用戶在宏觀上難以察覺,認為CPU可以同時運行多個任務。兩者的運行機制如圖1和圖2所示。

圖1 單任務機制程序流程圖

圖2 多任務機制程序流程圖
基于CPU的分時技術可以實現多任務運行機制。分時技術將系統的各個功能分解為多個相對獨立的任務模塊,并將CPU的運行時間劃分成很多的微小的時間片,由一定的調度算法按不同的優先級分別分配給各個任務模塊運行,造成任務模塊在微觀上輪流運行、宏觀上多個并行的運行效果。各個任務模塊被限制在各自的時間片內使用CPU,若某個任務模塊在分配給它的時間片內尚未完成,該任務模塊將被暫時停止,CPU被分配給下一個任務模塊使用,如此循環,當下一輪歸屬自己的時間片到來時該任務模塊再繼續工作。由于CPU的運行速度很快,只要時間片的間隔分配得當,那么某個任務從掛起到獲得下一個工作時間片的時間很短,用戶不會察覺到任務運行有所“停頓”,而是感到整個系統為其“獨占”。
基于分時技術的多任務程序運行機制主要具有以下特點:(1)多路性,用戶通過各自的設備,可以同時訪問同一個系統;(2)及時性,用戶的需求能在較短的時間內得到及時響應;(3)獨占性,用戶之間的操作相互獨立,不會互相影響。因此,將基于分時技術的多任務程序運行機制引入單片機后,可以有效地改善單片機系統的程序運行方式,滿足復雜控制系統的需求。
單片機系統的程序運行機制是按單任務設計的,即程序只能按單一的順序執行,而多任務機制要求在同一時間執行多個任務,對于單個CPU,只能在各個任務間快速切換來完成多任務需求。單片機內部都提供了定時/計數器,可以實現毫秒級的定時,將定時器的中斷系統用于多任務系統中的時間片的分配,設定好相應的定時時間,定時時間到后產生中斷,利用中斷機制進行多個任務的分時切換。因此,單片機具備了實現時分多任務機制的硬件條件。又由于單片機運算能力有限,多任務調度的算法必須簡潔明了,因此采用時分式的程序控制機制也是較為合適的。
實現多任務分時控制的關鍵在于將系統的功能細分成多個任務模塊,并合理地劃分各模塊運行所占用的時間片。時間片劃分的越小,系統的實時性就越好,但是任務切換會比較頻繁,降低CPU的運行效率,程序模塊的運行會被分割得較為破碎;時間片劃分的較長,CPU運行效率將會升高,但系統的實時性將會降低。因此,在設計時間片的大小時,需要根據系統任務的執行、采樣時間、A/D轉換、本機頻率等因素,合理地確定時間片的大小。
采用時分式的多任務程序運行機制后,程序不再是按單任務的順序執行,而是在相應的時間片內,執行對應的程序模塊。每個程序模塊都相互獨立,并設置了運行標志位。進入功能模塊后,先根據標志位判斷是否需要執行該模塊,如滿足要求則執行,否則,跳過并進入下一個功能模塊。類似延時這樣的功能模塊,不再使用循環等待,而是以時間片設置的時間為基準,轉換為對模塊執行頻率的需求。如果時間片足夠短,任務切換及時,程序的運行中等同于所有的模塊同時工作,滿足多任務的實時需求。
在單片機程序設計中,按系統的功能設置各個任務,每個任務細分為各個過程,按時間片管理各過程的運行。因此單片機多任務處理程序原理如下:(1)系統將各任務依次排成隊列,CPU依次執行各任務,在執行結束后返回首個任務執行并循環;(2)在執行單個任務時,如該任務無法在一個時間片內完成,就執行該任務的一個過程,其他過程等下一輪時間片到來時再執行;(3)循環延時等消耗CPU時間的程序,轉換為對任務執行頻率的需求;(4)任務和過程間通過變量共享和交換數據,任務切換時不保留現場數據和傳遞參數。
程序多任務運行機制中,關鍵在于合理地將系統功能分解為多個任務功能模塊,每個任務還可以按具體需求細分為過程。任務模塊的劃分與系統功能密切相關,劃分時應注意:(1)各任務模塊應盡量短小精悍,保證在一個系統時間片內執行完畢。對于傳統的循環延時類消耗CPU時間的程序,應盡量將其轉化為對模塊執行頻率的需求。例如,在數碼管動態顯示時,每位數碼管需要有3~5 ms的延時來保證顯示穩定,傳統的程序設計采用循環延時的方案,執行時需要消耗CPU較長時間,影響其他任務的執行;在多任務機制下,改變程序設計方法,數碼管位顯之間的切換由模塊執行頻率來決定,讓出CPU時間給其他任務。(2)各任務模塊運行時會出現某一任務運行時間過長而不能在單個系統時間片內完成的情況,可以將模塊分解為多個過程,分散到多個時間片內執行,通過變量產生消息聯結。(3)各任務模塊功能上應相互獨立,便于程序的調試和維護。(4)模塊之間需要相互配合工作時,采用一些公共單元進行通信以保持同步,合理地劃分任務模塊可以充分發揮多任務機制的優越性。
綜上所述,在單片機系統中實現多任務程序運行機制,需要由定時器定時產生時間片,由調度程序將時間片分配給各任務模塊使用。單片機內部提供了定時/計數器,可以實現毫秒級的定時,把定時中斷用于控制時間片的分配,可實現多個任務的分時輪換執行;同時,單片機的通用I/O口使用中斷可以實現對任務的控制。以定時器中斷為核心的軟件和硬件相互配合實現任務切換,任務中還可以設置若干個狀態,若干個優先級,CPU通過循環查詢各個任務,根據任務的不同狀態和優先級,決定該任務的執行策略。
在多任務機制中,需要根據系統運行的實際需求,合理地劃分出時間片,按照一定的策略分配給各個任務運行。系統時間片的大小取決于各個任務執行所需最小時間,通常應保證大多數任務在其規定的時間片內能夠執行完畢。對于一些耗時較長的任務,如按鍵掃描、串口通信和A/D等,需要進行任務細分,將任務分解為若干模塊,每個模塊分配到不同階段的時間片中去完成,經歷多個時間片之后綜合完成整個任務。實踐中,還必須多次反復試驗,才能選定最佳的時間片劃分方案。
對于典型的STM32控制系統,一般包括數據輸入、輸出、處理、顯示等模塊。顯示模塊一般采用8位LED數碼管,工作在動態顯示方式下,每位數碼管輪流顯示數據,為使數字顯示穩定,每一位數碼管的數據需要保持2~5 ms的時間,整個數碼管的顯示時間約為24~40 ms,無法在一個時間片內執行完畢,因此考慮將整個的顯示任務劃分為每個數碼管單獨顯示的模塊,每個模塊分散放入單個時間片內執行,通過時間片控制顯示的切換;輸入模塊一般采用4×4的矩陣鍵盤,整個鍵盤的識別需要經歷按鍵按下、防抖、判斷鍵值、判斷按鍵彈起等過程,往往需要300 ms甚至更長時間,因此也需要將任務劃分為多個階段,分散到各個時間片內執行;串行口模塊需要定時發送和接收數據,收發數據的時間很短,可以在單個時間片內執行完畢,因此主要考慮其執行頻率的需求。根據上述模塊的工作特點,將各模塊的工作頻率分配如下:顯示模塊,每個數碼管顯示保持4 ms,8個數碼顯示完成共需32 ms;鍵盤掃描任務每50 ms掃描一次,如果有按鍵按下,整個按鍵處理完成的時間約耗時500 ms;串行口每100 ms發送一次數據。因此,根據系統需求,系統任務的最高執行頻率為250,系統時間片設置為4 ms,由此計算出各子任務執行時的所需的計數器的值。
STM32系統使用定時器SysTick進行定時,設置定時時間為4 ms,即為系統的時間片,定時時間到產生中斷,同時設置計數變量進行計數。當每個時間片到來時,就執行其對應的任務函數,處理相應的任務。例如:計數值每到達1的倍數時,表明時間過去了4 ms,此時切換數碼管的顯示數據;計數值每到達10的倍數時,表明時間過去了40 ms,需要執行按鍵掃描任務;計數值每到達25的倍數時,表明時間過去了100 ms,此時應開始進行串行口的數據發送和接收。
對于各個模塊任務,在進行程序設計時,要使用任務處理函數、任務執行狀態、任務優先級等多個函數及參數,需要定義大量的變量來存儲和傳遞這些參數。隨著處理任務數量的增加,參數數量也隨之大規模增長,程序設計時極易產生混亂,且程序可讀性差,執行效率低。考慮到這些數據同屬于模塊任務運行時所需要的參數,嵌入式CPU一般不支持C++語言,無法使用C++語言的數據結構,可以采用C語言中類似的數據結構——結構體來組織封裝這些參數,使它們有機地組合成一個整體,程序結構清晰,不易出錯。STM32系統的運算能力很強,即使在程序設計時加入了一些較為復雜的數據結構,采用一些復雜的算法,并不會降低系統的性能,反而能夠清晰地描述算法,提高程序設計的效率。
因控制系統中處理的任務較多,且不同任務的處理方法又有所不同,為了在程序設計中有效地管理任務處理函數,定義了一個結構體,作為管理任務處理函數的核心。

上述結構體中,變量IsRunning標明任務的運行狀態,0表示尚未運行,1表示正在運行;變量Priotity標明任務的優先級(0~9),任務運行時判斷該標志按優先級先后依次執行;變量TaskRun為需要執行的任務處理函數的指針,通過指針對任務處理函數進行調用。
程序運行時,需要先將結構體其初始化,因此定義一個結構體數組,用于管理任務處理函數:


其中的TaskDisplay、TaskKeyScan、TaskSerial等均為系統的任務處理函數,由于使用數組進行管理,可以根據需求方便地進行增加和刪減,而無需對主程序做較大的改動。

程序執行時,通過循環遍歷整個數組,調用函數指針,執行相應的任務處理程序。根據相應的時間片分配,使得每個任務處理函數分時間片按頻率要求得到循環執行。在任務處理函數內部,根據運行狀態、優先級等參數的要求,再設計具體的實現算法。
上述程序在STM32系統上正確運行,顯示模塊、鍵盤控制、串行口通信等子任務在調度程序的控制之下正確工作,互不沖突,且所處理的任務可以方便地增加和刪減,程序設計方便。在單片機系統上已經初步實現了多任務的程序運行機制。
隨著控制系統復雜程度的不斷提升,現有單片機的程序運行機制不支持實時多任務運行,極大地限制了其在控制系統中的應用。針對這種程序運行機制的局限性,文章研究了嵌入式系統中多任務機制的實施原理,分析了實現多任務機制的基本條件,提出了實施多任務機制的策略,然后將多任務程序運行機制引入單片機系統,設計了一個基于分時機制的且具有一定優先級規則的多任務程序運行系統,并使用C語言編寫了程序。該程序使用C語言的結構體管理系統模塊的任務處理函數及其各類參數,程序可讀性好,編程效率高,通過數組調用函數指針執行相應的任務處理函數,可以根據需求方便的進行增加和刪減。程序合理地分配了任務地切換時間,大大增加了程序的運行效率,提升了單片機系統的工作效率,且編譯程序和構造算法具有快捷、簡便、開發周期短、容易調試和維護等特點,滿足了多任務系統的運行需求,程序也真正得以實用化。