2023年初,筆者著手寫乙個
ic卡預付費電表的工作程式,該電表使用
philips
公司的8位51
擴充套件型微控制器
87lpc764
,要求實現很多功能,包括熄顯示、負荷計算與控制、指示閃爍以及電表各種引數的查詢等,總之,要使用時間的單元很多。筆者當時使用
asm51
完成了這個程式的編寫,完成後的程式量是
2kb多一點。後來,由於種種原因,這個程式並沒有真正使用,只是作了一些改動之後用在乙個老化裝置上進行計時與負荷計算。約一年後,筆者又重新改寫了這些**。
1 系統的改進
可以說,這個用asm51實現的**是沒有什麼組織性可言的,要什麼功能就加入什麼功能,弄得程式的結構非常鬆散,其實這也是導致筆者最終決定重新改寫這些**的原因。
大家知道,87lpc764有
4kb的
flash rom
,而筆者的程式量只有
2kb多點,因而第乙個想法是改用
c語言作為主要的開發語言,應該不至於導致**空間不夠用。其次,考慮到需要定時功能的模組
(或稱任務,以下統稱任務
)較多,有必要對這些任務進行有序的管理。筆者考慮使用時間片輪詢方式,即給每個要求時間管理的任務以乙個時間間隔,時間間隔一到,即執行其**,達到合理使用系統定時器資源的目的。就
51系統而言,一般至少乙個定時器可用來進行時間片的輪詢。基於以上的想法,構造了下述資料型別。
typedef unsigned char uint8
typedef struct _op_;
資料結構定義好之後,接著就是實現**,包括三部分,即初始化資料、時間片的重新整理與時間到執行。
初始化資料。
#define proc_cnt 0x08 //定義過程或任務數量
//任務棧初始化
code _op_ op[proc_cnt]=,,
,,…
//設定時間片初始值
data uint8 time_val[proc_cnt]=;
時間片重新整理。
void time_int1(void) interrupt 3
while(1);
在上面的結構定義中,proc是不能帶引數的,各任務之間的通訊可以定義乙個引數記憶體塊,通過一種機制進行資料資訊互動,如定義乙個全域性變數。對於小容量微控制器系統而言,需要這樣做的任務並不多,總任務量也不會太多,因而這種協調並不太難處理。
也許大家都有這樣的認識,即乙個實時系統中,差不多所有的具體任務都是有時間屬性的,即使是不需要定時的過程或任務,也不見得要時時進行查詢與重新整理。如ic卡介質檢測,保證每秒一次就足夠了。因而,這些任務也可以列入到這個結構中來。
在以上的程式**中,考慮到微控制器系統的ram限制,不能像一些實時
os那樣將任務棧建立在
ram中。筆者將任務棧建立在**空間,因而不能在程式執行時動態地加入任務,因此要求在程式編譯時,任務棧已經確定。同時,定義一組計數值旗標
time_val
,記錄程式執行時的時間量,並在乙個定時器中斷中對其進行重新整理。改變時間片重新整理中斷過程語句
time_counter:=time_unit;
中的time_unit
,可以改變系統時間片的重新整理粒度,一般這個值由系統的最小時間度量值確定。
同時,由任務的執行流程可知,此種系統構造並沒有改變其前/後台系統的性質,只是對後台邏輯操作序列進行了有效管理。同時,如果將任務執行流程進行一些更改,並保證時間片小的任務前置,如下述程式。
dowhile(1);
則系統變為乙個以執行頻率為優先順序的任務排程系統。當然,設定此種方式得非常小心,並要注意時間片的分配,如果時間片過小,則可能導致執行頻率較低的任務難以被執行;而如果存在兩個同樣的時間片,則更加危險,可能導致第二個具有相同時間片的任務不被執行,因而,時間片的分配要合理,並保證其唯一性。
2 效能分析與任務拆分
以上兩種任務管理方式,前一種按任務棧的順序與時間片的大小依次進行排程,暫且稱其為流水作業排程;而後一種,且稱其為頻率優先排程。兩種方式各有優缺點。流水作業排程的各任務具有等同優先順序,時間片一到即會被按序呼叫,時間片大小的次序與唯一性不作要求;缺點是可能導致時間片小的,即要求執行得較快的任務等待過長的時間。頻率優先排程的各任務按其時間片的大小,即執行頻率劃分優先順序,時間片小的任務,其執行頻率高,總是具有較高的優先權,但時間片的分配得協調,否則可能會導致執行頻率低的任務長時間等待。
要特別注意的是,兩種方式都有可能導致一些任務長時間等待,時間片所設定的時間也因此不能作為精確時間的依據,根據系統的要求或需要,甚至要在任務執行過程中進行某些保護工作,如中斷遮蔽等,因而在進行任務規劃時要注意。如果乙個任務較繁瑣或可能要等待很長時間,則應當考慮任務的拆分,把乙個較大的任務細化為較小的任務,把乙個費時長的任務劃分為多個費時小的任務,協同完成其功能。如在等待時間長的情況下,可附加乙個定時任務,定時任務到則傳送乙個訊息旗標,主過程沒有檢測到訊息旗標就馬上返回,否則繼續執行。下面是示例**,假定該任務將等待很長時間,現將其拆分為兩個任務proc1與
proc2
協同完成原來的工作,
proc1
每100
個時間單位執行一次,而
proc2
每200
個時間單位執行一次。
//定義兩個任務,並將其加入到任務棧中。
code _op_ op[proc_cnt]=,};
data int time1_seg; //定義乙個全域性旗標
//任務實現
void proc1(void){
if (time1_seg)
exit;
else
time1_seg=const_time1; //如果時間到了,則恢復初值並
//接著執行下列**。
… //
任務實際執行**
void proc2(void){
if(time1_seg)
time1_seg--;
由上例可以看出,任務拆分後,幾乎不佔過多的cpu時間,使得任務的等待時間大減,讓
cpu有足夠的時間進行任務管理與排程。同時也讓程式的結構性與可讀性大為加強。
結語
基於上述思路與結構對ic卡電表工作程式進行全部改寫後,系統的結構效能得到了很大改善。全部編寫完成後,程式**量約為
3kb多一點,可見此種結構的程式構造並不會造成很大的系統開銷
(大部分開銷是由於使用
c的結果
),卻使開發得到了簡化。這只要將系統細分為一系列任務,然後加入到任務棧進行編譯即可,很適合小容量微控制器系統的開發,而筆者也在多個系統中成功地應用了此種結構。
時間片輪詢法
時間片輪詢法是一種比較簡單易用的系統架構之一,它對於系統中的任務排程演算法是分時處理。核心思路是把 cpu 的時間分時給各個任務使用。我們常用的定時方法是定時器,把排程器放在定時中,可以簡單的實現時間片輪詢法。需要注意的是,這種方法的前提是執行的每個任務都是短小精悍的,要不然乙個任務執行的時間過長,...
RT Thread 執行緒的時間片輪詢排程
優先順序和時間片是執行緒的兩個重要引數,分別描述執行緒競爭處理器資源的能力和持有處理器時間長短的能力。rt thread支援256個優先順序。數值越小,優先順序越高。0為最高優先順序,最低優先順序預留給空閒執行緒。使用者可以通過rt config.h中的rt thread priority max巨...
時間片輪轉
時間片輪轉排程演算法,這種演算法是將程序控制塊按照進入就緒佇列的先後次序排成佇列。關於就緒佇列的操作就是從隊頭摘下乙個程序控制塊和從隊尾掛入乙個程序控制塊。單處理器系統中程序控制塊分成乙個正在執行程序的程序控制塊 就緒程序的程序控制塊組織成的就緒佇列和等待程序的程序控制塊組成的等待佇列。由於實驗模擬...