***c語言嵌入式系統程式設計修煉(三)
使用巨集定義在c
語言中,巨集是產生內嵌**的唯一方法。對於嵌入式系統而言,為了能達到效能要求,巨集是一種很好的代替函式的方法。
寫乙個「標準」巨集
min
,這個巨集輸入兩個引數並返回較小的乙個:
錯誤做法:
#define min(a,b)
( a <= b ? a : b )
正確做法:
#define min(a,b) ((a
)<= (b) ?
(a) : (b) )
對於巨集,我們需要知道三點:
(1)巨集定義「像」函式;
(2)巨集定義不是函式,因而需要括上所有「引數」;
(3)巨集定義可能產生***。
下面的**:
least = min(*p++, b);
將被替換為:
( (*p++) <= (b) ?(*p++):(b) )
發生的事情無法預料。因而不要給巨集定義傳入有***的「引數」。
使用暫存器變數
當對乙個變數頻繁被讀寫時,需要反覆訪問記憶體,從而花費大量的訪問時間。為此,
c語言提供了一種變數,即暫存器變數。這種變數存放在
cpu的暫存器中,使用時,不需要訪問記憶體,而直接從暫存器中讀寫,從而提高效率。暫存器變數的說明符是
register
。對於迴圈次數較多的迴圈控制變數及迴圈體內反覆使用的變數均可定義為暫存器變數,而迴圈計數是應用暫存器變數的最好候選者。
(1)
只有區域性自動變數和形參才可以定義為暫存器變數。因為暫存器變數屬於動態儲存方式,凡需要採用靜態儲存方式的量都不能定義為暫存器變數,包括:模組間全域性變數、模組內全域性變數、區域性
static
變數;(2) register
是乙個「建議」型關鍵字,意指程式建議該變數放在暫存器中,但最終該變數可能因為條件不滿足並未成為暫存器變數,而是被放在了儲存器中,但編譯器中並不報錯(在
c++語言中有另乙個「建議」型關鍵字:
inline
)。下面是乙個採用暫存器變數的例子:
/* 求
1+2+3+
….+n
的值*/
word addition(byte n)
return s;
}
本程式迴圈n次,
i和s都被頻繁使用,因此可定義為暫存器變數。
內嵌彙編
程式中對時間要求苛刻的部分可以用內嵌彙編來重寫,以帶來速度上的顯著提高。但是,開發和測試彙編**是一件辛苦的工作,它將花費更長的時間,因而要慎重選擇要用彙編的部分。
在程式中,存在乙個
80-20
原則,即
20%的程式消耗了
80%的執行時間,因而我們要改進效率,最主要是考慮改進那
20%的**。
嵌入式c
c程式中直接插入
_asm
內嵌彙編語句:
/* 把兩個輸入引數的值相加,結果存放到另外乙個全域性變數中
*/
int result;
void add(long a, long *b)
}
利用硬體特性
首先要明白
cpu對各種儲存器的訪問速度,基本上是:
cpu內部
ram>
外部同步
ram>
外部非同步
ram>
flash/rom
對於程式**,已經被燒錄在
flash
或rom
中,我們可以讓
cpu直接從其中讀取**執行,但通常這不是乙個好辦法,我們最好在系統啟動後將
flash
或rom
中的目標**拷貝入
ram中後再執行以提高取指令速度;
對於uart
等裝置,其內部有一定容量的接收
buffer
,我們應盡量在
buffer
被佔滿後再向
cpu提出中斷。例如計算機終端在向目標機通過
rs-232
傳遞資料時,不宜設定
uart
只接收到乙個
byte
就向cpu
提中斷,從而無謂浪費中斷處理時間;
如果對某裝置能採取
dma方式讀取,就採用
dma讀取,
dma讀取方式在讀取目標中包含的儲存資訊較大時效率較高,其資料傳輸的基本單位是塊,而所傳輸的資料是從裝置直接送入記憶體的(或者相反)。
dma方式較之中斷驅動方式,減少了
cpu
對外設的干預,進一步提高了
cpu與外設的並行操作程度。
活用位操作使用c
語言的位操作可以減少除法和取模的運算。在電腦程式中資料的位是可以操作的最小資料單位,理論上可以用「位運算」來完成所有的運算和操作,因而,靈活的位操作可以有效地提高程式執行的效率。舉例如下:
/* 方法
1 */
int i,j;
i = 879 / 16;
j = 562 % 32;
/* 方法
2 */
int i,j;
i = 879 >> 4;
j = 562 - (562 >> 5 << 5);
對於以2
的指數次方為「
*」、「
/」或「
%」因子的數**算,轉化為移位運算「
<< >>
」通常可以提高演算法效率。因為乘除運算指令週期通常比移位運算大。
c語言位運算除了可以提高運算效率外
,在嵌入式系統的程式設計中,它的另乙個最典型的應用,而且十分廣泛地正在被使用著的是位間的與(
&)、或(
|)、非(
~)操作,這跟嵌入式系統的程式設計特點有很大關係。我們通常要對硬體暫存器進行位設定,譬如,我們通過將
am186er
型80186
處理器的中斷遮蔽控制暫存器的第低
6位設定為
0(開中斷
2),最通用的做法是:
#define int_i2_mask 0x0040
wtemp = inword(int_mask);
outword(int_mask, wtemp &~int_i2_mask);
而將該位設定為
1的做法是:
#define int_i2_mask 0x0040
wtemp = inword(int_mask);
outword(int_mask, wtemp | int_i2_mask);
判斷該位是否為
1的做法是:
#define int_i2_mask 0x0040
wtemp = inword(int_mask);
if(wtemp & int_i2_mask)
上述方法在嵌入式系統的程式設計中是非常常見的,我們需要牢固掌握。
總結
在效能優化方面永遠注意
80-20
準備,不要優化程式中開銷不大的那
80%,這是勞而無功的。
巨集定義是
c語言中實現類似函式功能而又不具函式呼叫和返回開銷的較好方法,但巨集在本質上不是函式,因而要防止巨集展開後出現不可預料的結果,對巨集的定義和使用要慎而處之。很遺憾,標準
c至今沒有包括
c++中
inline
函式的功能,
inline
函式兼具無呼叫開銷和安全的優點。
使用暫存器變數、內嵌彙編和活用位操作也是提高程式效率的有效方法。
除了程式設計上的技巧外,為提高系統的執行效率,我們通常也需要最大可能地利用各種硬體裝置自身的特點來減小其運轉開銷,例如減小中斷次數、利用
dma傳輸方式等
C語言嵌入式系統程式設計修煉之道 目錄
c語言嵌入式系統程式設計修煉之道 背景篇 c語言嵌入式系統程式設計修煉之道 軟體架構篇 1.模組劃分 2.多工還是單任務 3.單任務程式典型架構 4.中斷服務程式 5.硬體驅動模組 6.c的物件導向化 總結 c語言嵌入式系統程式設計修煉之道 記憶體操作篇 1.資料指標 2.函式指標 3.陣列 vs....
C語言嵌入式系統程式設計修煉之三 記憶體操作
資料指標 在嵌入式系統的程式設計中,常常要求在特定的記憶體單元讀寫內容,彙編有對應的mov指令,而除c c 以外的其它程式語言基本沒有直接訪問絕對位址的能力。在嵌入式系統的實際除錯中,多借助c語言指標所具有的對絕對位址單元內容的讀寫能力。以指標直接操作記憶體多發生在如下幾種情況 1 某i o晶元被定...
C語言嵌入式系統程式設計修煉之記憶體操作
資料指標 在嵌入式系統的程式設計中,常常要求在特定的記憶體單元讀寫內容,彙編有對應的mov指令,而除c c 以外的其它程式語言基本沒有直接訪問絕對位址的能力。在嵌入式系統的實際除錯中,多借助c語言指標所具有的對絕對位址單元內容的讀寫能力。以指標直接操作記憶體多發生在如下幾種情況 1 某i o晶元被定...