琼海及奈物流有限公司

小容量單片機系統的C語(yǔ)言程序結構

當前位置:首頁(yè) >> 儀器儀表資訊

小容量單片機系統的C語(yǔ)言程序結構

引 言:

  2002年初,筆者著(zhù)手寫(xiě)一個(gè)IC卡預付費電表的工作程序,該電表使用Philips公司的8位51擴展型單片機87LPC764,要求實(shí)現很多功能,包括熄顯示、負荷計算與控制、指示閃爍以及電表各種參數的查詢(xún)等,總之,要使用時(shí)間的單元很多。筆者當時(shí)使用ASM51完成了這個(gè)程序的編寫(xiě),完成后的程序量是2KB多一點(diǎn)。后來(lái),由于種種原因,這個(gè)程序并沒(méi)有真正使用,只是作了一些改動(dòng)之后用在一個(gè)老化設備上進(jìn)行計時(shí)與負荷計算。約一年后,筆者又重新改寫(xiě)了這些代碼。

1 系統的改進(jìn)

  可以說(shuō),這個(gè)用ASM51實(shí)現的代碼是沒(méi)有什么組織性可言的,要什么功能就加入什么功能,弄得程序的結構非常松散,其實(shí)這也是導致筆者最終決定重新改寫(xiě)這些代碼的原因。

  大家知道,87LPC764有4KB的Flash ROM,而筆者的程序量只有2KB多點(diǎn),因而第一個(gè)想法是改用C語(yǔ)言作為主要的開(kāi)發(fā)語(yǔ)言,應該不至于導致代碼空間不夠用。其次,考慮到需要定時(shí)功能的模塊(或稱(chēng)任務(wù),以下統稱(chēng)任務(wù))較多,有必要對這些任務(wù)進(jìn)行有序的管理。筆者考慮使用時(shí)間片輪詢(xún)方式,即給每個(gè)要求時(shí)間管理的任務(wù)以一個(gè)時(shí)間間隔,時(shí)間間隔一到,即運行其代碼,達到合理使用系統定時(shí)器資源的目的。就51系統而言,一般至少一個(gè)定時(shí)器可用來(lái)進(jìn)行時(shí)間片的輪詢(xún);谝陨系南敕,構造了下述數據類(lèi)型。

typedef unsigned char uInt8
typedef struct {
void (*proc)(void); //處理程序
uInt8 ms_count; //時(shí)間片大小
} _op_;
數據結構定義好之后,接著(zhù)就是實(shí)現代碼,包括三部分,即初始化數據、時(shí)間片的刷新與時(shí)間到執行。
初始化數據。
#define proc_cnt 0x08 //定義過(guò)程或任務(wù)數量
//任務(wù)棧初始化
code _op_ Op[proc_cnt]={{ic_check,10},{disp_loop,100},\
{calc_power,150},{set_led,2},…\
};
//設置時(shí)間片初始值
data uInt8 time_val[proc_cnt]={10,100,150,2,…};
時(shí)間片刷新。
void time_int1(void) interrupt 3
{ uInt8 cnt;
Time_Counter:=Time_Unit;
for(cnt=0;cnt<proc_cnt;cnt++)
{ time_val[cnt]--;
}
}
任務(wù)的執行。
void main(void){
uInt8 cnt;
init(); //程序初始化
interrupt_on(); //打開(kāi)中斷
do{
for(cnt=0;cnt<proc_cnt;cnt++)
{ if(!time_val[cnt])
{ time_val[cnt]=Op[cnt].ms_count;
Op[cnt].proc();
}
}
}while(1);
}

  在上面的結構定義中,proc是不能帶參數的,各任務(wù)之間的通信可以定義一個(gè)參數內存塊,通過(guò)一種機制進(jìn)行數據信息交互,如定義一個(gè)全局變量。對于小容量單片機系統而言,需要這樣做的任務(wù)并不多,總任務(wù)量也不會(huì )太多,因而這種協(xié)調并不太難處理。

  也許大家都有這樣的認識,即一個(gè)實(shí)時(shí)系統中,差不多所有的具體任務(wù)都是有時(shí)間屬性的,即使是不需要定時(shí)的過(guò)程或任務(wù),也不見(jiàn)得要時(shí)時(shí)進(jìn)行查詢(xún)與刷新。如IC卡介質(zhì)檢測,保證每秒一次就足夠了。因而,這些任務(wù)也可以列入到這個(gè)結構中來(lái)。

  在以上的程序代碼中,考慮到單片機系統的RAM限制,不能像一些實(shí)時(shí)OS那樣將任務(wù)棧建立在RAM中。筆者將任務(wù)棧建立在代碼空間,因而不能在程序運行時(shí)動(dòng)態(tài)地加入任務(wù),因此要求在程序編譯時(shí),任務(wù)棧已經(jīng)確定。同時(shí),定義一組計數值旗標time_val,記錄程序運行時(shí)的時(shí)間量,并在一個(gè)定時(shí)器中斷中對其進(jìn)行刷新。改變時(shí)間片刷新中斷過(guò)程語(yǔ)句Time_Counter:=Time_Unit;中的Time_Unit,可以改變系統時(shí)間片的刷新粒度,一般這個(gè)值由系統的最小時(shí)間度量值確定。

  同時(shí),由任務(wù)的執行流程可知,此種系統構造并沒(méi)有改變其前/后臺系統的性質(zhì),只是對后臺邏輯操作序列進(jìn)行了有效管理。同時(shí),如果將任務(wù)執行流程進(jìn)行一些更改,并保證時(shí)間片小的任務(wù)前置,如下述程序。
do{
for(cnt=0;cnt<proc_cnt;cnt++){
if(!time_val[cnt]){
time_val[cnt]=Op[cnt].ms_count;
Op[cnt].proc();
break; //執行完成后,重新進(jìn)行優(yōu)先調度
}
}
}while(1);

  則系統變?yōu)橐粋(gè)以執行頻率為優(yōu)先級的任務(wù)調度系統。當然,設置此種方式得非常小心,并要注意時(shí)間片的分配,如果時(shí)間片過(guò)小,則可能導致執行頻率較低的任務(wù)難以被執行;而如果存在兩個(gè)同樣的時(shí)間片,則更加危險,可能導致第二個(gè)具有相同時(shí)間片的任務(wù)不被執行,因而,時(shí)間片的分配要合理,并保證其唯一性。

2 性能分析與任務(wù)拆分

  以上兩種任務(wù)管理方式,前一種按任務(wù)棧的順序與時(shí)間片的大小依次進(jìn)行調度,暫且稱(chēng)其為流水作業(yè)調度;而后一種,且稱(chēng)其為頻率優(yōu)先調度。兩種方式各有優(yōu)缺點(diǎn)。流水作業(yè)調度的各任務(wù)具有等同優(yōu)先級,時(shí)間片一到即會(huì )被按序調用,時(shí)間片大小的次序與唯一性不作要求;缺點(diǎn)是可能導致時(shí)間片小的,即要求執行得較快的任務(wù)等待過(guò)長(cháng)的時(shí)間。頻率優(yōu)先調度的各任務(wù)按其時(shí)間片的大小,即執行頻率劃分優(yōu)先級,時(shí)間片小的任務(wù),其執行頻率高,總是具有較高的優(yōu)先權,但時(shí)間片的分配得協(xié)調,否則可能會(huì )導致執行頻率低的任務(wù)長(cháng)時(shí)間等待。

  要特別注意的是,兩種方式都有可能導致一些任務(wù)長(cháng)時(shí)間等待,時(shí)間片所設定的時(shí)間也因此不能作為精確時(shí)間的依據,根據系統的要求或需要,甚至要在任務(wù)執行過(guò)程中進(jìn)行某些保護工作,如中斷屏蔽等,因而在進(jìn)行任務(wù)規劃時(shí)要注意。如果一個(gè)任務(wù)較繁瑣或可能要等待很長(cháng)時(shí)間,則應當考慮任務(wù)的拆分,把一個(gè)較大的任務(wù)細化為較小的任務(wù),把一個(gè)費時(shí)長(cháng)的任務(wù)劃分為多個(gè)費時(shí)小的任務(wù),協(xié)同完成其功能。如在等待時(shí)間長(cháng)的情況下,可附加一個(gè)定時(shí)任務(wù),定時(shí)任務(wù)到則發(fā)送一個(gè)消息旗標,主過(guò)程沒(méi)有檢測到消息旗標就馬上返回,否則繼續執行。下面是示例代碼,假定該任務(wù)將等待很長(cháng)時(shí)間,現將其拆分為兩個(gè)任務(wù)proc1與proc2協(xié)同完成原來(lái)的工作,proc1每100個(gè)時(shí)間單位執行一次,而proc2每200個(gè)時(shí)間單位執行一次。

//定義兩個(gè)任務(wù),并將其加入到任務(wù)棧中。
code _op_ Op[proc_cnt]={…,{proc1,100},{proc2,200}};
data int time1_Seg; //定義一個(gè)全局旗標
//任務(wù)實(shí)現
void proc1(void){
if (time1_Seg)
exit;
else
time1_Seg=const_Time1; //如果時(shí)間到了,則恢復初值并
//接著(zhù)執行下列代碼。
… //任務(wù)實(shí)際執行代碼
}

void proc2(void){ 
if(time1_Seg) 
time1_Seg--; 
}

  由上例可以看出,任務(wù)拆分后,幾乎不占過(guò)多的CPU時(shí)間,使得任務(wù)的等待時(shí)間大減,讓CPU有足夠的時(shí)間進(jìn)行任務(wù)管理與調度。同時(shí)也讓程序的結構性與可讀性大為加強。

結 語(yǔ)

  基于上述思路與結構對IC卡電表工作程序進(jìn)行全部改寫(xiě)后,系統的結構性能得到了很大改善。全部編寫(xiě)完成后,程序代碼量約為3KB多一點(diǎn),可見(jiàn)此種結構的程序構造并不會(huì )造成很大的系統開(kāi)銷(xiāo)(大部分開(kāi)銷(xiāo)是由于使用C的結果),卻使開(kāi)發(fā)得到了簡(jiǎn)化。| 電磁波轉換器 | 選配傳感器 | 測試儀器 | 電磁波測試儀這只要將系統細分為一系列任務(wù),然后加入到任務(wù)棧進(jìn)行編譯即可,很適合小容量單片機系統的開(kāi)發(fā),而筆者也在多個(gè)系統中成功地應用了此種結構。

發(fā)布人:2008/11/4 9:55:002219 發(fā)布時(shí)間:2008/11/4 9:55:00 此新聞已被瀏覽:2219次