如何寫出高效的單片機(jī)C語言程序代碼
與之比較的。PC
對于單片機(jī)來說就截然不同了,一般的單片機(jī)的Flash
機(jī)的資源是少得可憐,為此我們必須想法設(shè)法榨盡其所有資源,將它的性能發(fā)揮到最佳,程序設(shè)計(jì)時必須
遵循以下幾點(diǎn)進(jìn)行優(yōu)化:
1.
能夠使用字符型(char)定義的變量,就不要使用整型(int)變量來定義;能夠使用整型變量定義的變
量就不要用長整型(long
量后不要超過變量的作用范圍,如果超過變量的范圍賦值,C
而且這樣的錯誤很難發(fā)現(xiàn)。
2.
通常使用自加、自減指令和復(fù)合賦值表達(dá)式(如a-=1
程序代碼,編譯器通常都能夠生成inc
的指令,有很多C
3.
可以使用運(yùn)算量小但功能相同的表達(dá)式替換原來復(fù)雜的的表達(dá)式。
(1)
N=
說明:位操作只需一個指令周期即可完成,而大部分的C
完成,代碼長、執(zhí)行速度慢。通常,只要求是求2n
(2)
N=Pow(3,2)
說明:在有內(nèi)置硬件乘法器的單片機(jī)中(如51
的求平方是通過調(diào)用子程序來實(shí)現(xiàn)的,乘法運(yùn)算的子程序比平方運(yùn)算的子程序代碼短,執(zhí)行速度快。
(3)
N=M*8
N=M/8
說明:通常如果需要乘以或除以2n,都可以用移位的方法代替。如果乘以2n,都可以生成左移
的代碼,而乘以其它的整數(shù)或除以任何數(shù),均調(diào)用乘除法子程序。用移位的方法得到代碼比調(diào)用乘除法子
程序生成的代碼效率高。實(shí)際上,只要是乘以或除以一個整數(shù),均可以用移位的方法得到結(jié)果。如N=M*9
可以改為N=(M<<3)+M;
(4)
例如我們平時使用的延時函數(shù)都是通過采用自加的方式來實(shí)現(xiàn)。
void
{
UINT16
for(i=0;i
}
可以改為
void
{
UINT16
for(i=t;i>=0;i--)
for(j=1000;i>=0;j--)
}
說明:兩個函數(shù)的延時效果相似,但幾乎所有的C
個字節(jié),因?yàn)閹缀跛械腗CU
4.
void
{
while(t--)
{
NOP();
}
}
可以改為
void
{
do
{
NOP();
}while(--t)
}
說明:使用do…while
5.
void
{
while(*str
{
UARTSendByte(*str++)
}
}
可以改為
void
{
register
while(*pstr
{
UARTSendByte(*pstr++)
}
}
說明:在聲明局部變量的時候可以使用register
器中,而不是在堆棧中,合理使用這種方法可以提高執(zhí)行速度。函數(shù)調(diào)用越是頻繁,越是可能提高代碼的
速度,注意register
6.
volatile
哪里使用、在哪里失效,分析結(jié)果可以用于常量合并,常量傳播等優(yōu)化,進(jìn)一步可以死代碼消除。一般來
說,volatile
a)
b)
c)
總之,volatile
更改,比如:操作系統(tǒng)、硬件或者其它線程等。遇到這個關(guān)鍵字聲明的變量,編譯器對訪問該變量的代碼
就不再進(jìn)行優(yōu)化,從而可以提供對特殊地址的穩(wěn)定訪問。
7.
在數(shù)據(jù)校驗(yàn)實(shí)戰(zhàn)當(dāng)中,CRC16
校驗(yàn)值,效率更高,當(dāng)校驗(yàn)數(shù)據(jù)量大的時候,使用查表法優(yōu)勢更加明顯,不過唯一的缺點(diǎn)是占用大量的空
間。
//查表法:
code
0x0000,
0x8108,
0x1231,
0x9339,
0x2462,
0xa56a,
0x3653,
0xb75b,
0x48c4,
0xc9cc,
0x5af5,
0xdbfd,
0x6ca6,
0xedae,
0x7e97,
0xff9f,
0x9188,
0x1080,
0x83b9,
0x02b1,
0xb5ea,
0x34e2,
0xa7db,
0x26d3,
0xd94c,
0x5844,
0xcb7d,
0x4a75,
0xfd2e,
0x7c26,
0xef1f,
0x6e17,
};
UINT16
{
UINT16
UINT16
for(i
{
uncrcReg
^
uncrcConst
}
return
}
如果系統(tǒng)要求實(shí)時性比較強(qiáng),在CRC16
8.
首先不推薦所有函數(shù)改為宏函數(shù),以免出現(xiàn)不必要的錯誤。但是一些基本功能的函數(shù)很有必要使用宏
函數(shù)來代替。
UINT8
{
return
}
可以改為
#define
說明:函數(shù)和宏函數(shù)的區(qū)別就在于,宏函數(shù)占用了大量的空間,而函數(shù)占用了時間。大家要知道的是,函
數(shù)調(diào)用是要使用系統(tǒng)的棧來保存數(shù)據(jù)的,如果編譯器里有棧檢查選項(xiàng),一般在函數(shù)的頭會嵌入一些匯編語
句對當(dāng)前棧進(jìn)行檢查;同時,cpu
函數(shù)調(diào)用需要一些cpu
不會產(chǎn)生函數(shù)調(diào)用,所以僅僅是占用了空間,在頻繁調(diào)用同一個宏函數(shù)的時候,該現(xiàn)象尤其突出。
9.
作為程序員的我們會毫不猶豫地點(diǎn)擊鍵盤寫出以下的計(jì)算方法:
UINT16
{
UINT8
for(i=1;i<=100;i++)
{
s+=i;
}
return
}
很明顯大家都會想到這種方法,但是效率方面并不如意,我們需要動腦筋,就是采用數(shù)學(xué)算法解決問題,
使計(jì)算效率提升一個級別。
UINT16
{
UINT16
s=(100
return
}
結(jié)果很明顯,同樣的結(jié)果不同的計(jì)算方法,運(yùn)行效率會有大大不同,所以我們需要最大限度地通過數(shù)
學(xué)的方法提高程序的執(zhí)行效率。
10.
在許多種情況下,可以用指針運(yùn)算代替數(shù)組索引,這樣做常常能產(chǎn)生又快又短的代碼。與數(shù)組索引相
比,指針一般能使代碼速度更快,占用空間更少。使用多維數(shù)組時差異更明顯。下面的代碼作用是相同的,
但是效率不一樣。
UINT8
UINT8
UINT8
UINT8
for(i=0;i<64;i++)szArrayB[i]=szArrayA[i];
for(i=0;i<64;i++)szArrayB[i]=*p++;
指針方法的優(yōu)點(diǎn)是,szArrayA
方法中,每次循環(huán)中都必須進(jìn)行基于i
11.
C
可以提供程序效率,而且使程序更加之簡潔,由于強(qiáng)制轉(zhuǎn)換在C
個比較典型的例子作為講解。
例子1:將帶符號字節(jié)整型轉(zhuǎn)換為無符號字節(jié)整型
UINT8
INT8
a=(UINT8)b;
例子2:在大端模式下(8051
方法1:采用位移方法。
UINT8
UINT16
b=(a[0]<<8)|a[1];
結(jié)果:b=0x1234
方法2:強(qiáng)制類型轉(zhuǎn)換。
UINT8
UINT16
b=
結(jié)果:b=0x1234
例子3:保存結(jié)構(gòu)體數(shù)據(jù)內(nèi)容。
方法1:逐個保存。
typedef
{
UINT8
UINT8
UINT8
UINT8
UINT8
}ST;
ST
UINT8
s.a=1;
s.b=2;
s.c=3;
s.d=4;
s.e=5;
a[0]=s.a;
a[1]=s.b;
a[2]=s.c;
a[3]=s.d;
a[4]=s.e;
結(jié)果:數(shù)組a
方法2:強(qiáng)制類型轉(zhuǎn)換。
typedef
{
UINT8
UINT8
UINT8
UINT8
UINT8
}ST;
ST
UINT8
UINT8
UINT8
s.a=1;
s.b=2;
s.c=3;
s.d=4;
s.e=5;
for(i=0;i
a[i]=*p++;
}
評論