PICC 代碼優(yōu)化技巧_第1頁
PICC 代碼優(yōu)化技巧_第2頁
PICC 代碼優(yōu)化技巧_第3頁
PICC 代碼優(yōu)化技巧_第4頁
PICC 代碼優(yōu)化技巧_第5頁
已閱讀5頁,還剩6頁未讀 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、常用優(yōu)化技巧: 要減少 bank 切換,把在不同 bank 里的變量放到一起。 在初始化代碼里,在程序的開頭,注意初始化的順序一開始所有的變量放在bankO,然后放bankl,接著bank2, bank3。在初始化代碼里可能有些變量不需要初始化。在可能的地方,掉換操作數(shù)的順序來使編譯器避免多余使 用 W 寄存器或臨時位置。對于數(shù)學運算,表達式里的變量盡量要在同一個bank里,以避免過多的 bank 切換。如果可能,盡可能地采用字節(jié) byte 運算代替字 word 運算。如果可能,對于數(shù)組元素的訪問盡量采用指針而不是用下 標索引。注意在一個小的循環(huán)里使用指針時,管理循環(huán)多出 的代碼抵銷了使用指針

2、節(jié)省下來的代碼,所以使用兩種方法 差不多。一系列的:ifelse ifelse if . 通常會比 case 語句產生更小的代碼。在switch - case里,改變常量為有順序的數(shù)據,不要有 間隔。依靠 bank 切換必需的:Depending on the bank switching required:var = valuel;if (!flag)var = value2;產生更理想的代碼:if (flag)var = valuel;elsevar = value2;只是要確認在該代碼執(zhí)行時不要在中斷里使用這個 var。清零,遞增,以及遞減一個字節(jié) byte 是單指令的操作。給 一個字節(jié)

3、賦值需要兩條指令(value - W, and W - byte).只要可能,盡量使用bits代替unsigned chars。置位Bit sets, 清零clears,以及位測試跳轉等都是單條指令。因為不能在 函數(shù)里申明位變量,你可以全局聲明位變量。調用函數(shù)會產生一些管理代碼。嘗試著用一些宏marcro代 替你的一些小一點的函數(shù)。如果堆??臻g允許,大塊的重復代碼應該由函數(shù)及函數(shù)調 用來替代。當前邏輯的優(yōu)化。我還只是剛接到一個固定要求的項目, 于是我嘗試著把代碼寫得非常靈活。當我快接近項目結束時, 我發(fā)現(xiàn)一些彈性代碼不再需要了,這樣可以刪除它來節(jié)省代碼。優(yōu)化提示 l : Signed vs.

4、Unsigned 變量比較使用 signed 和 unsigned 變量的匯編代碼,你會發(fā)現(xiàn)在比 較有符號signed變量時會多出一些指令。結論1:盡可能地使用 unsigned 的 int 或 char。優(yōu)化提示2:基于字節(jié)Byte的循環(huán)Loops這里有兩塊代碼,它們做的完全是同樣的事情。但是其中一個要完成得快 25,并且使用更小的 RAM 空間,你能挑出 是哪一個嗎?unsigned char i;for(i=O;i25O;i+) do_func(); /executes do_func() 25O times, in3.25msfor(i=25O;i!=O;i-) do_func();

5、/executes do_func() 25O times, in2.5ms要找出這個,我們來看一下產生的匯編代碼for(i=O;i8)#define lobyte(x) (unsigned char)(x&0 xff)/the optimizer takes care of using the hi/lo correct byte of integerLoops to avoid with timeouts: 320000 to 380000 cycles for 20000 iterations.for(timeout=0;timeout20000;timeout+) do_func();

6、 /380011 cyclesfor(timeout=20000;timeout!=0;timeout-) do_func(); /320011 cycles |Best loop for a timeout: 295000 cycles for 20000 iterations./we want to execute do_func() approx. 20000 times before timing outtimeout=(20000/0 x100)*0 x100; /keeps lobyte(timeout)=0, which speeds up assignmentsfor(;hib

7、yte(timeout)!=0;timeout-) do_func(); /295704 cyclesNotice the features of the loop shown above.它在每個循環(huán)里只測試整型數(shù)的高位字節(jié)。它檢查這個字節(jié)是否到零,所以很快。當初始化 timeout 變量時,它又一個好處是:匯編代碼清零一個ram變量只要一條指令,而賦值則需要兩條指令。結論 3:盡可能地采用遞減到零的循環(huán),因為檢查 ram 變量是否為 零更簡單。在延時循環(huán)里只查詢整型數(shù)的高位字節(jié),這要快一些。給整型數(shù)賦值時,對 ram 變量清零要比賦一個數(shù)值要快一 些。優(yōu)化提示4:使用內部定時器的Timeo

8、ut定時循環(huán)當然,使用芯片片內定時器并檢查中斷的方式是最快的定時 循環(huán)。它通常會比用延時循環(huán)快 70左右。/set up tmr0 to set flag T0IF high when it rolls over while(RA0=0 & !T0IF); /wait until port goes high結論4: 盡可能地使用內建的定時器及中斷標志。優(yōu)化提示 5: Case 語句慢而且效率低c=getch();switch(c)case A:do something;break;case H:do something;break;case Z:do something;break; 快且高

9、效率c=getch();switch(c)case 0:do something;break;case 1:do something;break;case 2:do something;break;Hi-Tech C的優(yōu)化器會盡可能地把switch語句變成計算偏移的 goto。結論 5: 盡可能地在 case 語句里使用連續(xù)的數(shù)字。優(yōu)化提示6: Hi-Tech C里的除法如果你使用Hi-Tech C,在你程序的任何位置有任何數(shù)學除法 的運算,就將會使用到bankO里13到23個字節(jié)的空間,以 及一些 EPROM/Flash 程序空間。盡管變量不在bankO,這一樣會發(fā)生。OccurrenceA

10、ny mathematical division at all in the entire program using a variable of type long, even if all variables do not reside in bankO.RAM usage23 bytes in bankOROM/flash usagelarge, it has to include ldiv routinesFix/ExplanationUse combinations of bit shifts ie: x=x*6 is replaced by x1=x;x2=x;x=x12 + x2

11、Install Language Tool: Language Suitehi-tech piccTool Name PICC CompilerExecutable c:hi-picinpicc.exe (假如你的 PICC 是默認安 裝的)選 Command-line最后OK.上面這步只需要設定一次,除非你重新安裝了MPLAB.3:創(chuàng)建你的項目文件:(假如你實現(xiàn)用EDIT編輯好了一 個叫AA.C的C代碼文件)Project-New Project-File Name-myc (假如我們把項目 文件取名字叫MYC.PJT) 右邊窗口當然要選擇中你的工作目錄然后0K.4:設定你的PICC 工作參

12、數(shù):Project-Edit Project 上面4個欄目就用默認的,空的也就讓它空著,無所謂的 需要修改的是:Development Mode 選擇你的PIC型號.當然要選擇Mplab SIM Simulator讓你可以用軟件仿真.Language Tool Suite-HI-TECH PICC 上面的步驟,你可能會遇見多個提示條,不要管它,一路確 定.下面是PICC編譯器的選擇項:雙擊Project Files窗口里面的MYC. HEX,出現(xiàn)一個選擇 攔目命令很多,大家可以看PICC文本編輯器里面的HELP,里面有詳細說明. 下面就推薦幾個常用也是建議用的:Generate debug i

13、nfo 以及下面的 2項.Produce assembler list file 就在它們后面打勾即可,其它的不要管,除非你有特殊要求 5:添加你的C代碼文件:當進行了前面幾步后,按Add Node找到AA.C文件就0 K了.6:編譯C代碼:最簡單的一步:直接按下F10.編譯完后,會出現(xiàn)各種調試信息.C代碼對應的匯編代碼就 是工作目錄里面的AA.IST,用EDIT 打開可以看見詳細的對比.7:其它,要是一切都沒問題,那么你就可以調試和燒片了, 和以往操作無異.2、如何從匯編轉向 PICC首先要求你要有C語言的基礎。PICC不支持C+,這對于 習慣了 C+的朋友還得翻翻C語言的書。C代碼的頭文件

14、一定要有#include,它是很多頭文件的集 合, C 編譯器在 pic.h 中根據你的芯片自動栽 入相應的其它頭文件。這點比匯編好用。載入的頭文件中其 實是聲明芯片的寄存器和一些函數(shù)。順便摘抄一個片段:static volatile unsigned char TMR0 0 x01;static volatile unsigned char PCL 0 x02;static volatile unsigned char STATUS 0 x03;可以看出和匯編的頭文件中定義寄存器是差不多的。如下:TMR0 EQU 0X01;PCL EQU 0X02;STATUS EQU 0X03; 都是把無

15、聊的地址定義為大家公認的名字。 一:怎么附值?如對 TMR0 附值,匯編中:MOVLW 200;MOVWF TMR0; 當然得保證當前頁面在 0,不然會出錯。C 語言:TMR0=200; 無論在任何頁面都不會出錯??梢钥闯鰜鞢是很直接了當?shù)?。并且最大好處是操作一個寄 存器時候,不用考慮頁面的問題。一切由C 自動完成。二:怎么位操作?匯編中的位操作是很容易的。在C中更簡單。C的頭文件中 已經對所有可能需要位操作的寄存器的每 一位都有定義名稱:如:PORTA 的每一個 I/O 口定義為:RAO、RAI、RA2。RA7。OPTION的每一位定義為:PS0、PSI、PS2、PSA、T0SE、T0CS、

16、INTEDG、RBPU。可以 對其直接進行運算和附值。如:RAO=O;RA2=1;在匯編中是:BCF PORTA, O;BSF PORTA, 2;可以看出2者是大同小異的,只是C中不需要考慮頁面的問 題。三:內存分配問題: 在匯編中定義一個內存是一件很小心的問題,要考慮太多的 問題,稍微不注意就會出錯。比如16 位的運算等。用 C 就不需要考慮太多。下面給個例子:16位的除法(C代碼):INT X=5OOO;INT Y=1OOO;INT Z=X/Y; 而在匯編中則需要花太多精力。 給一個小的 C 代碼,用 RAO 控制一個 LED 閃爍: #include void main()int x;C

17、MCON=0B111; 掉A 口比較器,要是有比較器功能的話。ADCON1=0B110; 掉A/D功能,要是有A/D功能的話。 TRISA=O; /RA 口全為輸出。loop:RAO=!RAO; for(x=6OOOO;-x;); /延時 goto loop;說說RA0=! RA0的意思:PIC對PORT寄存器操作都是先 讀取修改寫入。上句的含義是程序先讀RA0,然后取反,最后把運算后的值重新寫入RA0,這就 實現(xiàn)了閃爍的功能。3、淺談 PICC 的位操作由于 PIC 處理器對位操作是最高效的,所以把一些 BOOL 變 量放在一個內存的位中,既可以達到運算速度快,又可以達到最大限度節(jié)省空間的目

18、的。在 C 中的位 操作有多種選擇。如:char x;x=xl0B00001000; /*對 X 的 4 位置 1。*/char x;x=x&0B11011111; /*對 X 的 5 位清0。 */ 把上面的變成公式則是:#define bitset(var,bitno)(var |=1bitno) #define bitclr(var,bitno)(var &=(1bitno) 則上面的操作就是: char x;bitset(x,4)char x;bitclr(x,5)但上述的方法有缺點,就是對每一位的含義不直觀,最好是 能在代碼中能直觀看出每一位代表的意思,這樣就能提高編程效率,避免出錯

19、。如果我們想用X的0-2位 分別表示溫度、電壓、電流的 BOOL 值可以如下:unsigned char x 0 x20; /*象匯編那樣把 X 變量定義到一個 固定內存中。 */bit temperature (unsigned)&x*8+0; /*溫度*/bit voltage (unsigned)&x*8+1; /*電壓*/bit current (unsigned)&x*8+2; /*電流 */這樣定義后X的位就有一個形象化的名字,不再是枯燥的1、2、3、4 等數(shù)字了??梢詫?X 全局修改, 也可以對每一位進行操作:char=255; temperature=0;if(voltage)

20、還有一個方法是用 C 的 struct 結構來定義:如:struct cypok temperature:1; /*溫度*/ voltage:1; /*電壓*/current:1; /*電流*/none:4;x 0 x20;這樣就可以用 x.temperature=0;if(x.current)等操作了。上面的方法在一些簡單的設計中很有效,但對于復雜的設計 中就比較吃力。如象在多路工業(yè)控制上。前端需要分別收集多路的多路信號,然后再設定控制多路的 多路輸出。如:有 2 路控制,每一路的前端信號有溫度、電壓、電流。后端控制有電機、喇叭、繼電器、LED。如果用匯編來實現(xiàn)的話,是很頭疼的事情,用 C

21、來實現(xiàn)是很輕松的事情,這里也涉及到一點 C 的 內存管理(其實 C 的最大優(yōu)點就是內存管理)。 采用如下結構:union cypok struct outmotor:1; /*電機*/ relay:1; /*繼電器*/ speaker:1; /*喇叭*/led1:1; /*指示燈*/led2:1; /*指示燈*/out;struct innone:5;temperature:1; /*溫度*/ voltage:1; /*電壓*/current:1; /*電流*/in;char x;union cypok an1;union cypok an2; 上面的結構有什么好處呢? 細分了信號的路 an1

22、 和 an2; 細分了每一路的信號的類型(是前端信號 in 還是后端信號 out):an1.in ;an1.out;an2.in;an2.out; 然后又細分了每一路信號的具體含義,如: an1.in.temperature;an1.out.motor; an2.in.voltage;an2.out.led2; 等這樣的結構很直觀的在 2 個內存中就表示了 2 路信號。并且 可以極其方便的擴充。如添加更多路的信號,只需要添加:union cypok an3;union cypok an4; 從上面就可以看出用 C 的巨大好處4、PICC 之延時函數(shù)和循環(huán)體優(yōu)化。很多朋友說 C 中不能精確控制延

23、時時間,不能象匯編那樣直 觀。其實不然,對延時函數(shù)深入了解一下就能設計出 一個理想的 框價出 來。 一般的我們都用 for(x=100;-x;);此句等同與 x=100;while(-x);或 for(x=0;x100;x+);。來寫一個延時函數(shù)。在這里要特別注意:X=100,并不表示只運行100個指令時 間就跳出循環(huán)??梢钥纯淳幾g后的匯編:x=100;while(-x);匯編后:movlw 100bcf 3,5bcf 3,6movwf _delayl2 decfsz _delaygoto l2return從代碼可以看出總的指令是是303個,其公式是8+3*(X-1)。 注意其中循環(huán)周期是 X

24、-1 是 99 個。這里總結的是 x 為 char 類型的循環(huán)體,當 x 為 int 時候,其中 受 X 值的影響較大。建議設計一個 char 類型的 循環(huán)體,然后再用一個循環(huán)體來調用它,可以實現(xiàn)精確的長 時間的延時。下面給出一個能精確控制延時的 函數(shù),此函數(shù)的匯編代碼是最簡潔、最能精確控制指令時間 的:void delay(char x,char y)char z;doz=y;do;while(-z);while(-x);其指令時間為:7+(3* (Y-1) +7)*(X-1 )如果再加上函數(shù)調用的 call 指令、頁面設定、傳遞參數(shù)花掉的7個指令。則是:14+(3* (Y-1) +7)*

25、(X-1)。如 果要求不是特別嚴格的延時,可以用這個函數(shù):void delay()unsigned int d=1000;while(-d);此函數(shù)在4M晶體下產生10003us的延時,也就是10MS。 如果把D改成2000,則是20003us,以此類推。有朋友不明白,為什么不用 while(x-)后減量,來控制設 定 X 值是多少就循環(huán)多少周期呢?現(xiàn)在看看編 譯它的匯編代碼:bcf 3,5bcf 3,6movlw 10movwf _delayl2decf _delayincfsz _delay,wgoto l2return可以看出循環(huán)體中多了一條指令,不簡潔。所以在 PICC 中 最好用前減

26、量來控制循環(huán)體。再談談這樣的語句:for(x=100;-x;);和 for(x=0;x100;x+);從字面上看 2 者意思一樣,但可以通過匯編查看代碼。后者 代碼雍長,而前者就很好的匯編出了簡潔的代碼。所以在PICC中最好用前者的形式來寫循環(huán)體,好的C編 譯器會自動把增量循環(huán)化為減量循環(huán)。因為 這是由處理器硬件特性決定的。 PICC 并不是一個很智能的 C 編譯器,所以還是人腦才是第一的,掌握一些 經驗對寫出高效,簡潔的代碼是有好處的。5、深入探討PICC之位操作一:用位操作來做一些標志位,也就是BOOL變量可以 簡單如下定義:bit a,b,c;PICC會自動安排一個內存,并在此內存中自動

27、安排一位 來對應a,b,c由于我們只是用它們來簡單的表示一些0,1信息,所以我們不需要詳細的知道它們的地 址位究竟是多少,只管拿來就用好了.二:要是需要用一個地址固定的變量來位操作,可以參照PIC. H里面定義寄存器.如:用2 5H內存來定義8個位變量.static volatile unsigned char myvar 0 x25;static volatile bit b7 (unsigned)&myvar*8+7;static volatile bit b6 (unsigned)&myvar*8+6;static volatile bit b5 (unsigned)&myvar*8+5

28、;static volatile bit b4 (unsigned)&myvar*8+4;static volatile bit b3 (unsigned)&myvar*8+3;static volatile bit b2 (unsigned)&myvar*8+2;static volatile bit b1 (unsigned)&myvar*8+1;static volatile bit b0 (unsigned)&myvar*8+0;這樣即可以對MYVAR操作,也可以對B0-B7直接 位操作.但不好的是,此招在低檔片子,如C5X系列上可能會出問 題.還有就是表達起來復雜,你不覺得輸入代碼受

29、累么?呵呵 三:這也是一些常用手法:#define testbit(var, bit) (var) & (1 (bit) 測試某一位, 可以做BOOL運算#define setbit(var, bit) (var) |= (1 (bit) 把某一位置1#define clrbit(var, bit) (var) &= (1 (bit) 把某一位清 0付上一段代碼,可以用MPLAB調試觀察#include#define testbit(var, bit) (var) & (1 (bit)#define setbit(var, bit) (var) |= (1 (bit)#define clrbi

30、t(var, bit) (var) &= (1 (bit) char a,b;void main() char myvar; myvar=0B10101010;a=testbit(myvar,0); setbit(myvar,0);a=testbit(myvar,0); clrbit(myvar,5);b=testbit(myvar,5); if(!testbit(myvar,3) a=255;elsea=100;while(1);四:用標準C的共用體來表示:#include union var unsigned char byte;struct unsigned b0:1, b1:1, b2

31、:1, b3:1, b4:1, b5:1, b6:1, b7:1; bits;char a,b;void main()static union var myvar; myvar.byte=0B10101010;a=myvar.bits.b0;b=myvar.bits.b1; if(myvar.bits.b7) a=255;elsea=100; while(1);五:用指針轉換來表示:#include typedef struct unsigned b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1; bits; / 先定義一個變量的位#define m

32、ybit0 (bits *)&myvar)-b0) / 取 myvar 的 地 址(&myvar )強制轉換成bits類型的指針#define mybit1 (bits *)&myvar)-b1)#define mybit2 (bits *)&myvar)-b2)#define mybit3 (bits *)&myvar)-b3)#define mybit4 (bits *)&myvar)-b4)#define mybit5 (bits *)&myvar)-b5)#define mybit6 (bits *)&myvar)-b6)#define mybit7 (bits *)&myvar)-b

33、7) char myvar;char a,b;void main() myvar=0B10101010;a=mybit0;b=mybit1; if(mybit7) a=255;else a=100;while(1); 六:五的方法還是煩瑣,可以用粘貼符號的形式來簡化它 #include typedef struct unsigned b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1; bits;#define _paste(a,b) a#b#define bitof(var,num) (bits *)&(var)-_paste(b,num)char

34、myvar;char a,b;void main() a=bitof(myvar,0);b=bitof(myvar,1); if(bitof(myvar,7) a=255;else a=100;while(1);有必要說說#define _paste(a,b) a#b的意思: 此語句是粘貼符號的意思,表示把b符號粘貼到a符號之后. 例子中是a=bitof(myvar,0);(bits *)&(myvar)-_paste(b,O) (bits *)&(var)-bO) 可以看出來,_paste(b,O)的作用是把0粘貼到了 b后面,成 了 b0 符號.總結:C語言的優(yōu)勢是能直接對低層硬件操作,代

35、碼可以非 常非常接近匯編,上面幾個例子的位操作代碼 是10 0%的達到匯編的程度的另一個優(yōu)勢是可讀性高, 代碼靈活.上面的幾個位操作方法任由你選, 你不必擔心會產生多余的代碼量出來6、在 PICC 中使用常數(shù)指針。 常數(shù)指針使用非常靈活,可以給編程帶來很多便利。我測試 過,PICC也支持常數(shù)指針,并且也會自動 分頁,實在是一大喜事。定義一個指向8位RAM數(shù)據的常數(shù)指針(起始為0 x00):#define DBYTE (unsigned char volatile *) 0)定義一個指向16位RAM數(shù)據的常數(shù)指針(起始為0 x00):#define CWORD (unsigned int vol

36、atile *) 0)(unsigned char volatile *) 0) 中的 0 表示指向 RAM 區(qū)域的起 始地址,可以靈活修改它。DBYTEx中的x表示偏移量。下面是一段代碼1:char a1,a2,a3,a4;#define DBYTE (unsigned char volatile *) 0)void main(void)long cc=0 x89abcdef;a1=DBYTE0 x24; a2=DBYTE0 x25;a3=DBYTE0 x26;a4=DBYTE0 x27;while(1);2:char a1,a2,a3,a4;#define DBYTE (unsigned

37、char volatile *) 0)void pp(char y) a1=DBYTEy+;a2=DBYTEy+; a3=DBYTEy+;a4=DBYTEy;void main(void)long cc=0 x89abcdef; char x;x=&cc;pp(x);while(1);3:char a1,a2,a3,a4;#define DBYTE (unsigned char volatile *) 0)void pp(char y) a1=DBYTEy+;a2=DBYTEy+; a3=DBYTEy+;a4=DBYTEy;void main(void)bank1 static long cc

38、=0 x89abcdef;char x;x=&cc;pp(x);while(1);7、PICC 關于 unsigned 和 signed 的幾個關鍵問題! unsigned 是表示一個變量(或常數(shù))是無符號類型。 signed 表 示有符號。它們表示數(shù)值范圍不一樣。PICC默認所有變量都是unsigned類型的,哪怕你用了 signed 變量。因為有符號運算比無符號運算耗資源,而且 MCU 運算一般不涉及有符號運算。在 PICC 后面加上-SIGNED_CHAR 后綴可以告訴 PICC 把 signed 變量當作有符號處理。在 PICC 默認的無符號運算下看這樣的語句:char i;for(i

39、=7;i=0;i-); / 中間語句這樣的 C 代碼看上去是沒有丁點錯誤的,但編譯后,問題出 現(xiàn)了:movlw 7movwf iloop/中間語句decf i /只是遞減,沒有判斷語句!goto loop原因是當i是0時候,條件還成立,還得循環(huán)一次,直到i成 負 1 條件才不成立。而 PICC 在默認參數(shù)下是不能判斷負數(shù)的,所以編譯過程出現(xiàn)問題。那么采用這樣的 語句來驗證:char i;i=7;while(1)i-;/中間語句if(i=0)break; 告訴PICC以判斷i是否是0來作為條件編譯后代碼正確:movlw 7movwf iloop/中間語句decfsz i /判斷是否是0goto

40、loop再編譯這樣的語句:(同樣循環(huán) 8 次)for(i=8;i0;i-),movlw 8movwf iloopdecfsz i /同上編譯的代碼。goto loop再次驗證了剛才的分析。在 PICC 后面加上 -SIGNED_CHAR 后綴,則第一個示例就正確編譯出來了,更證明了剛才的分析是正確的。 代碼如下:movlw 7movwf iloop/中間語句decf i /遞減btfss i,7 /判斷 i 的7 位來判斷是否為負數(shù)goto l94總結:在 PICC 無符號編譯環(huán)境下,對于遞減的 for 語句的 條件判斷語句不能是=0 的形式。最后談談 PICC 的小竅門:在 PICC 默認的

41、無符號環(huán)境下,對比如下代碼:a 語句:char i,j8;i=7;while(1)ji=0;i-;if(i=0)break;b 語句:char i,j8;for(i=8;i0;i-)ji-1=0;表面看上去,一般會認為下面的代碼編譯后要大一點點,因 為多了 ji-l中的i-1。其實編譯后代碼量是一摸一樣的。原因如下:movlw 8 或 7 /a 語句是 7,b 語句是 8movf iloop/a 語句在這里提取 i 給 j 數(shù)組/i 遞減判斷語句/b 語句在這里提取 i 給 j 數(shù)組goto loop 可以看出只是代碼位置不同而已,并沒添加代碼量。 b 語句 同樣達到了從 7 到 0 的循環(huán)。

42、小總結:對于遞減到 0 的 for 語句推薦用0 判斷語句來實 現(xiàn),不會出現(xiàn)編譯錯誤的問題,并且不會增加代 碼量,尤其對于數(shù)組操作的方面。另:對于PICC或CCS,在其默認的無符號編譯環(huán)境下,如 果出現(xiàn)負數(shù)運算就會出問題。如(-100)+50 等,所以在編寫代碼時候要特別小心!8、用 PICC 寫高效的位移操作。 在許多模擬串行通信中需要用位移操作。以 1-W 總線的讀字節(jié)為例,原廠的代碼是:unsigned char read_byte(void)unsigned char i;unsigned char value = 0;for (i = 0; i 8; i+)if(read_bit()

43、 value| = 0 x 01i;/ reads byte in, one byte at a time and then/ shifts it leftdelay(10); / wait for rest of timeslotreturn(value); 雖然可以用,但編譯后執(zhí)行效率并不高效,這也是很多朋友 認為 C 一定不能和匯編相比的認識提供了說法。其實完全可以深入了解C和匯編之間的關系,寫出非常高效的 C 代碼,既有 C 的便利,又有匯編的效率。首先對for (i = 0; i 8; i+)做手術,改成遞減的形式: for(i=8;i!=0;i-),因為 CPU 判斷一個數(shù)是否是

44、0 (只需要一個指令),比判斷一個數(shù)是多大來的快(需要 3 個 指令)。再對valuel = 0 x 01i;做手術。valuel = 0 x 01i;其實是一個低水平的代碼,效率低,DALLAS 的工程師都是N01,奇怪為什么會如此疏忽。1;語句其實是一個低水平的寫法,效率非常低。奇怪DALLAS 的工程師都是N01,怎么會如此疏忽。仔細研究 C 語言的位移操作,可以發(fā)現(xiàn) C 總是先把標志位 清 0,然后再把此位移入字節(jié)中,也就是說,當 前移動進字節(jié)的位一定是 0。那么,既然已經是 0 了,我們 就只剩下一個步驟:判斷總線狀態(tài)是否是高來 決定是否改寫此位,而不需要判斷總線是低的情況。于是改

45、寫如下代碼:for(i=8;i!=0;i-)value=1; /先右移一位, value 最高位一定是 0 if(read_bit() valuel=0 x80; /判斷總線狀態(tài),如果是高,就把 value 的最高位置 1這樣一來,整個代碼變得極其高效,編譯后根本就是匯編級 的代碼。再舉一個例子: 在采集信號方面,經常是連續(xù)采集 N 次,最后求其平均值。一般的,無論是用匯編或C,在采集次數(shù)上都推薦用8, 16,32、64、128、256 等次數(shù),因為這些數(shù)都比 較特殊,對于 MCU 計算有很大好處。我們以128次采樣為例:注:sampling()為外部采樣函數(shù)。unsigned int tot

46、al;unsigned char i,val;for(i=0;i7);再觀察下去:total7還可以變通成 (total8,先左移動一位,再右移動8位,不就成了右移 7 位了么?可知道位移1 , 4, 8 的操作只需要 一個指令哦。有上面的概驗了,就可以寫出如下的代碼: unsigned int total;unsigned char i=0unsigned char val;while(!(i&0 x80) 判斷i第7位,只需要一個指令。total+=sampling();i+;val=(unsigned char)(total8); /幾個指令就代替了幾十 個指令的除法運算哈哈,發(fā)現(xiàn)什么?

47、代碼量竟然可以減少一大半,運算速度可 以提高幾倍。再回頭,就可以理解為什么采樣次數(shù)要用推薦的一些特殊值 了。9、C 程序優(yōu)化 對程序進行優(yōu)化,通常是指優(yōu)化程序代碼或程序執(zhí)行速度。 優(yōu)化代碼和優(yōu)化速度實際上是一個予 盾的統(tǒng)一,一般是優(yōu)化了代碼的尺寸,就會帶來執(zhí)行時間的 增加,如果優(yōu)化了程序的執(zhí)行速度,通常會帶 來代碼增加的副作用,很難魚與熊掌兼得,只能在設計時掌 握一個平衡點。一、程序結構的優(yōu)化1 、程序的書寫結構 雖然書寫格式并不會影響生成的代碼質量,但是在實際編寫 程序時還是應該尊循一定的書寫規(guī)則,一 個書寫清晰、明了的程序,有利于以后的維護。在書寫程序 時,特別是對于 While、for、

48、dowhile、ifelst、 switchcase 等語句或這些語句嵌套組合時,應采用“縮格” 的書寫形式,2、標識符 程序中使用的用戶標識符除要遵循標識符的命名規(guī)則以外, 一般不要用代數(shù)符號(如a、b、x1、y1)作 為變量名,應選取具有相關含義的英文單詞(或縮寫)或漢語拼 音作為標識符,以增加程序的可讀性,如:count、 number1 、 red、 work 等。3、程序結構C 語言是一種高級程序設計語言,提供了十分完備的規(guī)范化 流程控制結構。因此在采用 C 語言設計單 片機應用系統(tǒng)程序時,首先要注意盡可能采用結構化的程序 設計方法,這樣可使整個應用系統(tǒng)程序結構清 晰,便于調試和維護

49、。于一個較大的應用程序,通常將整個 程序按功能分成若干個模塊,不同模塊完成不 同的功能。各個模塊可以分別編寫,甚至還可以由不同的程 序員編寫,一般單個模塊完成的功能較為簡單,設計和調試也相對容易一些。在C語言中,一個函數(shù)就可以認為是一個模塊。所謂程序模塊化,不僅是要 將整個程序劃分成若干個功能模塊,更重要的是,還應該注 意保持各個模塊之間變量的相對獨立性,即保 持模塊的獨立性,盡量少使用全局變量等。對于一些常用的 功能模塊,還可以封裝為一個應用程序庫,以 便需要時可以直接調用。但是在使用模塊化時,如果將模塊 分成太細太小,又會導致程序的執(zhí)行效率變低(進 入和退出一個函數(shù)時保護和恢復寄存器占用了

50、一些時間)。4、定義常數(shù)在程序化設計過程中,對于經常使用的一些常數(shù),如果將它 直接寫到程序中去,一旦常數(shù)的數(shù)值發(fā)生 變化,就必須逐個找出程序中所有的常數(shù),并逐一進行修改 這樣必然會降低程序的可維護性。因此,應 盡量當采用預處理命令方式來定義常數(shù),而且還可以避免輸 入錯誤。5、減少判斷語句能夠使用條件編譯(ifdef)的地方就使用條件編譯而不使用if 語句,有利于減少編譯生成的代碼的長度。6、表達式 對于一個表達式中各種運算執(zhí)行的優(yōu)先順序不太明確或容易 混淆的地方,應當采用圓括號明確指定它 們的優(yōu)先順序。一個表達式通常不能寫得太復雜,如果表達 式太復雜,時間久了以后,自己也不容易看得懂,不利于以

51、后的維護。7、函數(shù) 對于程序中的函數(shù),在使用之前,應對函數(shù)的類型進行說明, 對函數(shù)類型的說明必須保證它與原來定 義的函數(shù)類型一致,對于沒有參數(shù)和沒有返回值類型的函數(shù) 應加上“void”說明。如果果需要縮短代碼的長 度,可以將程序中一些公共的程序段定義為函數(shù),在 Keil 中 的高級別優(yōu)化就是這樣的。如果需要縮短程序 的執(zhí)行時間,在程序調試結束后,將部分函數(shù)用宏定義來代 替。注意,應該在程序調試結束后再定義宏, 因為大多數(shù)編譯系統(tǒng)在宏展開之后才會報錯,這樣會增加排 錯的難度。8、盡量少用全局變量,多用局部變量。因為全局變量是放在 數(shù)據存儲器中,定義一個全局變量,MCU就少一個可以利用的數(shù)據存儲器

52、空間,如果定義了太多的全局 變量,會導致編譯器無足夠的內存可以分配。而局部變量大多定位于 MCU 內部的寄存器中,在絕大多數(shù) MCU 中,使用寄存器操作速度比數(shù)據存儲器快, 指令也更多更靈活,有利于生成質量更高的代碼,而且局部 變量所的占用的寄存器和數(shù)據存儲器在不同的 模塊中可以重復利用。9、設定合適的編譯程序選項 許多編譯程序有幾種不同的優(yōu)化選項,在使用前應理解各優(yōu) 化選項的含義,然后選用最合適的一種優(yōu) 化方式。通常情況下一旦選用最高級優(yōu)化,編譯程序會近乎 病態(tài)地追求代碼優(yōu)化,可能會影響程序的正確 性,導致程序運行出錯。因此應熟悉所使用的編譯器,應知 道哪些參數(shù)在優(yōu)化時會受到影響,哪些參數(shù)不

53、 會受到影響。在 ICCAVR 中,有 “Default” 和 “Enable Code Compression” 兩個優(yōu)化選項。在 CodeVisionAVR 中,“Tiny”和“small”兩種內存模式。在 IAR 中,共有 7 種不同的內存模式選項。在GCCAVR中優(yōu)化選項更多,一不小心更容易選到不恰當?shù)?選項。二、代碼的優(yōu)化1、選擇合適的算法和數(shù)據結構 應該熟悉算法語言,知道各種算法的優(yōu)缺點,具體資料請參 見相應的參考資料,有很多計算機書籍上 都有介紹。將比較慢的順序查找法用較快的二分查找或亂序 查找法代替,插入排序或冒泡排序法用快速排 序、合并排序或根排序代替,都可以大大提高程序執(zhí)行的效 率。 .選擇一種合適的數(shù)據結構也很重要,比如 你在一堆隨機存放的數(shù)中使用了大量的插入和刪除指令,那 使用鏈表要快得多。數(shù)組與指針具有十分密碼的關系,一般來說,指針比較靈活 簡潔,而數(shù)組則比較直觀,容易理解。對于大 部分的編譯器,使用指針比使用數(shù)組生成的代碼更短,執(zhí)行 效率更高。但是在 Keil 中則相反,使用數(shù)組比 使用的指針生成的代碼更短。2、使用盡量小的數(shù)據類型能夠使用字符型(char)定義的變量,就不要使用整型(int)變量 來定義;能夠使用整型變量定義的變量就不要用長整型(l ong int),能不使用浮點

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論