雖然單片機的處理能力低下,但是我們還是要盡量榨干它,以最少的資源干更多的事情,所以在單片機上進行多任務處理還是很常見的事情,任務多了,資源還是那些,每個任務得到執行的周期必定拉長,勢必會影響任務的實時性。
遇到這種情況,為了保證實時性,都會引入任務調度機制,對於ARM7或更高級的16位或32處理器,我們可以加入一個RTOS來處理,但RTOS的任務調度和系統開銷會占用很大一部分處理器資源的,對於一般的低檔8位機顯然難以承受。怎么辦?想想,引入RTOS不就是為了實時任務調度么,我們自己調度一下不就完了嘛,費那事。不過自己調度是挺費事的,不過8位機也不會安排太多任務,下面就介紹一種保證實時性的任務調度方法。
一般無OS的單片機程序都是在初始化后直接進入死循環,幾個要執行的任務都放在死循環中去周期循環調用。因為這個循環周期無法保證,所以死循環中每個任務被調用執行的周期就無法保證,當然具有實時性要求的任務就無法保證在規定時間內得到處理。現在以一個范例來說明這種任務調度方法,假定為某單片機安排了5個任務用Task1~Task5表示,其各任務的實時性要求和實測得最長執行時間如下表:
Task1
|
Task2
|
Task3
|
Task4
|
Task5
|
|
實時性要求(ms)
|
1
|
2
|
5
|
10
|
100
|
最長執行時間(ms)
|
0.1
|
0.2
|
0.5
|
0.3
|
1.2
|
由上表可以看出,五個任務放在一個死循環中順序執行,最長的執行周期達到了2.3ms,這對於Task1和Task2來說是無法忍受的,而且響應系統中斷也要花費時間,雖然一般系統中斷處理函數都很短。現假設有3個中斷處理函數Int1~Int3,其最小中斷周期和最長執行時間見下表:
Int1
|
Int2
|
Int3
|
|
最小中斷周期(ms)
|
0.2
|
2
|
10
|
最長執行時間(ms)
|
0.02
|
0.05
|
0.05
|
現在我要保證最短的Task1的1ms響應時間,必須使得程序初始化后進入的死循環執行周期在1ms內,於是乎我對某些任務進行以下加工:
Task3拆分成5個最長執行時間為0.1ms的任務Task3.1~Task3.5。
Task5拆分成4個最長執行時間為0.3ms的任務Task5.1~Task5.4。
做完拆分后將這些子任務安排到5個組(Class1~Class5)里,分配如下:
|
Class1
|
Class2
|
Class3
|
Class4
|
Class5
|
任務
|
Task1,Task2
Task3.1,Task4
|
Task1,Task3.2
Task5.1
|
Task1,Task2
Task3.3,Task5.2
|
Task1,Task3.4
Task5.3
|
Task1,Task2
Task3.5,Task5.4
|
最長執行時間(ms)
|
0.7
|
0.5
|
0.7
|
0.5
|
0.7
|
如上表,再算上期間各中斷的執行時間,每組的執行時間也不可能超過1ms吧。
好了,將這五個組按順序放在死循環里,並且保證被拆分的每個子任務在執行之前都去判斷排在它前頭的那個子任務是否被執行到,如果被執行過了則本段子任務執行,否則不執行(例Task3.3在執行前要去判斷Task3.2是否被執行,而且本段子程序在執行后要給出標志,告知下段子程序Task3.4我執行過了)。這么做看看能不能滿足每個任務要求的實時響應時間?都在允許范圍內吧。
如果希望Task1能夠實現精准的1ms定時執行,可以設置一個1ms的定時器來定時順序切換這5個組。
上面的這種做法也算是實現一種簡單的實時任務調度,其優點是:任務調度都在編程時做好了,在程序實際運行過程中幾乎不會占用額外的硬件資源和處理器時間。缺點是:編程麻煩,必須預先算准各任務和拆分的子任務的最長執行時間,不能有偏差,而且編寫出來的程序可讀性差。
總的來說,這種調度方法對於低檔8位單片機來說還是一種不錯的實時多任務的解決方案,行之有效,就是費點事。