PIC單片機-C編程技巧_第1頁
PIC單片機-C編程技巧_第2頁
PIC單片機-C編程技巧_第3頁
PIC單片機-C編程技巧_第4頁
PIC單片機-C編程技巧_第5頁
已閱讀5頁,還剩29頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

實用PIC單片機C編程技巧PIC單片機C編程技巧1、PICC和MPLAB集成PICC和MPLAB集成:PICC有自己的文本編輯器,不過是DOS風(fēng)格的,看來PICC的工程師要專業(yè)冷到酷底了???大家大可不必用它,如果你沒什么癖好的話,你不會不用UltraEdit吧?1:建立你的工作目錄:建議在C盤根目錄下建立一個以A開頭的文件夾做為工作目錄.因為你會發(fā)現(xiàn)它總是在你查找文件時候第一個跳入你眼中.2:MPLAB調(diào)用PICC.(以MPLAB5.7版本為例子)啟動MPLAB.在Project-->InstallLanguageTool:LanguageSuite—>hi-techpiccToolName>PICCCompilerExecutable>c:hi-picinpicc.exe(假如你的PICC是默認安裝的)文案實用選Command-line最后OK.上面這步只需要設(shè)定一次,除非你重新安裝了MPLAB.3:創(chuàng)建你的項目文件:(假如你實現(xiàn)用EDIT編輯好了一個叫AA.C的C代碼文件)Project-->NewProject-->FileName>myc(假如我們把項目文件取名字叫MYC.PJT)右邊窗口當然要選擇中你的工作目錄.然后OK.4:設(shè)定你的PICC工作參數(shù):Project-->EditProject上面4個欄目就用默認的,空的也就讓它空著,無所謂的.需要修改的是:DevelopmentMode>選擇你的PIC型號.當然要選擇MplabSIMSimulator讓你可以用軟件仿真.LanguageToolSuite>HI-TECHPICC上面的步驟,你可能會遇見多個提示條,不要管它,一路確定.下面是PICC編譯器的選擇項:雙擊ProjectFiles窗口里面的MYC.HEX,出現(xiàn)一個選擇欄目.命令很多,大家可以看PICC文本編輯器里面的HELP,里面有詳細說明.文案實用下面就推薦幾個常用也是建議用的:Generatedebuginfo以及下面的2項.Produceassemblerlistfile就在它們后面打勾即可,其它的不要管,除非你有特殊要求.5:添加你的C代碼文件:當進行了前面幾步后,按AddNode找到AA.C文件就OK了.6:編譯C代碼:最簡單的一步:直接按下F10.編譯完后,會出現(xiàn)各種調(diào)試信息.C代碼對應(yīng)的匯編代碼就是工作目錄里面的AA.IST,用EDIT打開可以看見詳細的對比.7:其它,要是一切都沒問題,那么你就可以調(diào)試和燒片了,和以往操作無異.2、如何從匯編轉(zhuǎn)向PICC首先要求你要有C語言的基礎(chǔ)。PICC不支持C++,這對于習(xí)慣了C++的朋友還得翻翻C語言的書。C代碼的頭文件一定要有#include<pic.h>,它是很多頭文件的集合,C編譯器在pic.h中根據(jù)你的芯片自動載入相應(yīng)的其它頭文件。這點比匯編好用。載入的頭文件中其實是聲明芯片的寄存器和一些函數(shù)。順便摘抄文案實用一個片段:staticvolatileunsignedcharTMR0@0x01;staticvolatileunsignedcharPCL@0x02;staticvolatileunsignedcharSTATUS@0x03;可以看出和匯編的頭文件中定義寄存器是差不多的。如下:TMR0EQU0X01;PCLEQU0X02;STATUSEQU0X03;都是把無聊的地址定義為大家公認的名字。一:怎么附值?如對TMR0附值,匯編中:MOVLW200;MOVWFTMR0;當然得保證當前頁面在0,不然會出錯。C語言:TMR0=200;//無論在任何頁面都不會出錯??梢钥闯鰜鞢是很直接了當?shù)?。并且最大好處是操作一個寄存器時候,不用考慮頁面的問題。一切由C自動完成。二:怎么位操作?匯編中的位操作是很容易的。在C中更簡單。C的頭文件中已經(jīng)對所有可能需要位操作的寄存器的每一位都有定義名稱:文案實用如:PORTA的每一個I/O口定義為:RA0、RA1、RA2。。。RA7。OPTION的每一位定義為:PS0、PS1、PS2、PSA、T0SE、T0CS、INTEDG、RBPUE以對其直接進行運算和附值。如:RA0=0;RA2=1;在匯編中是:BCFPORTA,0;BSFPORTA,2;可以看出2者是大同小異的,只是C中不需要考慮頁面的問題。三:內(nèi)存分配問題:在匯編中定義一個內(nèi)存是一件很小心的問題,要考慮太多的問題,稍微不注意就會出錯。比如16位的運算等。用C就不需要考慮太多。下面給個例子:16位的除法(C代碼):INTX=5000;INTY=1000;INTZ=X/Y;文案實用而在匯編中則需要花太多精力。給一個小的C代碼,用RA0控制一個LED閃爍:#include<pic.h>voidmain。(intx;CMCON=0B111;//調(diào)A口比較器,要是有比較器功能的話。ADCON1=0B110;//調(diào)A/D功能,要是有A/D功能的話。TRISA=0;//RA口全為輸出。loop:RA0=!RA0;for(x=60000;--x;){;}//延時gotoloop;}說說RA0=!RA0的意思:PIC對PORT寄存器操作都是先讀取--修改--寫入。上句的含義是程序先讀RA0,然后取反,最后把運算后的值重新寫入RA0,這就實現(xiàn)了閃爍的功能。3、淺談PICC的位操作由于PIC處理器對位操作是最高效的,所以把一些BOOL變量放在一個內(nèi)存的位中,既可以達到運算速度快,又可以達到最大限度節(jié)省空間的目的。在C中的位操作有多種選擇。*********************************************文案實用如:charx;x=x|0B00001000;/*對X的4位置1。*/charx;x=x&0B11011111;/*對X的5位清0。*/把上面的變成公式則是:#definebitset(var,bitno)(var|=1<<bitno)#definebitclr(var,bitno)(var&=~(1<<bitno))則上面的操作就是:charx;bitset(x,4)charx;bitclr(x,5)******?***_***?***但上述的方法有缺點,就是對每一位的含義不直觀,最好是能在代碼中能直觀看出每一位代表的意思,這樣就能提高編程效率,避免出錯。如果我們想用X的0-2位分別表示溫度、電壓、電流的BOOL值,可以如下:unsignedcharx@0x20;/*象匯編那樣把X變量定義到一個固定內(nèi)存中。*/bittemperature@(unsigned)&x*8+0;/幅度*/bitvoltage@(unsigned)&x*8+1;/*電壓*/bitcurrent@(unsigned)&x*8+2;/*電流*/這樣定義后X的位就有一個形象化的名字,不再是枯燥的1、2、3、4等數(shù)字了??梢詫全局修改,也可以對每一位進行操作:char=255;temperature=0;if(voltage) 文案實用*****************************************************************還有一個方法是用C的struct結(jié)構(gòu)來定義:如:structcypok{temperature:1;/*溫度*/voltage:1;/*電壓*/current:1;/*電流*/none:4;}x@0x20;這樣就可以用x.temperature=0;if(x.current)等操作了。**********************************************************上面的方法在一些簡單的設(shè)計中很有效,但對于復(fù)雜的設(shè)計中就比較吃力。如象在多路工業(yè)控制上。前端需要分別收集多路的多路信號,然后再設(shè)定控制多路的多路輸出。如:有2路控制,每一路的前端信號有溫度、電壓、電流。后端控制有電機、喇叭、繼電器、LED。如果用匯編來實現(xiàn)的話,是很頭疼的事情,用C來實現(xiàn)是很輕松的事情,這里也涉及到一點C的內(nèi)存管理(其實C的最大優(yōu)點就是內(nèi)存管理)。采用如下結(jié)構(gòu):unioncypok{structout{文案實用motor:1;/*電機*/relay:1;/*繼電器*/speaker:1;/*喇叭*/led1:1;/*指示燈*/led2:1;/*指示燈*/}out;structin{none:5;temperature:1;/*溫度*/voltage:1;/*電壓*/current:1;/*電流*/}in;charx;};unioncypokan1;unioncypokan2;上面的結(jié)構(gòu)有什么好處呢?細分了信號的路an1和an2;細分了每一路的信號的類型(是前端信號in還是后端信號out):an1.in;an1.out;an2.in;an2.out;然后又細分了每一路信號的具體含義,如:文案實用anl.in.temperature;an1.out.motor;an2.in.voltage;an2.out.led2;等這樣的結(jié)構(gòu)很直觀的在2個內(nèi)存中就表示了2路信號。并且可以極其方便的擴充。如添加更多路的信號,只需要添加:unioncypokan3;unioncypokan4;從上面就可以看出用C的巨大好處4、PICC之延時函數(shù)和循環(huán)體優(yōu)化。很多朋友說C中不能精確控制延時時間,不能象匯編那樣直觀。其實不然,對延時函數(shù)深入了解一下就能設(shè)計出一個理想的框,價出來。一般的我們都用for(x=100;--x;){;}此句等同與x=100;while(--x){;};或for(x=0;x<100;x++){;}。來寫一個延時函數(shù)。在這里要特別注意:X=100,并不表示只運行100個指令時間就跳出循環(huán)??梢钥纯淳幾g后的匯編:x=100;while(--x){;}匯編后:文案實用movlw100bcf3,5bcf3,6movwf_delay12decfsz_delaygoto12return從代碼可以看出總的指令是是303個,其公式是8+3*(X-1)。注意其中循環(huán)周期是X-1是99個。這里總結(jié)的是x為char類型的循環(huán)體,當x為int時候,其中受X值的影響較大。建議設(shè)計一個char類型的循環(huán)體,然后再用一個循環(huán)體來調(diào)用它,可以實現(xiàn)精確的長時間的延時。下面給出一個能精確控制延時的函數(shù),此函數(shù)的匯編代碼是最簡潔、最能精確控制指令時間的:voiddelay(charx,chary){charz;do{z=y;do{;}while(--z);}while(--x);}其指令時間為:7+(3*(Y-1)+7)*(X-1)如果再加上函數(shù)調(diào)用的call指令、頁面設(shè)定、傳遞參數(shù)花掉的7個指令。則是:14+(3*(Y-1)+7)*(X-1)。如果要求不是特別嚴格的延時,可以用這個函數(shù):voiddelay(){unsignedintd=1000;文案實用while(--d){;}}此函數(shù)在4M晶體下產(chǎn)生10003us的延時,也就是10MS。如果把D改成2000,則是20003US,以此類推。有朋友不明白,為什么不用while(x--)后減量,來控制設(shè)定X值是多少就循環(huán)多少周期呢?現(xiàn)在看看編譯它的匯編代碼:bcf3,5bcf3,6movlw10movwf_delayl2decf_delayincfsz_delay,wgotol2return可以看出循環(huán)體中多了一條指令,不簡潔。所以在PICC中最好用前減量來控制循環(huán)體。再談?wù)勥@樣的語句:for(x=100;--x;){;}和for(x=0;x<100;x++){;}從字面上看2者意思一樣,但可以通過匯編查看代碼。后者代碼雍長,而前者就很好的匯編出了簡潔的代碼。所以在PICC中最好用前者的形式來寫循環(huán)體,好的C編譯器會自動把增量循環(huán)化為減量循環(huán)。因為這是由處理器硬件特性決定的。PICC并不是一個很智能的C編譯器,所以還是人腦才是第一的,掌握一些經(jīng)驗對寫出高效,簡潔的代碼是有好處的。文案實用5、深入探討PICC之位操作一:用位操作來做一些標志位,也就是8001變量.可以簡單如下定義:bita,b,c;PICC會自動安排一個內(nèi)存,并在此內(nèi)存中自動安排一位來對應(yīng)a,b,c,由于我們只是用它們來簡單的表示一些0,1信息,所以我們不需要詳細的知道它們的地址\位究竟是多少,只管拿來就用好了.二:要是需要用一個地址固定的變量來位操作,可以參照pic.h里面定義寄存器.如:用25H內(nèi)存來定義8個位變量.staticvolatileunsignedcharmyvar@0x25;staticvolatilebitb7@(unsigned)&myvar*8+7;staticvolatilebitb6@(unsigned)&myvar*8+6;staticvolatilebitb5@(unsigned)&myvar*8+5;staticvolatilebitb4@(unsigned)&myvar*8+4;staticvolatilebitb3@(unsigned)&myvar*8+3;staticvolatilebitb2@(unsigned)&myvar*8+2;staticvolatilebitb1@(unsigned)&myvar*8+1;staticvolatilebitb0@(unsigned)&myvar*8+0;這樣即可以對MYVAR操作,也可以對B0一—87直接位操作.但不好的是,此招在低檔片子,如C5X系列上可能會出問題.還有就是表達起來復(fù)雜,你不覺得輸入代碼受累么?呵呵文案實用三:這也是一些常用手法:#definetestbit(var,bit)((var)&(1<<(bit)))//測試某一位,可以做BOOL運算#definesetbit(var,bit)((var)|=(1<<(bit)))//把某一位置1#defineclrbit(var,bit)((var)&=~(1<<(bit)))//把某一位清。付上一段代碼,可以用MPLAB調(diào)試觀察#include<pic.h>#definetestbit(var,bit)((var)&(1<<(bit)))#definesetbit(var,bit)((var)|=(1<<(bit)))#defineclrbit(var,bit)((var)&=~(1<<(bit)))chara,b;voidmain(){charmyvar;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<pic.h>unionvar{unsignedcharbyte;struct{unsignedb0:1,b1:1,b2:1,b3:1,b4:1,b5:1,b6:1,b7:1;}bits;};chara,b;voidmain(){staticunionvarmyvar;myvar.byte=0B10101010;a=myvar.bits.b0;b=myvar.bits.b1;if(myvar.bits.b7)a=255;elsea=100;while(1){;}}五:用指針轉(zhuǎn)換來表示:#include<pic.h>typedefstruct{unsignedb0:1,b1:1,b2:1,b3:1,b4:1,b5:1,b6:1,b7:1;}bits;//先定義一個變量的位#definemybit0(((bits*)&myvar)->b0)//取myvar的地址(&myvar)強制轉(zhuǎn)換成bits類型的指針#definemybit1(((bits*)&myvar)->b1)#definemybit2(((bits*)&myvar)->b2)#definemybit3(((bits*)&myvar)->b3)文案實用#definemybit4(((bits*)&myvar)->b4)#definemybit5(((bits*)&myvar)->b5)#definemybit6(((bits*)&myvar)->b6)#definemybit7(((bits*)&myvar)->b7)charmyvar;chara,b;voidmain(){myvar=0B10101010;a=mybit0;b=mybit1;if(mybit7)a=255;elsea=100;while(1){;}}[NextPage]六:五的方法還是煩瑣,可以用粘貼符號的形式來簡化它.#include<pic.h>typedefstruct{unsignedb0:1,b1:1,b2:1,b3:1,b4:1,b5:1,b6:1,b7:1;}bits;#define_paste(a,b)a##b#definebitof(var,num)(((bits*)&(var))->_paste(b,num))charmyvar;chara,b;voidmain(){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,0)) >(((bits*)&(var))->b0)可以看出來,_paste(b,0)的作用是把0粘貼到了b后面,成了b0符號.總結(jié):c語言的優(yōu)勢是能直接對低層硬件操作,代碼可以非常非常接近匯編,上面幾個例子的位操作代碼是100%的達到匯編的程度的.另一個優(yōu)勢是可讀性高,代碼靈活,上面的幾個位操作方法任由你選,你不必擔心會產(chǎn)生多余的代碼量出來.6、在PICC中使用常數(shù)指針。常數(shù)指針使用非常靈活,可以給編程帶來很多便利。我測試過,PICC也支持常數(shù)指針,并且也會自動分頁,實在是一大喜事。定義一個指向8位RAM數(shù)據(jù)的常數(shù)指針(起始為0x00):#defineDBYTE((unsignedcharvolatile*)0)文案實用定義一個指向16位RAM數(shù)據(jù)的常數(shù)指針(起始為0x00):#defineCWORD((unsignedintvolatile*)0)((unsignedcharvolatile*)0)中的0表示指向RAM區(qū)域的起始地址,可以靈活修改它。DBYTE[x]中的x表示偏移量。下面是一段代碼1:chara1,a2,a3,a4;#defineDBYTE((unsignedcharvolatile*)0)voidmain(void){longcc=0x89abcdef;a1=DBYTE[0x24];a2=DBYTE[0x25];a3=DBYTE[0x26];a4=DBYTE[0x27];while(1);}2:chara1,a2,a3,a4;#defineDBYTE((unsignedcharvolatile*)0)voidpp(chary){a1=DBYTE[y++];a2=DBYTE[y++];a3=DBYTE[y++];a4=DBYTE[y];}voidmain(void){longcc=0x89abcdef;charx;x=&cc;pp(x);文案實用while(1);}3:chara1,a2,a3,a4;#defineDBYTE((unsignedcharvolatile*)0)voidpp(chary){a1=DBYTE[y++];a2=DBYTE[y++];a3=DBYTE[y++];a4=DBYTE[y];}voidmain(void){bank1staticlongcc=0x89abcdef;charx;x=&cc;pp(x);while(1);}7、PICC關(guān)于unsigned和signed的幾個關(guān)鍵問題!unsigned是表示一個變量(或常數(shù))是無符號類型。signed表示有符號。它們表示數(shù)值范圍不一樣。PICC默認所有變量都是unsigned類型的,哪怕你用了signed變量。因為有符號運算比無符號運算耗資源,而且MCU運算一般不涉及有符號運算。在PICC后面加上-SIGNED_CHAR后綴可以告訴PICC把signed變量當作有符號處理。在PICC默認的無符號運算下看這樣的語句:文案實用chari;for(i=7;i>=0;i--){;〃中間語句}這樣的C代碼看上去是沒有丁點錯誤的,但編譯后,問題出現(xiàn)了:movlw7movwfiloop〃中間語句decfi〃只是遞減,沒有判斷語句?。?!gotoloop原因是當i是0時候,條件還成立,還得循環(huán)一次,直到i成負1條件才不成立。而PICC在默認參數(shù)下是不能判斷負數(shù)的,所以編譯過程出現(xiàn)問題。那么采用這樣的語句來驗證:chari;i=7;while(1){i--;〃中間語句if(i==0)break;//告訴PICC以判斷i是否是0來作為條件}編譯后代碼正確:movlw7movwfiloop文案實用〃中間語句decfszi//判斷是否是0gotoloop再編譯這樣的語句:(同樣循環(huán)8次)for(i=8;i>0;i--){■;}movlw8movwfiloopdecfszi〃同上編譯的代碼。gotoloop再次驗證了剛才的分析。在PICC后面加上-SIGNED_CHAR后綴,則第一個示例就正確編譯出來了,更證明了剛才的分析是正確的。代碼如下:movlw7movwfiloop//中間語句decfi//遞減btfssi,7〃判斷i的7位來判斷是否為負數(shù)goto194總結(jié):在PICC無符號編譯環(huán)境下,對于遞減的for語句的條文案實用件判斷語句不能是>=0的形式。最后談?wù)凱ICC的小竅門:在PICC默認的無符號環(huán)境下,對比如下代碼:a語句:chari,j[8];i=7;while(1){j[i]=0;i--;if(i==0)break;}b語句:chari,j[8];for(i=8;i>0;i--){j[i-1]=0;}表面看上去,一般會認為下面的代碼編譯后要大一點點,因為多了j[i-1]中的i-1。其實編譯后代碼量是一摸一樣的。原因如下:movlw8或7//a語句是7,b語句是8movfiloop//a語句在這里提取i給j數(shù)組//i遞減判斷語句文案實用//b語句在這里提取i給j數(shù)組gotoloop可以看出只是代碼位置不同而已,并沒添加代碼量。b語句同樣達到了從7到0的循環(huán)。小總結(jié):對于遞減到0的for語句推薦用>0判斷語句來實現(xiàn),不會出現(xiàn)編譯錯誤的問題,并且不會增加代碼量,尤其對于數(shù)組操作的方面。另:對于PICC或CCS,在其默認的無符號編譯環(huán)境下,如果出現(xiàn)負數(shù)運算就會出問題。如(-100)+50等,所以在編寫代碼時候要特別小心?。?!8、用PICC寫高效的位移操作。在許多模擬串行通信中需要用位移操作。以1-W總線的讀字節(jié)為例,原廠的代碼是:unsignedcharread_byte(void)(unsignedchari;unsignedcharvalue=0;for(i=0;i<8;i++)(if(read_bit())value|=0x01<<i;//readsbytein,onebyteatatimeandthen//shiftsitleftdelay(10);//waitforrestoftimeslot}return(value);}文案實用雖然可以用,但編譯后執(zhí)行效率并不高效,這也是很多朋友認為C一定不能和匯編相比的認識提供了說法。其實完全可以深入了解C和匯編之間的關(guān)系,寫出非常高效的C代碼,既有C的便利,又有匯編的效率。首先對for(i=0;i<8;i++)做手術(shù),改成遞減的形式:for(i=8;i!=0;i--),因為CPU判斷一個數(shù)是否是0(只需要一個指令),比判斷一個數(shù)是多大來的快(需要3個指令)。再對value|=0x01<<i;做手術(shù)。value|=0x01<<1其實是一個低水平的代碼,效率低,DALLAS的工程師都是NO1,奇怪為什么會如此疏忽。<I;語句其實是一個低水平的寫法,效率非常低。奇怪DALLAS的工程師都是NO1,怎么會如此疏忽。<P>仔細研究C語言的位移操作,可以發(fā)現(xiàn)C總是先把標志位清0,然后再把此位移入字節(jié)中,也就是說,當前移動進字節(jié)的位一定是0。那么,既然已經(jīng)是0了,我們就只剩下一個步驟:判斷總線狀態(tài)是否是高,來決定是否改寫此位,而不需要判斷總線是低的情況。于是改寫如下代碼:for(i=8;i!=0;i--){value>>=1;//先右移一位,value最高位一定是0if(read_bit())value|=0x80;〃判斷總線狀態(tài),如果是高,就把value的最高位置1文案實用}這樣一來,整個代碼變得極其高效,編譯后根本就是匯編級的代碼。再舉一個例子:在采集信號方面,經(jīng)常是連續(xù)采集N次,最后求其平均值。一般的,無論是用匯編或9在采集次數(shù)上都推薦用8,16,32、64、128、256等次數(shù),因為這些數(shù)都比較特殊,對于MCU計算有很大好處。我們以128次采樣為例:注:sampling。為外部采樣函數(shù)。unsignedinttotal;unsignedchari,val;for(i=0;i<128;i++){total+=sampling();}val=total/128;以上代碼是很多場合都可以看見的,但是效率并不怎么樣,狂浪費資源。結(jié)合C和匯編的關(guān)系,再加上一些技巧,就可以寫出天壤之別的匯編級的C代碼出來,首先分析128這個數(shù)是0B10000000,發(fā)現(xiàn)其第7位是1,其他低位全是0,那么就可以判斷第7位的狀態(tài)來判斷是否到了128次采樣次數(shù)。在分析除以128的運算,上面的代碼用了除法運算,浪費了N多資源,完全可以用右移的方法來代替之,val=total/128等同于val=(unsignedchar)(total>>7);再觀察下去:total>>7還可以變通成文案實用(total<<1)>>8冼左移動一位,再右移動8位,不就成了右移7位了么?可知道位移1,4,8的操作只需要一個指令哦。有上面的概驗了,就可以寫出如下的代碼:unsignedinttotal;unsignedchari=0unsignedcharval;while(!(i&0x80)){〃判斷i第7位,只需要一個指令。total+=sampling();i++;}val=(unsignedchar)((total<<1)>>8);//幾個指令就代替了幾十個指令的除法運算哈哈,發(fā)現(xiàn)什么?代碼量竟然可以減少一大半,運算速度可以提高幾倍。再回頭,就可以理解為什么采樣次數(shù)要用推薦的一些特殊值了。9、C程序優(yōu)化對程序進行優(yōu)化,通常是指優(yōu)化程序代碼或程序執(zhí)行速度。優(yōu)化代碼和優(yōu)化速度實際上是一個予盾的統(tǒng)一,一般是優(yōu)化了代碼的尺寸,就會帶來執(zhí)行時間的增加,如果優(yōu)化了程序的執(zhí)行速度,通常會帶來代碼增加的副作用,很難魚與熊掌兼得,只能在設(shè)計時掌握一個平衡點。一、程序結(jié)構(gòu)的優(yōu)化1、程序的書寫結(jié)構(gòu)文案實用雖然書寫格式并不會影響生成的代碼質(zhì)量,但是在實際編寫程序時還是應(yīng)該尊循一定的書寫規(guī)則,一個書寫清晰、明了的程序,有利于以后的維護。在書寫程序時,特別是對于While、for、do…while、if…elst、switch…case等語句或這些語句嵌套組合時,應(yīng)采用“縮格”的書寫形式,2、標識符程序中使用的用戶標識符除要遵循標識符的命名規(guī)則以外,一般不要用代數(shù)符號(如a、b、x1、y1)作為變量名,應(yīng)選取具有相關(guān)含義的英文單詞(或縮寫)或漢語拼音作為標識符,以增加程序的可讀性,如:count、numberl、red、work等。3、程序結(jié)構(gòu)C語言是一種高級程序設(shè)計語言,提供了十分完備的規(guī)范化流程控制結(jié)構(gòu)。因此在采用C語言設(shè)計單片機應(yīng)用系統(tǒng)程序時,首先要注意盡可能采用結(jié)構(gòu)化的程序設(shè)計方法,這樣可使整個應(yīng)用系統(tǒng)程序結(jié)構(gòu)清晰,便于調(diào)試和維護。于一個較大的應(yīng)用程序,通常將整個程序按功能分成若干個模塊,不同模塊完成不同的功能。各個模塊可以分別編寫,甚至還可以由不同的程序員編寫,一般單個模塊完成的功能較為簡單,設(shè)計和調(diào)試也相對容易一些。在C語言中,一個函數(shù)就可以認為是一個模塊。所謂程序模塊化,不僅是要將整個程序劃分成若干個功能模塊,更重要的是,還應(yīng)該注意保持各個文案實用模塊之間變量的相對獨立性,即保持模塊的獨立性,盡量少使用全局變量等。對于一些常用的功能模塊,還可以封裝為一個應(yīng)用程序庫,以便需要時可以直接調(diào)用。但是在使用模塊化時,如果將模塊分成太細太小,又會導(dǎo)致程序的執(zhí)行效率變低(進入和退出一個函數(shù)時保護和恢復(fù)寄存器占用了一些時間)。4、定義常數(shù)在程序化設(shè)計過程中,對于經(jīng)常使用的一些常數(shù),如果將它直接寫到程序中去,一旦常數(shù)的數(shù)值發(fā)生變化,就必須逐個找出程序中所有的常數(shù),并逐一進行修改,這樣必然會降低程序的可維護性。因此,應(yīng)盡量當采用預(yù)處理命令方式來定義常數(shù),而且還可以避免輸入錯誤。5、減少判斷語句能夠使用條件編譯(ifdef)的地方就使用條件編譯而不使用if語句,有利于減少編譯生成的代碼的長度。6、表達式對于一個表達式中各種運算執(zhí)行的優(yōu)先順序不太明確或容易混淆的地方,應(yīng)當采用圓括號明確指定它們的優(yōu)先順序。一個表達式通常不能寫得太復(fù)雜,如果表達式太復(fù)雜,時間久了以后,自己也不容易看得懂,不利于以后的維護。7、函數(shù)對于程序中的函數(shù),在使用之前,應(yīng)對函數(shù)的類型進行說明,文案實用對函數(shù)類型的說明必須保證它與原來定義的函數(shù)類型一致,對于沒有參數(shù)和沒有返回值類型的函數(shù)應(yīng)加上“void說明。如果果需要縮短代碼的長度,可以將程序中一些公共的程序段定義為函數(shù),在Keil中的高級別優(yōu)化就是這樣的。如果需要縮短程序的執(zhí)行時間,在程序調(diào)試結(jié)束后,將部分函數(shù)用宏定義來代替。注意,應(yīng)該在程序調(diào)試結(jié)束后再定義宏,因為大多數(shù)編譯系統(tǒng)在宏展開之后才會報錯,這樣會增加排錯的難度。8、盡量少用全局變量,多用局部變量。因為全局變量是放在數(shù)據(jù)存儲器中,定義一個全局變量,MCU就少一個可以利用的數(shù)據(jù)存儲器空間,如果定義了太多的全局變量,會導(dǎo)致編譯器無足夠的內(nèi)存可以分配。而局部變量大多定位于MCU內(nèi)部的寄存器中,在絕大多數(shù)MCU中,使用寄存器操作速度比數(shù)據(jù)存儲器快,指令也更多更靈活,有利于生成質(zhì)量更高的代碼,而且局部變量所的占用的寄存器和數(shù)據(jù)存儲器在不同的模塊中可以重復(fù)利用。9、設(shè)定合適的編譯程序選項許多編譯程序有幾種不同的優(yōu)化選項,在使用前應(yīng)理解各優(yōu)化選項的含義,然后選用最合適的一種優(yōu)化方式。通常情況下一旦選用最高級優(yōu)化,編譯程序會近乎病態(tài)地追求代碼優(yōu)化,可能會影響程序的正確性,導(dǎo)致程序運行出錯。因此應(yīng)熟悉所使用的編譯器,應(yīng)知道哪些參數(shù)在優(yōu)化時會受到影響,哪些參數(shù)不會受到影響。文案實用在ICCAVR中,有“口6£231”和“EnableCodeCompression”兩個優(yōu)化選項。在CodeVisionAVR中,“Tiny”和“small”兩種內(nèi)存模式。在IAR中,共有7種不同的內(nèi)存模式選項。在GCCAVR中優(yōu)化選項更多,一不小心更容易選到不恰當?shù)倪x項。二、代碼的優(yōu)化1、選擇合適的算法和數(shù)據(jù)結(jié)構(gòu)應(yīng)該熟悉算法語言,知道各種算法的優(yōu)缺點,具體資料請參見相應(yīng)的參考資料,有很多計算機書籍上都有介紹。將比較慢的順序查找法用較快的二分查找或亂序查找法代替,插入排序或冒泡排序法用快速排序、合并排序或根排序代替,都可以大大提高程序執(zhí)行的效率。.選擇一種合適的數(shù)據(jù)結(jié)構(gòu)也很重要,比如你在一堆隨機存放的數(shù)中使用了大量的插入和刪除指令,那使用鏈表要快得多。數(shù)組與指針具有十分密碼的關(guān)系,一般來說,指針比較靈活簡潔,而數(shù)組則比較直觀,容易理解。對于大部分的編譯器,使用指針比使用數(shù)組生成的代碼更短,執(zhí)行效率更高。但是在Keil中則相反,使用數(shù)組比使用的指針生成的代碼更短。2、使用盡量小的數(shù)據(jù)類型文案實用能夠使用字符型(char)定義的變量,就不要使用整型(int)變量來定義;能夠使用整型變量定義的變量就不要用長整型(longint),能不使用浮點型?10分1)變量就不要使用浮點型變量。當然,在定義變量后不要超過變量的作用范圍,如果超過變量的范圍賦值,C編譯器并不報錯,但程序運行結(jié)果卻錯了,而且這樣的錯誤很難發(fā)現(xiàn)。在ICCAVR中,可以在Options中設(shè)定使用printf參數(shù),盡量使用基本型參數(shù)(%c、%d、%x、%X、%u和%$格式說明符),少用長整型參數(shù)(%壯、%lu、%lx和%”格式說明符),至于浮點型的參數(shù)(%。則盡量不要使用,其,它C編譯器也一樣。在其它條件不變的情況下,使用%f參數(shù),會使生成的代碼的數(shù)量增加很多,執(zhí)行速度降低。3、使用自加、自減指令通常使用自加、自減指令和復(fù)合賦值表達式(如a-=1及a+=1等)都能夠生成高質(zhì)量的程序

溫馨提示

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

評論

0/150

提交評論