文章編號(hào):1672-5913(2008)06-0098-02
摘要:本文探討了基于C語言的ARM嵌入式程序設(shè)計(jì)課程中學(xué)生反映不好掌握的一些知識(shí)點(diǎn)。
關(guān)鍵詞:ARM嵌入式;C語言;程序設(shè)計(jì);技巧
中圖分類號(hào):G642
文獻(xiàn)標(biāo)識(shí)碼:B
引言
嵌入式C語言程序設(shè)計(jì)是利用基本的C語言知識(shí),面向嵌入式工程實(shí)際應(yīng)用進(jìn)行程序設(shè)計(jì)語言。如何能夠在嵌入式系統(tǒng)開發(fā)中熟練、正確地運(yùn)用C語言開發(fā)出高質(zhì)量的應(yīng)用程序,是學(xué)習(xí)嵌入式程序設(shè)計(jì)的關(guān)鍵。下面介紹基于C語言的ARM嵌入式程序設(shè)計(jì)課程中學(xué)生反映不好掌握的一些難點(diǎn)。
1變量定義
先看下面一個(gè)例子:
char a; char a;
short b; char c;
char c; short b;
int d; int d;
這里定義的4個(gè)變量形式都一樣,只是次序不同,卻導(dǎo)致了在最終映像中不同的數(shù)據(jù)布局,如圖1所示。顯然,第2種方式節(jié)約了更多的存儲(chǔ)空間(Pad為無意義的填充數(shù)據(jù))。
由此可見,在變量聲明時(shí),最好把所有相同類型的變量放在一起定義,這樣可以優(yōu)化存儲(chǔ)器布局。
圖1變量在數(shù)據(jù)區(qū)里的布局
對(duì)于局部變量類型的定義,一般情況下,人們總是設(shè)法使用short或char來定義變量,以節(jié)省存儲(chǔ)空間;但是,當(dāng)一個(gè)函數(shù)的局部變量數(shù)目有限時(shí),其結(jié)果恰恰相反。因?yàn)榫幾g器會(huì)把局部變量分配給內(nèi)部寄存器,每個(gè)變量占用一個(gè)寄存器,如圖2所示。假定a1是任意可能的寄存器存儲(chǔ)函數(shù)的局部變量,同樣完成加1的操作,32位的int型變量最快,只用1條加法指令。而8位和16位變量,完成加法操作后,還需要在32位的寄存器中進(jìn)行括號(hào)擴(kuò)展,其中有符號(hào)的變量,要用邏輯左移(LSL)和算術(shù)右移(ASR)2條指令才能完成符號(hào)擴(kuò)展;無符號(hào)的變量,要使用1條邏輯“與”(AND)指令對(duì)符號(hào)位進(jìn)行清0。所以,使用32位int或unsingedint局部變量最有效率。
圖2不同類型局部變量的編譯結(jié)果
變量定義中,為了精簡程序,程序員總是竭力避免使用冗余變量。通常情況下這是正確的,但也有例外。
例如:冗余變量的使用與否比較。
int f(void);
int g(void);
int errs;
void test1(void)
{
errs+=f();
errs+=g();
}
void test2(void)
{
int localerrs=errs;
localerrs+=f();
localerrs+=g();
errs=localerrs;
}
在第一種情況test1()里,每次訪問全局變量errs時(shí),都要先從相應(yīng)的存儲(chǔ)器Load到寄存器里,經(jīng)f()或g()函數(shù)調(diào)用后再Store回原來的存儲(chǔ)區(qū)里面。在這個(gè)例子里,一共要進(jìn)行2次這樣的Load/Store操作(第一次是Load,第2次是Store)。而在第2種情況test2()里,局部變量localerrs被分配以寄存器,這樣一來,整個(gè)函數(shù)就只需1次Load/Store訪問全局變量存儲(chǔ)器。減少存儲(chǔ)器訪問的次數(shù)對(duì)于系統(tǒng)性能的改善是非常有好處的。
2參數(shù)傳遞
為了使單獨(dú)編譯的C語言程序和匯編程序能夠互相調(diào)用,定義了統(tǒng)一的函數(shù)過程調(diào)用標(biāo)準(zhǔn)ATPCS(ARM-Thumb Procedure Call Standard)。ATPCS定義了寄存器組中的{R0~R3}作為參數(shù)傳遞和結(jié)果返回寄存器。如果參數(shù)數(shù)目超過4個(gè),則使用堆棧進(jìn)行傳遞。由于內(nèi)部寄存器的訪問速度遠(yuǎn)遠(yuǎn)大于存儲(chǔ)器,所以要盡量使參數(shù)傳遞在寄存器里面進(jìn)行,即應(yīng)盡量把函數(shù)的參數(shù)控制在4個(gè)以下。例如:從C語言中直接調(diào)用匯編語言函數(shù):
extern void strcopy(char *d,const char *s);
int main(void){
const char src=“Source”;
char dest[10];
…
strcopy(dest,src);
…
}
AREAStrCopy,CODE,READONLY
EXPORT strcopy
strcopy
LDRBR2,[R1],#1
STRBR2,[R0],#1
CMP R2,#0
BNE strcopy
MOVPC,LR
END
例子中的函數(shù)strcopy(dest,src)用匯編來實(shí)現(xiàn)。根據(jù)ATPCS的定義,函數(shù)參數(shù)從左到右由寄存器進(jìn)行傳遞,所以在匯編中可直接由R0和R1進(jìn)行引用。這樣,在C語言和匯編語言之間進(jìn)行相互調(diào)用就容易實(shí)現(xiàn)了。
3循環(huán)條件
計(jì)數(shù)循環(huán)是程序中十分常用的流程控制結(jié)構(gòu)。在C語言中,常用下面累加計(jì)數(shù)的循環(huán)形式:
for(loop=1;loop<=limit;loop++)
這種累加計(jì)數(shù)的方法符合一般的自然思維習(xí)慣,所以下面這種遞減計(jì)數(shù)方法很少使用:
for(loop= limit;loop!=0;loop--)
這2種循環(huán)形式在邏輯上并沒有效率差異,但是映射到具體的體系結(jié)構(gòu)中時(shí),就產(chǎn)生了很大的不同,如圖3所示。
圖3不同的循環(huán)條件設(shè)置比較
從圖中可以發(fā)現(xiàn),累加法比遞減法多用了1條指令,當(dāng)循環(huán)次數(shù)比較大時(shí),這2段代碼就會(huì)在性能上產(chǎn)生明顯的差異。
其本質(zhì)原因是:當(dāng)進(jìn)行一個(gè)非0常數(shù)比較時(shí),必須用專門的CMP指令來執(zhí)行;而當(dāng)一個(gè)變量與0進(jìn)行比較時(shí),ARM指令則可直接利用條件執(zhí)行的特性(NE)來進(jìn)行判別。因此,在ARM的體系結(jié)構(gòu)下編程,最好采用遞減至0的方法來設(shè)置循環(huán)條件。
參考文獻(xiàn)
[1] 梁合慶等. 從C到嵌入式C編程語言[M]. 北京航空航天大學(xué)出版社,2000.
[2] 田澤. 嵌入式系統(tǒng)開發(fā)與應(yīng)用[M]. 北京航空航天大學(xué)出版社,2005.
[3] 杜春雷. ARM體系結(jié)構(gòu)與編程[M]. 北京:清華大學(xué)出版社,2003.
[4] 桑楠. 嵌入式系統(tǒng)原理及應(yīng)用開發(fā)技術(shù)[M]. 北京航空航天大學(xué)出版社,2002.