1、PICC和MPLAB集成 PICC和MPLAB集成: PICC有自己的文本編輯器,不過是DOS風(fēng)格的,看來PICC的工程師要專業(yè)冷到酷底了... 大家大可不必用它,如果你沒什么癖好的話,你不會(huì)不用UltraEdit 吧? 1:建立你的工作目錄: 建議在C盤根目錄下建立一個(gè)以A開頭的文件夾做為工作目錄.因?yàn)槟銜?huì)發(fā)現(xiàn)它總是在你查找文件時(shí)候第 一個(gè)跳入你眼中. 2:MPLAB調(diào)用PICC.(以MPLAB5.7版本為例子) 啟動(dòng)MPLAB.在Project-->Install Language Tool: Language Suite----->hi-tech picc Tool Name ---->PICC Compiler Executable ---->c:hi-picinpicc.exe (假如你的PICC是默認(rèn)安裝的) 選Command-line 最后OK. 上面這步只需要設(shè)定一次,除非你重新安裝了MPLAB. 3:創(chuàng)建你的項(xiàng)目文件:(假如你實(shí)現(xiàn)用EDIT編輯好了一個(gè)叫AA.C的C代碼文件) Project-->New Project-->File Name--->myc (假如我們把項(xiàng)目文件取名字叫MYC.PJT) 右邊窗口當(dāng)然要選擇中你的工作目錄.然后OK. 4:設(shè)定你的PICC工作參數(shù): Project-->Edit Project 上面4個(gè)欄目就用默認(rèn)的,空的也就讓它空著,無所謂的. 需要修改的是: Development Mode---->選擇你的PIC型號(hào).當(dāng)然要選擇Mplab SIM Simulator 讓你可以用軟件仿真. Language Tool Suite--->HI-TECH PICC 上面的步驟,你可能會(huì)遇見多個(gè)提示條,不要管它,一路確定. 下面是PICC編譯器的選擇項(xiàng): 雙擊Project Files 窗口里面的MYC.HEX,出現(xiàn)一個(gè)選擇攔目.命令很多,大家可以看PICC文本編 輯器里面的HELP,里面有詳細(xì)說明. 下面就推薦幾個(gè)常用也是建議用的: Generate debug info 以及下面的2項(xiàng). Produce assembler list file 就在它們后面打勾即可,其它的不要管,除非你有特殊要求. 5:添加你的C代碼文件: 當(dāng)進(jìn)行了前面幾步后,按Add Node 找到AA.C文件就OK了. 6:編譯C代碼: 最簡單的一步:直接按下F10. 編譯完后,會(huì)出現(xiàn)各種調(diào)試信息.C代碼對(duì)應(yīng)的匯編代碼就是工作目錄里面的AA.IST,用EDIT 打開可以看見詳細(xì)的對(duì)比. 7:其它,要是一切都沒問題,那么你就可以調(diào)試和燒片了,和以往操作無異. 2、如何從匯編轉(zhuǎn)向PICC 首先要求你要有C 語言的基礎(chǔ)。PICC 不支持C++,這對(duì)于習(xí)慣了C++的朋友還得翻翻C 語言的書。C 代碼的頭文件一定要有#include<pic.h>,它是很多頭文件的集合,C 編譯器在pic.h 中根據(jù)你的芯片自動(dòng)栽 入相應(yīng)的其它頭文件。這點(diǎn)比匯編好用。載入的頭文件中其實(shí)是聲明芯片的寄存器和一些函數(shù)。順便摘抄 一個(gè)片段: static volatile unsigned char TMR0 @ 0x01; static volatile unsigned char PCL @ 0x02; static volatile unsigned char STATUS @ 0x03; 可以看出和匯編的頭文件中定義寄存器是差不多的。如下: TMR0 EQU 0X01; PCL EQU 0X02; STATUS EQU 0X03; 都是把無聊的地址定義為大家公認(rèn)的名字。 一:怎么附值? 如對(duì)TMR0 附值,匯編中: MOVLW 200; MOVWF TMR0; 當(dāng)然得保證當(dāng)前頁面在0,不然會(huì)出錯(cuò)。 C 語言: TMR0=200;//無論在任何頁面都不會(huì)出錯(cuò)。 可以看出來C 是很直接了當(dāng)?shù)。并且最大好處是操作一個(gè)寄存器時(shí)候,不用考慮頁面的問題。一切由 C 自動(dòng)完成。 二:怎么位操作? 匯編中的位操作是很容易的。在C 中更簡單。C 的頭文件中已經(jīng)對(duì)所有可能需要位操作的寄存器的每 一位都有定義名稱: 如:PORTA 的每一個(gè)I/O 口定義為:RA0、RA1、RA2。。。RA7。OPTION 的每一位定義為:PS0、 PS1、PS2 、PSA 、T0SE、T0CS、INTEDG 、RBPU?梢詫(duì)其直接進(jìn)行運(yùn)算和附值。 如: RA0=0; RA2=1; 在匯編中是: BCF PORTA,0; BSF PORTA,2; 可以看出2 者是大同小異的,只是C 中不需要考慮頁面的問題。 三:內(nèi)存分配問題: 在匯編中定義一個(gè)內(nèi)存是一件很小心的問題,要考慮太多的問題,稍微不注意就會(huì)出錯(cuò)。比如16 位的 運(yùn)算等。用C 就不需要考慮太多。下面給個(gè)例子: 16 位的除法(C 代碼): INT X=5000; INT Y=1000; INT Z=X/Y; 而在匯編中則需要花太多精力。 給一個(gè)小的C 代碼,用RA0 控制一個(gè)LED 閃爍: #include<pic.h> void main() { int x; CMCON=0B111; //掉A 口比較器,要是有比較器功能的話。 ADCON1=0B110; //掉A/D 功能,要是有A/D 功能的話。 TRISA=0; //RA 口全為輸出。 loop:RA0=!RA0; for(x=60000;--x;){;} //延時(shí) goto loop; } 說說RA0=!RA0 的意思:PIC 對(duì)PORT 寄存器操作都是先讀取----修改----寫入。上句的含義是程序先 讀RA0,然后取反,最后把運(yùn)算后的值重新寫入RA0,這就實(shí)現(xiàn)了閃爍的功能。 3、淺談PICC 的位操作 由于PIC 處理器對(duì)位操作是最高效的,所以把一些BOOL 變量放在一個(gè)內(nèi)存的位中,既可以達(dá)到運(yùn)算 速度快,又可以達(dá)到最大限度節(jié)省空間的目的。在C 中的位操作有多種選擇。 ********************************************* 如:char x;x=x|0B00001000; /*對(duì)X 的4 位置1。*/ char x;x=x&0B11011111; /*對(duì)X 的5 位清0。*/ 把上面的變成公式則是: #define bitset(var,bitno)(var |=1<<bitno) #define bitclr(var,bitno)(var &=~(1<<bitno)) 則上面的操作就是:char x;bitset(x,4) char x;bitclr(x,5) ************************************************* 但上述的方法有缺點(diǎn),就是對(duì)每一位的含義不直觀,最好是能在代碼中能直觀看出每一位代表的意思, 這樣就能提高編程效率,避免出錯(cuò)。如果我們想用X 的0-2 位分別表示溫度、電壓、電流的BOOL 值可以 如下: unsigned char x @ 0x20; /*象匯編那樣把X 變量定義到一個(gè)固定內(nèi)存中。*/ bit temperature@ (unsigned)&x*8+0; /*溫度*/ bit voltage@ (unsigned)&x*8+1; /*電壓*/ bit current@ (unsigned)&x*8+2; /*電流 */ 這樣定義后X 的位就有一個(gè)形象化的名字,不再是枯燥的1、2、3、4 等數(shù)字了。可以對(duì)X 全局修改, 也可以對(duì)每一位進(jìn)行操作: char=255; temperature=0; if(voltage)...... ***************************************************************** 還有一個(gè)方法是用C 的struct 結(jié)構(gòu)來定義: 如: struct cypok{ temperature:1; /*溫度*/ voltage:1; /*電壓*/ current:1; /*電流*/ none:4; }x @ 0x20; 這樣就可以用 x.temperature=0; if(x.current).... 等操作了。 ********************************************************** 上面的方法在一些簡單的設(shè)計(jì)中很有效,但對(duì)于復(fù)雜的設(shè)計(jì)中就比較吃力。如象在多路工業(yè)控制上。 前端需要分別收集多路的多路信號(hào),然后再設(shè)定控制多路的多路輸出。如:有2 路控制,每一路的前端信 號(hào)有溫度、電壓、電流。后端控制有電機(jī)、喇叭、繼電器、LED。如果用匯編來實(shí)現(xiàn)的話,是很頭疼的事 情,用C 來實(shí)現(xiàn)是很輕松的事情,這里也涉及到一點(diǎn)C 的內(nèi)存管理(其實(shí)C 的最大優(yōu)點(diǎn)就是內(nèi)存管理)。 采用如下結(jié)構(gòu): union cypok{ struct out{ motor:1; /*電機(jī)*/ relay:1; /*繼電器*/ speaker:1; /*喇叭*/ led1:1; /*指示燈*/ led2:1; /*指示燈*/ }out; struct in{ none:5; temperature:1; /*溫度*/ voltage:1; /*電壓*/ current:1; /*電流*/ }in; char x; }; union cypok an1; union cypok an2; 上面的結(jié)構(gòu)有什么好處呢? 細(xì)分了信號(hào)的路an1 和an2; 細(xì)分了每一路的信號(hào)的類型(是前端信號(hào)in 還是后端信號(hào)out): an1.in ; an1.out; an2.in; an2.out; 然后又細(xì)分了每一路信號(hào)的具體含義,如: an1.in.temperature; an1.out.motor; an2.in.voltage; an2.out.led2;等 這樣的結(jié)構(gòu)很直觀的在2 個(gè)內(nèi)存中就表示了2 路信號(hào)。并且可以極其方便的擴(kuò)充。 如添加更多路的信號(hào),只需要添加: union cypok an3; union cypok an4; 從上面就可以看出用C 的巨大好處 4、PICC 之延時(shí)函數(shù)和循環(huán)體優(yōu)化。 很多朋友說C 中不能精確控制延時(shí)時(shí)間,不能象匯編那樣直觀。其實(shí)不然,對(duì)延時(shí)函數(shù)深入了解一下 就能設(shè)計(jì)出一個(gè)理想的框價(jià)出來。一般的我們都用for(x=100;--x;){;}此句等同與x=100;while(--x){;}; 或for(x=0;x<100;x++){;}。 來寫一個(gè)延時(shí)函數(shù)。 在這里要特別注意:X=100,并不表示只運(yùn)行100 個(gè)指令時(shí)間就跳出循環(huán)。 可以看看編譯后的匯編: x=100;while(--x){;} 匯編后: MOVlw 100 bcf 3,5 bcf 3,6 MOVwf _delay l2 decfsz _delay goto l2 return 從代碼可以看出總的指令是是303 個(gè),其公式是8+3*(X-1)。注意其中循環(huán)周期是X-1 是99 個(gè)。這 里總結(jié)的是x 為char 類型的循環(huán)體,當(dāng)x 為int 時(shí)候,其中受X 值的影響較大。建議設(shè)計(jì)一個(gè)char 類型的 循環(huán)體,然后再用一個(gè)循環(huán)體來調(diào)用它,可以實(shí)現(xiàn)精確的長時(shí)間的延時(shí)。下面給出一個(gè)能精確控制延時(shí)的 函數(shù),此函數(shù)的匯編代碼是最簡潔、最能精確控制指令時(shí)間的: void delay(char x,char y){ char z; do{ z=y; do{;}while(--z); }while(--x); } 其指令時(shí)間為:7+(3*(Y-1)+7)*(X-1)如果再加上函數(shù)調(diào)用的call 指令、頁面設(shè)定、傳遞參數(shù) 花掉的7 個(gè)指令。則是:14+(3*(Y-1)+7)*(X-1)。如果要求不是特別嚴(yán)格的延時(shí),可以用這個(gè)函數(shù): void delay(){ unsigned int d=1000; while(--d){;} } 此函數(shù)在4M 晶體下產(chǎn)生10003us 的延時(shí),也就是10MS。如果把D 改成2000,則是20003us,以此類 推。有朋友不明白,為什么不用while(x--)后減量,來控制設(shè)定X 值是多少就循環(huán)多少周期呢?現(xiàn)在看看編 譯它的匯編代碼: bcf 3,5 bcf 3,6 MOVlw 10 MOVwf _delay l2 decf _delay incfsz _delay,w goto l2 return 可以看出循環(huán)體中多了一條指令,不簡潔。所以在PICC 中最好用前減量來控制循環(huán)體。 再談?wù)勥@樣的語句: for(x=100;--x;){;}和for(x=0;x<100;x++){;} 從字面上看2 者意思一樣,但可以通過匯編查看代碼。后者代碼雍長,而前者就很好的匯編出了簡潔的代 碼。所以在PICC 中最好用前者的形式來寫循環(huán)體,好的C 編譯器會(huì)自動(dòng)把增量循環(huán)化為減量循環(huán)。因?yàn)?BR>這是由處理器硬件特性決定的。PICC 并不是一個(gè)很智能的C 編譯器,所以還是人腦才是第一的,掌握一些 經(jīng)驗(yàn)對(duì)寫出高效,簡潔的代碼是有好處的。 5、深入探討PICC之位操作 一:用位操作來做一些標(biāo)志位,也就是BOOL變量.可以簡單如下定義: bit a,b,c; PICC會(huì)自動(dòng)安排一個(gè)內(nèi)存,并在此內(nèi)存中自動(dòng)安排一位來對(duì)應(yīng)a,b,c.由于我們只是用它們來簡單的 表示一些0,1信息,所以我們不需要詳細(xì)的知道它們的地址\位究竟是多少,只管拿來就用好了. 二:要是需要用一個(gè)地址固定的變量來位操作,可以參照PIC.H里面定義寄存器. 如:用25H內(nèi)存來定義8個(gè)位變量. static volatile unsigned char myvar @ 0x25; static volatile bit b7 @ (unsigned)&myvar*8+7; static volatile bit b6 @ (unsigned)&myvar*8+6; static volatile bit b5 @ (unsigned)&myvar*8+5; 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; 這樣即可以對(duì)MYVAR操作,也可以對(duì)B0--B7直接位操作. 但不好的是,此招在低檔片子,如C5X系列上可能會(huì)出問題. 還有就是表達(dá)起來復(fù)雜,你不覺得輸入代碼受累么?呵呵 三:這也是一些常用手法: #define testbit(var, bit) ((var) & (1 <<(bit))) //測試某一位,可以做BOOL運(yùn)算 #define setbit(var, bit) ((var) |= (1 << (bit))) //把某一位置1 #define clrbit(var, bit) ((var) &= ~(1 << (bit))) //把某一位清0 付上一段代碼,可以用MPLAB調(diào)試觀察 #include<pic.h> #define testbit(var, bit) ((var) & (1 <<(bit))) #define setbit(var, bit) ((var) |= (1 << (bit))) #define clrbit(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; else a=100; while(1){;} } 四:用標(biāo)準(zhǔn)C的共用體來表示: #include<pic.h> union var{ unsigned char byte; struct { unsigned b0:1, b1:1, b2: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; else a=100; while(1){;} } 五:用指針轉(zhuǎn)換來表示: #include<pic.h> typedef struct { unsigned b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1; } bits; //先定義一個(gè)變量的位 #define mybit0 (((bits *)&myvar)->b0) //取myvar 的地址(&myvar)強(qiáng)制轉(zhuǎn)換成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)->b7) char myvar; char a,b; void main(){ myvar=0B10101010; a=mybit0; b=mybit1; if(mybit7) a=255; else a=100; while(1){;} } |
|