PIC單片機(jī)C語(yǔ)言編程教程.doc_第1頁(yè)
PIC單片機(jī)C語(yǔ)言編程教程.doc_第2頁(yè)
PIC單片機(jī)C語(yǔ)言編程教程.doc_第3頁(yè)
PIC單片機(jī)C語(yǔ)言編程教程.doc_第4頁(yè)
PIC單片機(jī)C語(yǔ)言編程教程.doc_第5頁(yè)
已閱讀5頁(yè),還剩59頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

PIC 單片機(jī) C 語(yǔ)言編程簡(jiǎn)介用 C 語(yǔ)言來(lái)開(kāi)發(fā)單片機(jī)系統(tǒng)軟件最大的好處是編寫(xiě)代碼效率高、軟件調(diào)試直觀、維護(hù)升級(jí)方便、代碼的重復(fù)利用率高、便于跨平臺(tái)的代碼移植等等,因此 C 語(yǔ)言編程在單片機(jī)系統(tǒng)設(shè)計(jì)中已得到越來(lái)越廣泛的運(yùn)用。針對(duì) PIC 單片機(jī)的軟件開(kāi)發(fā),同樣可以用 C 語(yǔ)言實(shí)現(xiàn)。但在單片機(jī)上用 C 語(yǔ)言寫(xiě)程序和在 PC 機(jī)上寫(xiě)程序絕對(duì)不能簡(jiǎn)單等同。現(xiàn)在的 PC 機(jī)資源十分豐富,運(yùn)算能力強(qiáng)大,因此程序員在寫(xiě) PC 機(jī)的應(yīng)用程序時(shí)幾乎不用關(guān)心編譯后的可執(zhí)行代碼在運(yùn)行過(guò)程中需要占用多少系統(tǒng)資源,也基本不用擔(dān)心運(yùn)行效率有多高。寫(xiě)單片機(jī)的 C 程序最關(guān)鍵的一點(diǎn)是單片機(jī)內(nèi)的資源非常有限,控制的實(shí)時(shí)性要求又很高,因此,如果沒(méi)有對(duì)單片機(jī)體系結(jié)構(gòu)和硬件資源作詳盡的了解,以筆者的愚見(jiàn)認(rèn)為是無(wú)法寫(xiě)出高質(zhì)量實(shí)用的 C 語(yǔ)言程序。這就是為什么前面所有章節(jié)中的的示范代碼全部用基礎(chǔ)的匯編指令實(shí)現(xiàn)的原因,希望籍此能使讀者對(duì) PIC 單片機(jī)的指令體系和硬件資源有深入了解,在這基礎(chǔ)之上再來(lái)討論 C 語(yǔ)言編程,就有水到渠成的感覺(jué)。本書(shū)圍繞中檔系列 PIC 單片機(jī)來(lái)展開(kāi)討論,Microchip 公司自己沒(méi)有針對(duì)中低檔系列 PIC單片機(jī)的 C 語(yǔ)言編譯器,但很多專(zhuān)業(yè)的第三方公司有眾多支持 PIC 單片機(jī)的 C 語(yǔ)言編譯器提供,常見(jiàn)的有 Hitech、CCS、IAR、Bytecraft 等公司。其中筆者最常用的是 Hitech 公司的PICC 編譯器,它穩(wěn)定可靠,編譯生成的代碼效率高,在用 PIC 單片機(jī)進(jìn)行系統(tǒng)設(shè)計(jì)和開(kāi)發(fā)的工程師群體中得到廣泛認(rèn)可。其正式完全版軟件需要購(gòu)置,但在其網(wǎng)站上有限時(shí)的試用版供用戶(hù)評(píng)估。另外,Hitech 公司針對(duì)廣大 PIC 的業(yè)余愛(ài)好者和初學(xué)者還提供了完全免費(fèi)的學(xué)習(xí)版 PICC-Lite 編譯器套件,它的使用方式和完全版相同,只是支持的 PIC 單片機(jī)型號(hào)限制在 PIC16F84、PIC16F877 和 PIC16F628 等幾款。這幾款 Flash 型的單片機(jī)因其所具備的豐富的片上資源而最適用于單片機(jī)學(xué)習(xí)入門(mén),因此筆者建議感興趣的讀者可從 PICC-Lite 入手掌握 PIC 單片機(jī)的 C 語(yǔ)言編程。在此列出幾個(gè)主要的針對(duì) PIC 單片機(jī)的 C 編譯器相關(guān)連接網(wǎng)址,供讀者參考:Hitech-PICC: IAR:CCS:/picc.shtmlByteCraft:/mpccaps.html本章將介紹 Hitech-PICC 編譯器的一些基本概念,由于篇幅所限將不涉及 C 語(yǔ)言的標(biāo)準(zhǔn)語(yǔ)法和基礎(chǔ)知識(shí)介紹,因?yàn)樵谶@些方面都有大量的書(shū)籍可以參考。重點(diǎn)突出針對(duì) PIC 單片機(jī)的特點(diǎn)而所需要特別注意的地方。11.2Hitech-PICC 編譯器PICC 基本上符合 ANSI 標(biāo)準(zhǔn),除了一點(diǎn):它不支持函數(shù)的遞歸調(diào)用。其主要原因是因?yàn)?PIC 單片機(jī)特殊的堆棧結(jié)構(gòu)。在前面介紹 PIC 單片機(jī)架構(gòu)時(shí)已經(jīng)詳細(xì)說(shuō)明了 PIC 單片機(jī)中的堆棧是硬件實(shí)現(xiàn)的,其深度已隨芯片而固定,無(wú)法實(shí)現(xiàn)需要大量堆棧操作的遞歸算法;另外在 PIC 單片機(jī)中實(shí)現(xiàn)軟件堆棧的效率也不是很高,為此,PICC 編譯器采用一種叫做“靜態(tài)覆蓋”的技術(shù)以實(shí)現(xiàn)對(duì) C 語(yǔ)言函數(shù)中的局部變量分配固定的地址空間。經(jīng)這樣處理后產(chǎn)生出的機(jī)器代碼效率很高,按筆者實(shí)際使用的體會(huì),當(dāng)代碼量超過(guò) 4K 字后,C 語(yǔ)言編譯出的代碼長(zhǎng)度和全部用匯編代碼實(shí)現(xiàn)時(shí)的差別已經(jīng)不是很大( bsf= bcf0x20,70x20,3if (tmp&0xfe)= btfsc 0x20,0即所有只對(duì)變量中某一位操作的 C 語(yǔ)句代碼將被直接編譯成匯編的位操作指令。雖然編程時(shí)可以不用太關(guān)心,但如果能了解編譯器是如何工作的,那將有助于引導(dǎo)我們寫(xiě)出高效簡(jiǎn)介的 C 語(yǔ)言原程序。在有些應(yīng)用中需要將一組位變量放在同一個(gè)字節(jié)中以便需要時(shí)一次性地進(jìn)行讀寫(xiě),這一功能可以通過(guò)定義一個(gè)位域結(jié)構(gòu)和一個(gè)字節(jié)變量的聯(lián)合來(lái)實(shí)現(xiàn),例如:union struct unsigned b0: 1; unsigned b1: 1; unsigned b2: 1; unsigned b3: 1; unsigned b4: 1; unsigned b5: 1; unsigned : 2; /最高兩位保留 oneBit; unsigned char allBits; myFlag;例 11-3 定義位變量于同一字節(jié)需要存取其中某一位時(shí)可以myFlag.oneBit.b3=1; /b3 位置 1一次性將全部位清零時(shí)可以myFlag.allBits=0; /全部位變量清 0當(dāng)程序中把非位變量進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換成位變量時(shí),要注意編譯器只對(duì)普通變量的最低位做判別:如果最低位是 0,則轉(zhuǎn)換成位變量 0;如果最低位是 1,則轉(zhuǎn)換成位變量 1。而標(biāo)準(zhǔn)的 ANSI-C 做法是判整個(gè)變量值是否為 0。另外,函數(shù)可以返回一個(gè)位變量,實(shí)際上此返回的位變量將存放于單片機(jī)的進(jìn)位位中帶出返回。11.5.6 PICC 中的浮點(diǎn)數(shù)PICC 中描述浮點(diǎn)數(shù)是以 IEEE-754 標(biāo)準(zhǔn)格式實(shí)現(xiàn)的。此標(biāo)準(zhǔn)下定義的浮點(diǎn)數(shù)為 32 位長(zhǎng),在單片機(jī)中要用 4 個(gè)字節(jié)存儲(chǔ)。為了節(jié)約單片機(jī)的數(shù)據(jù)空間和程序空間,PICC 專(zhuān)門(mén)提供了一種長(zhǎng)度為 24 位的截短型浮點(diǎn)數(shù),它損失了浮點(diǎn)數(shù)的一點(diǎn)精度,但浮點(diǎn)運(yùn)算的效率得以提高。在程序中定義的 float 型標(biāo)準(zhǔn)浮點(diǎn)數(shù)的長(zhǎng)度固定為 24 位,雙精度 double 型浮點(diǎn)數(shù)一般也是 24 位長(zhǎng),但可以在程序編譯選項(xiàng)中選擇 double 型浮點(diǎn)數(shù)為 32 位,以提高計(jì)算的精度。一般控制系統(tǒng)中關(guān)心的是單片機(jī)的運(yùn)行效率,因此在精度能夠滿(mǎn)足的前提下盡量選擇24 位的浮點(diǎn)數(shù)運(yùn)算。11.5.7 PICC 中變量的絕對(duì)定位首先必須強(qiáng)調(diào),在用 C 語(yǔ)言寫(xiě)程序時(shí)變量一般由編譯器和連接器最后定位,在寫(xiě)程序之時(shí)無(wú)需知道所定義的變量具體被放在哪個(gè)地址(除了 bank 必須聲明)。真正需要絕對(duì)定位的只是單片機(jī)中的那些特殊功能寄存器,而這些寄存器的地址定位在PICC 編譯環(huán)境所提供的頭文件中已經(jīng)實(shí)現(xiàn),無(wú)需用戶(hù)操心。編程員所要了解的也就是 PICC是如何定義這些特殊功能寄存器和其中的相關(guān)控制位的名稱(chēng)。好在 PICC 的定義標(biāo)準(zhǔn)基本上按照芯片的數(shù)據(jù)手冊(cè)中的名稱(chēng)描述進(jìn)行,這樣就秉承了變量命名的一貫性。一個(gè)變量絕對(duì)定位的例子如下:unsigned char tmpData 0x20; /tmpData 定位在地址 0x20千萬(wàn)注意,PICC 對(duì)絕對(duì)定位的變量不保留地址空間。換句話說(shuō),上面變量 tmpData 的地址是 0x20,但最后 0x20 處完全有可能又被分配給了其它變量使用,這樣就發(fā)生了地址沖突。因此針對(duì)變量的絕對(duì)定位要特別小心。從筆者的應(yīng)用經(jīng)驗(yàn)看,在一般的程序設(shè)計(jì)中用戶(hù)自定義的變量實(shí)在是沒(méi)有絕對(duì)定位的必要。如果需要,位變量也可以絕對(duì)定位。但必須遵循上面介紹的位變量編址的方式。如果一個(gè)普通變量已經(jīng)被絕對(duì)定位,那么此變量中的每個(gè)數(shù)據(jù)位就可以用下面的計(jì)算方式實(shí)現(xiàn)位變量指派:unsigned char tmpData 0x20; /tmpData 定位在地址 0x20bit tmpBit0 tmpData*8+0; /tmpBit0 對(duì)應(yīng)于 tmpData 第 0 位bit tmpBit1 tmpData*8+1; /tmpBit0 對(duì)應(yīng)于 tmpData 第 1 位bit tmpBit2 tmpData*8+2; /tmpBit0 對(duì)應(yīng)于 tmpData 第 2 位如果 tmpData 事先沒(méi)有被絕對(duì)定位,那就不能用上面的位變量定位方式。11.5.8 PICC 的其它變量修飾關(guān)鍵詞&O1540; extern 外部變量聲明如果在一個(gè) C 程序文件中要使用一些變量但其原型定義寫(xiě)在另外的文件中,那么在本文件中必須將這些變量聲明成“extern”外部類(lèi)型。例如程序文件 code1.c 中有如下定義:bank1 unsigned char var1, var2; /定義了 bank1 中的兩個(gè)變量在另外一個(gè)程序文件 code2.c 中要對(duì)上面定義的變量進(jìn)行操作,則必須在程序的開(kāi)頭定義:extern bank1 unsigned char var1, var2; /聲明位于 bank1 的外部變量&O1540; volatile 易變型變量聲明PICC 中還有一個(gè)變量修飾詞在普通的 C 語(yǔ)言介紹中一般是看不到的,這就是關(guān)鍵詞“volatile”。顧名思義,它說(shuō)明了一個(gè)變量的值是會(huì)隨機(jī)變化的,即使程序沒(méi)有刻意對(duì)它進(jìn)行任何賦值操作。在單片機(jī)中,作為輸入的 IO 端口其內(nèi)容將是隨意變化的;在中斷內(nèi)被修改的變量相對(duì)主程序流程來(lái)講也是隨意變化的;很多特殊功能寄存器的值也將隨著指令的運(yùn)行而動(dòng)態(tài)改變。所有這種類(lèi)型的變量必須將它們明確定義成“volatile”類(lèi)型,例如:volatile unsigned char STATUS 0x03;volatile bit commFlag;“volatile”類(lèi)型定義在單片機(jī)的 C 語(yǔ)言編程中是如此的重要,是因?yàn)樗梢愿嬖V編譯器的優(yōu)化處理器這些變量是實(shí)實(shí)在在存在的,在優(yōu)化過(guò)程中不能無(wú)故消除。假定你的程序定義了一個(gè)變量并對(duì)其作了一次賦值,但隨后就再也沒(méi)有對(duì)其進(jìn)行任何讀寫(xiě)操作,如果是非volatile 型變量,優(yōu)化后的結(jié)果是這個(gè)變量將有可能被徹底刪除以節(jié)約存儲(chǔ)空間。另外一種情形是在使用某一個(gè)變量進(jìn)行連續(xù)的運(yùn)算操作時(shí),這個(gè)變量的值將在第一次操作時(shí)被復(fù)制到中間臨時(shí)變量中,如果它是非 volatile 型變量,則緊接其后的其它操作將有可能直接從臨時(shí)變量中取數(shù)以提高運(yùn)行效率,顯然這樣做后對(duì)于那些隨機(jī)變化的參數(shù)就會(huì)出問(wèn)題。只要將其定義成 volatile 類(lèi)型后,編譯后的代碼就可以保證每次操作時(shí)直接從變量地址處取數(shù)。&O1540; const 常數(shù)型變量聲明如果變量定義前冠以“const”類(lèi)型修飾,那么所有這些變量就成為常數(shù),程序運(yùn)行過(guò)程中不能對(duì)其修改。除了位變量,其它所有基本類(lèi)型的變量或高級(jí)組合變量都將被存放在程序空間(ROM 區(qū))以節(jié)約數(shù)據(jù)存儲(chǔ)空間。顯然,被定義在 ROM 區(qū)的變量是不能再在程序中對(duì)其進(jìn)行賦值修改的,這也是“const”的本來(lái)意義。實(shí)際上這些數(shù)據(jù)最終都將以“retlw”的指令形式存放在程序空間,但 PICC 會(huì)自動(dòng)編譯生成相關(guān)的附加代碼從程序空間讀取這些常數(shù),編程員無(wú)需太多操心。例如:const unsigned char name=”This is a demo”; /定義一個(gè)常量字符串如果定義了 “const”類(lèi)型的位變量,那么這些位變量還是被放置在 RAM 中,但程序不能對(duì)其賦值修改。本來(lái),不能修改的位變量沒(méi)有什么太多的實(shí)際意義,相信大家在實(shí)際編程時(shí)不會(huì)大量用到。&O1540; persistent 非初始化變量聲明按照標(biāo)準(zhǔn) C 語(yǔ)言的做法,程序在開(kāi)始運(yùn)行前首先要把所有定義的但沒(méi)有預(yù)置初值的變量全部清零。PICC 會(huì)在最后生成的機(jī)器碼中加入一小段初始化代碼來(lái)實(shí)現(xiàn)這一變量清零操作,且這一操作將在 main 函數(shù)被調(diào)用之前執(zhí)行。問(wèn)題是作為一個(gè)單片機(jī)的控制系統(tǒng)有很多變量是不允許在程序復(fù)位后被清零的。為了達(dá)到這一目的,PICC 提供了“persistent”修飾詞以聲明此類(lèi)變量無(wú)需在復(fù)位時(shí)自動(dòng)清零,編程員應(yīng)該自己決定程序中的那些變量是必須聲明成“persisten”類(lèi)型,而且須自己判斷什么時(shí)候需要對(duì)其進(jìn)行初始化賦值。例如:persistent unsigned char hour,minute,second; /定義時(shí)分秒變量經(jīng)常用到的是如果程序經(jīng)上電復(fù)位后開(kāi)始運(yùn)行,那么需要將 persistent 型的變量初始化,如果是其它形式的復(fù)位,例如看門(mén)狗引發(fā)的復(fù)位,則無(wú)需對(duì) persistent 型變量作任何修改。PIC 單片機(jī)內(nèi)提供了各種復(fù)位的判別標(biāo)志,用戶(hù)程序可依具體設(shè)計(jì)靈活處理不同的復(fù)位情形。11.5.9 PICC 中的指針PICC 中指針的基本概念和標(biāo)準(zhǔn) C 語(yǔ)法沒(méi)有太多的差別。但是在 PIC 單片機(jī)這一特定的架構(gòu)上,指針的定義方式還是有幾點(diǎn)需要特別注意。&O1540; 指向 RAM 的指針如果是匯編語(yǔ)言編程,實(shí)現(xiàn)指針尋址的方法肯定就是用 FSR 寄存器,PICC 也不例外。為了生成高效的代碼,PICC 在編譯 C 原程序時(shí)將指向 RAM 的指針操作最終用 FSR 來(lái)實(shí)現(xiàn)間接尋址。這樣就勢(shì)必產(chǎn)生一個(gè)問(wèn)題:FSR 能夠直接連續(xù)尋址的范圍是 256 字節(jié)(bank0/1或 bank2/3),要覆蓋最大 512 字節(jié)的內(nèi)部數(shù)據(jù)存儲(chǔ)空間,又該如何讓定義指針?PICC 還是將這一問(wèn)題留給編程員自己解決:在定義指針時(shí)必須明確指定該指針?biāo)m用的尋址區(qū)域,例如:unsigned char *ptr0; /定義覆蓋 bank0/1 的指針bank2 unsigned char *ptr1; /定義覆蓋 bank2/3 的指針bank3 unsigned char *ptr2; /定義覆蓋 bank2/3 的指針上面定義了三個(gè)指針變量,其中指針沒(méi)有任何 bank 限定,缺省就是指向 bank0 和 bank1;和一個(gè)指明了 bank2,另一個(gè)指明了 bank3,但實(shí)際上兩者是一樣的,因?yàn)橐粋€(gè)指針可以同時(shí)覆蓋兩個(gè) bank 的存儲(chǔ)區(qū)域。另外,上面三個(gè)指針變量自身都存放在 bank0 中。我們將在稍后介紹如何在其它 bank 中存放指針變量。既然定義的指針有明確的 bank 適用區(qū)域,在對(duì)指針變量賦值時(shí)就必須實(shí)現(xiàn)類(lèi)型匹配,下面的指針賦值將產(chǎn)生一個(gè)致命錯(cuò)誤:unsigned char *ptr0; bank2 unsigned char buff8; 程序語(yǔ)句:/定義指向 bank0/1 的指針/定義 bank2 中的一個(gè)緩沖區(qū)ptr0 = buff; /錯(cuò)誤!試圖將 bank2 內(nèi)的變量地址賦給指向 bank0/1 的指針若出現(xiàn)此類(lèi)錯(cuò)誤的指針操作,PICC 在最后連接時(shí)會(huì)告知類(lèi)似于下面的信息:Fixup overflow in expression (.)同樣的道理,若函數(shù)調(diào)用時(shí)用了指針作為傳遞參數(shù),也必須注意 bank 作用域的匹配,而這點(diǎn)往往容易被忽視。假定有下面的函數(shù)實(shí)現(xiàn)發(fā)送一個(gè)字符串的功能:void SendMessage(unsigned char *);那么被發(fā)送的字符串必須位于 bank0 或 bank1 中。如果你還要發(fā)送位于 bank2 或 bank3 內(nèi)的字符串,必須再另外單獨(dú)寫(xiě)一個(gè)函數(shù):void SendMessage_2(bank2 unsigned char *);這兩個(gè)函數(shù)從內(nèi)部代碼的實(shí)現(xiàn)來(lái)看可以一模一樣,但傳遞的參數(shù)類(lèi)型不同。按筆者的應(yīng)用經(jīng)驗(yàn)體會(huì),如果你看到了“Fixup overflow”的錯(cuò)誤指示,幾乎可以肯定是指針類(lèi)型不匹配的賦值所至。請(qǐng)重點(diǎn)檢查程序中有關(guān)指針的操作。&O1540; 指向 ROM 常數(shù)的指針如果一組變量是已經(jīng)被定義在 ROM 區(qū)的常數(shù),那么指向它的指針可以這樣定義:const unsigned char company=”Microchip”; const unsigned char *romPtr;程序中可以對(duì)上面的指針變量賦值和實(shí)現(xiàn)取數(shù)操作:romPtr company; /指針賦初值data = *romPtr+; /取指針指向的一個(gè)數(shù),然后指針加 1/定義 ROM 中的常數(shù)/定義指向 ROM 的指針?lè)催^(guò)來(lái),下面的操作將是一個(gè)錯(cuò)誤,因?yàn)樵撝羔樦赶虻氖浅?shù)型變量,不能賦值。*romPtr data; /往指針指向的地址寫(xiě)一個(gè)數(shù)&O1540; 指向函數(shù)的指針單片機(jī)編程時(shí)函數(shù)指針的應(yīng)用相對(duì)較少,但作為標(biāo)準(zhǔn) C 語(yǔ)法的一部分,PICC 同樣支持函數(shù)指針調(diào)用。如果你對(duì)編譯原理有一定的了解,就應(yīng)該明白在 PIC 單片機(jī)這一特定的架構(gòu)上實(shí)現(xiàn)函數(shù)指針調(diào)用的效率是不高的:PICC 將在 RAM 中建立一個(gè)調(diào)用返回表,真正的調(diào)用和返回過(guò)程是靠直接修改 PC 指針來(lái)實(shí)現(xiàn)的。因此,除非特殊算法的需要,建議大家盡量不要使用函數(shù)指針。&O1540; 指針的類(lèi)型修飾前面介紹的指針定義都是最基本的形式。和普通變量一樣,指針定義也可以在前面加上特殊類(lèi)型的修飾關(guān)鍵詞,例如“persistent”、“volatile”等。考慮指針本身還要限定其作用域,因此 PICC 中的指針定義初看起來(lái)顯得有點(diǎn)復(fù)雜,但只要了解各部分的具體含義,理解一個(gè)指針的實(shí)際用圖就變得很直接。 bank 修飾詞的位置含義前面介紹的一些指針有的作用于 bank0/1,有的作用于 bank2/3,但它們本身的存放位置全部在 bank0。顯然,在一個(gè)程序設(shè)計(jì)中指針變量將有可能被定位在任何可用的地址空間,這時(shí),bank 修飾詞出現(xiàn)的位置就是一個(gè)關(guān)鍵,看下面的例子:/定義指向 bank0/1 的指針,指針變量為于 bank0 中unsigned char *ptr0;/定義指向 bank2/3 的指針,指針變量為于 bank0 中bank2 unsigned char *ptr0; /定義指向 bank2/3 的指針,指針變量為于 bank1 中bank2 unsigned char * bank1 ptr0; 從中可以看出規(guī)律:前面的 bank 修飾詞指明了此指針的作用域;后面的 bank 修飾詞定義了此指針變量自身的存放位置。只要掌握了這一法則,你就可以定義任何作用域的指針且可以將指針變量放于任何 bank 中。 volatile、persistent 和 const 修飾詞的位置含義如果能理解上面介紹的 bank 修飾詞的位置含義,實(shí)際上 volatile、persistent 和 const 這些關(guān)鍵詞出現(xiàn)在前后不同位置上的含義規(guī)律是和 bank 一詞相一致的。例如:/定義指向 bank0/1 易變型字符變量的指針,指針變量位于 bank0 中且自身為非易變型volatile unsigned char *ptr0;/定義指向 bank2/3 非易變型字符變量的指針,指針變量位于 bank1 中且自身為易變型bank2 unsigned char * volatile bank1 ptr0;/定義指向 ROM 區(qū)的指針,指針變量本身也是存放于 ROM 區(qū)的常數(shù)const unsigned char * const ptr0;亦即出現(xiàn)在前面的修飾詞其作用對(duì)象是指針?biāo)柑幍淖兞浚怀霈F(xiàn)在后面的修飾詞其作用對(duì)象就是指針變量自己。11.6PICC 中的子程序和函數(shù)中檔系列的 PIC 單片機(jī)程序空間有分頁(yè)的概念,但用 C 語(yǔ)言編程時(shí)基本不用太多關(guān)心代碼的分頁(yè)問(wèn)題。因?yàn)樗泻瘮?shù)或子程序調(diào)用時(shí)的頁(yè)面設(shè)定(如果代碼超過(guò)一個(gè)頁(yè)面)都由編譯器自動(dòng)生成的指令實(shí)現(xiàn)。11.6.1 函數(shù)的代碼長(zhǎng)度限制PICC 決定了 C 原程序中的一個(gè)函數(shù)經(jīng)編譯后生成的機(jī)器碼一定會(huì)放在同一個(gè)程序頁(yè)面內(nèi)。中檔系列的 PIC 單片機(jī)其一個(gè)程序頁(yè)面的長(zhǎng)度是 2K 字,換句話說(shuō),用 C 語(yǔ)言編寫(xiě)的任何一個(gè)函數(shù)最后生成的代碼不能超過(guò) 2K 字。一個(gè)良好的程序設(shè)計(jì)應(yīng)該有一個(gè)清晰的組織結(jié)構(gòu),把不同的功能用不同的函數(shù)實(shí)現(xiàn)是最好的方法,因此一個(gè)函數(shù) 2K 字長(zhǎng)的限制一般不會(huì)對(duì)程序代碼的編寫(xiě)產(chǎn)生太多影響。如果為實(shí)現(xiàn)特定的功能確實(shí)要連續(xù)編寫(xiě)很長(zhǎng)的程序,這時(shí)就必須把這些連續(xù)的代碼拆分成若干函數(shù),以保證每個(gè)函數(shù)最后編譯出的代碼不超過(guò)一個(gè)頁(yè)面空間。11.6.2 調(diào)用層次的控制中檔系列 PIC 單片機(jī)的硬件堆棧深度為 8 級(jí),考慮中斷響應(yīng)需占用一級(jí)堆棧,所有函數(shù)調(diào)用嵌套的最大深度不要超過(guò) 7 級(jí)。編程員必須自己控制子程序調(diào)用時(shí)的嵌套深度以符合這一限制要求。PICC 在最后編譯連接成功后可以生成一個(gè)連接定位映射文件(*.map),在此文件中有詳細(xì)的函數(shù)調(diào)用嵌套指示圖“call graph”,建議大家要留意一下。其信息大致如下(取自于一示范程序的編譯結(jié)果):Call graph:*_main size 0,0 offset 0 _RightShift_C* _Task size 0,1 offset 0 lwtoft ftmul size 0,0 offset 0 ftunpack1 ftunpack2 ftadd size 0,0 offset 0 ftunpack1 ftunpack2 ftdenorm例 11-4 C 函數(shù)調(diào)用層次圖上面所舉的信息表明整個(gè)程序在正常調(diào)用子程序時(shí)嵌套最多為兩級(jí)(沒(méi)有考慮中斷)。因?yàn)閙ain 函數(shù)不可能返回,故其不用計(jì)算在嵌套級(jí)數(shù)中。其中有些函數(shù)調(diào)用是編譯代碼時(shí)自動(dòng)加入的庫(kù)函數(shù),這些函數(shù)調(diào)用從 C 原程序中無(wú)法直接看出,但在此嵌套指示圖上則一目了然。11.6.3 函數(shù)類(lèi)型聲明PICC 在編譯時(shí)將嚴(yán)格進(jìn)行函數(shù)調(diào)用時(shí)的類(lèi)型檢查。一個(gè)良好的習(xí)慣是在編寫(xiě)程序代碼前先聲明所有用到的函數(shù)類(lèi)型。例如:void Task(void);unsigned char Temperature(void);void BIN2BCD(unsigned char);void TimeDisplay(unsigned char, unsigned char);這些類(lèi)型聲明確定了函數(shù)的入口參數(shù)和返回值類(lèi)型,這樣編譯器在編譯代碼時(shí)就能保證生成正確的機(jī)器碼。筆者在實(shí)際工作中有時(shí)碰到一些用戶(hù)聲稱(chēng)發(fā)現(xiàn) C 編譯器生成了錯(cuò)誤的代碼,最后究其原因就是因?yàn)闆](méi)有事先聲明函數(shù)類(lèi)型所致。建議大家在編寫(xiě)一個(gè)函數(shù)的原代碼時(shí),立即將此函數(shù)的類(lèi)型聲明復(fù)制到原文件的起始處,見(jiàn)例 11-1;或是復(fù)制到專(zhuān)門(mén)的包含頭文件中,再在每個(gè)原程序模塊中引用。11.6.4 中斷函數(shù)的實(shí)現(xiàn)PICC 可以實(shí)現(xiàn) C 語(yǔ)言的中斷服務(wù)程序。中斷服務(wù)程序有一個(gè)特殊的定義方法:void interrupt ISR(void); 其中的函數(shù)名“ISR”可以改成任意合法的字母或數(shù)字組合,但其入口參數(shù)和返回參數(shù)類(lèi)型必須是“void”型,亦即沒(méi)有入口參數(shù)和返回參數(shù),且中間必須有一個(gè)關(guān)鍵詞“interrupt”。中斷函數(shù)可以被放置在原程序的任意位置。因?yàn)橐延嘘P(guān)鍵詞“interrupt”聲明,PICC 在最后進(jìn)行代碼連接時(shí)會(huì)自動(dòng)將其定位到 0x0004 中斷入口處,實(shí)現(xiàn)中斷服務(wù)響應(yīng)。編譯器也會(huì)實(shí)現(xiàn)中斷函數(shù)的返回指令“retfie”。一個(gè)簡(jiǎn)單的中斷服務(wù)示范函數(shù)如下:void interrupt ISR(void) /中斷服務(wù)程序 if (T0IE & T0IF) T0IF = 0; /在此加入 TMR0 中斷服務(wù) /判 TMR0 中斷/清除 TMR0 中斷標(biāo)志 if (TMR1IE & TMR1IF) /判 TMR1 中斷 TMR1IF0; /在此加入 TMR1 中斷服務(wù) /清除 TMR1 中斷標(biāo)志/中斷結(jié)束并返回例 11-5 C 語(yǔ)言中斷函數(shù)舉例PICC 會(huì)自動(dòng)加入代碼實(shí)現(xiàn)中斷現(xiàn)場(chǎng)的保護(hù),并在中斷結(jié)束時(shí)自動(dòng)恢復(fù)現(xiàn)場(chǎng),所以編程員無(wú)需象編寫(xiě)匯編程序那樣加入中斷現(xiàn)場(chǎng)保護(hù)和恢復(fù)的額外指令語(yǔ)句。但如果在中斷服務(wù)程序中需要修改某些全局變量時(shí),是否需要保護(hù)這些變量的初值將由編程員自己決定和實(shí)施。用 C 語(yǔ)言編寫(xiě)中斷服務(wù)程序必須遵循高效的原則:&O1540; 代碼盡量簡(jiǎn)短,中斷服務(wù)強(qiáng)調(diào)的是一個(gè)“快”字。&O1540; 避免在中斷內(nèi)使用函數(shù)調(diào)用。雖然 PICC 允許在中斷里調(diào)用其它函數(shù),但為了解決遞歸調(diào)用的問(wèn)題,此函數(shù)必須為中斷服務(wù)獨(dú)家專(zhuān)用。既如此,不妨把原本要寫(xiě)在其它函數(shù)內(nèi)的代碼直接寫(xiě)在中斷服務(wù)程序中。&O1540; 避免在中斷內(nèi)進(jìn)行數(shù)學(xué)運(yùn)算。數(shù)學(xué)運(yùn)算將很有可能用到庫(kù)函數(shù)和許多中間變量,就算不出現(xiàn)遞歸調(diào)用的問(wèn)題,光在中斷入口和出口處為了保護(hù)和恢復(fù)這些中間臨時(shí)變量就需要大量的開(kāi)銷(xiāo),嚴(yán)重影響中斷服務(wù)的效率。中檔系列 PIC 單片機(jī)的中斷入口只有一個(gè),因此整個(gè)程序中只能有一個(gè)中斷服務(wù)函數(shù)。11.6.5 標(biāo)準(zhǔn)庫(kù)函數(shù)PICC 提供了較完整的 C 標(biāo)準(zhǔn)庫(kù)函數(shù)支持,其中包括數(shù)學(xué)運(yùn)算函數(shù)和字符串操作函數(shù)。在程序中使用這些現(xiàn)成的庫(kù)函數(shù)時(shí)需要注意的是入口參數(shù)必須在 bank0 中。如果需要用到數(shù)學(xué)函數(shù),則應(yīng)在程序前 “#include ” 包含頭文件;如果要使用字符串操作函數(shù),就需要包含“#include ”頭文件。在這些頭文件中提供了函數(shù)類(lèi)型的聲明。通過(guò)直接查看這些頭文件就可以知道 PICC 提供了哪些標(biāo)準(zhǔn)庫(kù)函數(shù)。C 語(yǔ)言中常用的格式化打印函數(shù)“printf/sprintf”用在單片機(jī)的程序中時(shí)要特別謹(jǐn)慎。printf/sprintf 是一個(gè)非常大的函數(shù),一旦使用,你的程序代碼長(zhǎng)度就會(huì)增加很多。除非是在編寫(xiě)試驗(yàn)性質(zhì)的代碼,可以考慮使用格式化打印函數(shù)以簡(jiǎn)化測(cè)試程序;一般的最終產(chǎn)品設(shè)計(jì)都是自己編寫(xiě)最精簡(jiǎn)的代碼實(shí)現(xiàn)特定格式的數(shù)據(jù)顯示和輸出。本來(lái),在單片機(jī)應(yīng)用中輸出的數(shù)據(jù)格式都相對(duì)簡(jiǎn)單而且固定,實(shí)現(xiàn)起來(lái)應(yīng)該很容易。對(duì)于標(biāo)準(zhǔn) C 語(yǔ)言的控制臺(tái)輸入(scanf)輸出(printf)函數(shù),PICC 需要用戶(hù)自己編寫(xiě)其底層函數(shù) getch()和 putch()。在單片機(jī)系統(tǒng)中實(shí)現(xiàn) scanf/printf 本來(lái)就沒(méi)什么太多意義,如果一定要實(shí)現(xiàn),只要編寫(xiě)好特定的 getch()和 putch()函數(shù),你就可以通過(guò)任何接口輸入或輸出格式化的數(shù)據(jù)。11.7PICC 定義特殊區(qū)域值PICC 提供了相關(guān)的預(yù)處理指令以實(shí)現(xiàn)在原程序中定義單片機(jī)的配置字和標(biāo)記單元。11.7.1 定義工作配置字在原程序中定義 PIC 單片機(jī)工作配置字的重要性在前面章節(jié)中已經(jīng)闡述。在用 PICC 寫(xiě)程序時(shí)同樣可以在 C 原程序中定義,具體方式如下:_CONFIG (HS & UNPROTECT & PWRTEN & BORDIS & WDTEN); 上面的關(guān)鍵詞“_CONFIG”(注意前面有兩個(gè)下劃線符)專(zhuān)門(mén)用于是芯片配置字的設(shè)定,后面括號(hào)中的各項(xiàng)配置位符號(hào)在特定型號(hào)單片機(jī)的頭文件中已經(jīng)定義(注意不是 pic.h頭文件),相互之間用邏輯“與”操作符組合在一起。這樣定義的配置字信息最后將和程序代碼一起放入同一個(gè) HEX 文件。在這里列出了適用于 16F7x 系列單片機(jī)配置位符號(hào)預(yù)定義,其它型號(hào)或系列的單片機(jī)配置字定義方式類(lèi)似,使用前查閱一下對(duì)應(yīng)的頭文件即可。/*振蕩器配置*/ #define RC #define HS 0x3FFF / RC 振蕩0x3FFE / HS 模式#define XT #define LP /*看門(mén)狗配置*/ 0x3FFD / XT 模式0x3FFC / LP 模式#define WDTEN 0x3FFF / 看門(mén)狗打開(kāi)#define WDTDIS/*上電延時(shí)定時(shí)器配置*/#define PWRTEN0x3FFB / 看門(mén)狗關(guān)閉0x3FF7 / 上電延時(shí)定時(shí)器打開(kāi)#define PWRTDIS 0x3FFF / 上電延時(shí)定時(shí)器關(guān)閉/*低電壓復(fù)位配置*/#define BOREN 0x3FFF / 低電壓復(fù)位允許#define BORDIS/*代碼保護(hù)配置*/ 0x3FBF / 低電壓復(fù)位禁止#define UNPROTECT 0x3FFF / 沒(méi)有代碼保護(hù)#define PROTECT 0x3FEF / 程序代碼保護(hù)例 11-6 頭文件預(yù)定義的配置信息符號(hào)11.7.2 定義芯片標(biāo)記單元PIC 單片機(jī)中的標(biāo)記單元定義可以用下面的_IDLOC(注意前面有兩個(gè)下劃線符)預(yù)處理指令實(shí)現(xiàn),方法如下:_IDLOC (1234);其特殊之處是括號(hào)內(nèi)的值全部為 16 進(jìn)制數(shù),不需要用“0x”引導(dǎo)。這樣上面的定義就設(shè)定了標(biāo)記單元內(nèi)容為 01020304。11.8MPLAB-IDE 中實(shí)現(xiàn) PICC 的編譯選項(xiàng)設(shè)置在 11.3 節(jié)中已經(jīng)介紹了如何實(shí)現(xiàn) PICC 和 MPLAB-IDE 開(kāi)發(fā)平臺(tái)的掛接。一旦項(xiàng)目建立成功、程序編寫(xiě)完成后即可以通過(guò) MPLAB 環(huán)境下的項(xiàng)目管理工具實(shí)現(xiàn)程序的編譯、連接和調(diào)試。它們的含義分別是:項(xiàng)目維護(hù)(Make):MPLAB 檢查項(xiàng)目中的原程序文件,只編譯那些在上次編譯后又被修改過(guò)的原程序,最后進(jìn)行連接;項(xiàng)目重建(Build All):項(xiàng)目中的所有原程序文件,不管是否有修改,都將被重新編譯一次,最后進(jìn)行連接。也可以通過(guò) Project 菜單選擇“Make”或“Build All”實(shí)現(xiàn)項(xiàng)目編譯。不管采用何種方式,在啟動(dòng)編譯過(guò)程前一般都要設(shè)定一些編譯選項(xiàng)。11.8.1 選擇單片機(jī)型號(hào)在選擇 PICC 作為語(yǔ)言工具并建立了項(xiàng)目后,同樣通過(guò)菜單項(xiàng) Configure&O1616;Select Device在 MPLAB 環(huán)境中選擇具體單片機(jī)型號(hào)。請(qǐng)回顧一下例 11-1 的代碼,我們?cè)谠绦蛞婚_(kāi)始使用了“#include ”實(shí)現(xiàn)了相關(guān)單片機(jī)的一些預(yù)定義符號(hào)的直接引用,但沒(méi)有具體指明是哪一個(gè)型號(hào)。實(shí)際上,“pic.h”頭文件只是一個(gè)簡(jiǎn)單的管理工具(條件判別),它會(huì)按照MPLAB 所選擇的特定型號(hào)的單片機(jī),把真正對(duì)應(yīng)的頭文件包含進(jìn)來(lái)。有興趣者可以直接用文本編輯工具打開(kāi) pic.h 文件查看其是如何根據(jù)不同的單片機(jī)型號(hào)包含對(duì)應(yīng)的頭文件。這樣對(duì)編程員而言,程序中只需

溫馨提示

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

最新文檔

評(píng)論

0/150

提交評(píng)論