如何寫出高效的單片機C語言程序代碼
與之比較的。PC
對于單片機來說就截然不同了,一般的單片機的Flash
機的資源是少得可憐,為此我們必須想法設(shè)法榨盡其所有資源,將它的性能發(fā)揮到最佳,程序設(shè)計時必須
遵循以下幾點進行優(yōu)化:
1.
能夠使用字符型(char)定義的變量,就不要使用整型(int)變量來定義;能夠使用整型變量定義的變
量就不要用長整型(long
量后不要超過變量的作用范圍,如果超過變量的范圍賦值,C
而且這樣的錯誤很難發(fā)現(xiàn)。
2.
通常使用自加、自減指令和復合賦值表達式(如a-=1
程序代碼,編譯器通常都能夠生成inc
的指令,有很多C
3.
可以使用運算量小但功能相同的表達式替換原來復雜的的表達式。
(1)
N=
說明:位操作只需一個指令周期即可完成,而大部分的C
完成,代碼長、執(zhí)行速度慢。通常,只要求是求2n
(2)
N=Pow(3,2)
說明:在有內(nèi)置硬件乘法器的單片機中(如51
的求平方是通過調(diào)用子程序來實現(xiàn)的,乘法運算的子程序比平方運算的子程序代碼短,執(zhí)行速度快。
(3)
N=M*8
N=M/8
說明:通常如果需要乘以或除以2n,都可以用移位的方法代替。如果乘以2n,都可以生成左移
的代碼,而乘以其它的整數(shù)或除以任何數(shù),均調(diào)用乘除法子程序。用移位的方法得到代碼比調(diào)用乘除法子
程序生成的代碼效率高。實際上,只要是乘以或除以一個整數(shù),均可以用移位的方法得到結(jié)果。如N=M*9
可以改為N=(M<<3)+M;
(4)
例如我們平時使用的延時函數(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é),因為幾乎所有的MCU
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)化,進一步可以死代碼消除。一般來
說,volatile
a)
b)
c)
總之,volatile
更改,比如:操作系統(tǒng)、硬件或者其它線程等。遇到這個關(guān)鍵字聲明的變量,編譯器對訪問該變量的代碼
就不再進行優(yōu)化,從而可以提供對特殊地址的穩(wěn)定訪問。
7.
在數(shù)據(jù)校驗實戰(zhàn)當中,CRC16
校驗值,效率更高,當校驗數(shù)據(jù)量大的時候,使用查表法優(yōu)勢更加明顯,不過唯一的缺點是占用大量的空
間。
//查表法:
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)要求實時性比較強,在CRC16
8.
首先不推薦所有函數(shù)改為宏函數(shù),以免出現(xiàn)不必要的錯誤。但是一些基本功能的函數(shù)很有必要使用宏
函數(shù)來代替。
UINT8
{
return
}
可以改為
#define
說明:函數(shù)和宏函數(shù)的區(qū)別就在于,宏函數(shù)占用了大量的空間,而函數(shù)占用了時間。大家要知道的是,函
數(shù)調(diào)用是要使用系統(tǒng)的棧來保存數(shù)據(jù)的,如果編譯器里有棧檢查選項,一般在函數(shù)的頭會嵌入一些匯編語
句對當前棧進行檢查;同時,cpu
函數(shù)調(diào)用需要一些cpu
不會產(chǎn)生函數(shù)調(diào)用,所以僅僅是占用了空間,在頻繁調(diào)用同一個宏函數(shù)的時候,該現(xiàn)象尤其突出。
9.
作為程序員的我們會毫不猶豫地點擊鍵盤寫出以下的計算方法:
UINT16
{
UINT8
for(i=1;i<=100;i++)
{
s+=i;
}
return
}
很明顯大家都會想到這種方法,但是效率方面并不如意,我們需要動腦筋,就是采用數(shù)學算法解決問題,
使計算效率提升一個級別。
UINT16
{
UINT16
s=(100
return
}
結(jié)果很明顯,同樣的結(jié)果不同的計算方法,運行效率會有大大不同,所以我們需要最大限度地通過數(shù)
學的方法提高程序的執(zhí)行效率。
10.
在許多種情況下,可以用指針運算代替數(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)點是,szArrayA
方法中,每次循環(huán)中都必須進行基于i
11.
C
可以提供程序效率,而且使程序更加之簡潔,由于強制轉(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:強制類型轉(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:強制類型轉(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++;
}
評論