<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > PIC 單片機(jī) C 語言編程簡(jiǎn)介(2)

          PIC 單片機(jī) C 語言編程簡(jiǎn)介(2)

          作者: 時(shí)間:2016-11-22 來源:網(wǎng)絡(luò) 收藏
          11.5.7 PICC 中變量的絕對(duì)定位

          本文引用地址:http://www.ex-cimer.com/article/201611/319596.htm

          首先必須強(qiáng)調(diào),在用 C 語言寫程序時(shí)變量一般由編譯器和連接器最后定位,在寫程序

          之時(shí)無需知道所定義的變量具體被放在哪個(gè)地址(除了 bank 必須聲明)。

          真正需要絕對(duì)定位的只是單片機(jī)中的那些特殊功能寄存器,而這些寄存器的地址定位在

          PICC 編譯環(huán)境所提供的頭文件中已經(jīng)實(shí)現(xiàn),無需用戶操心。編程員所要了解的也就是 PICC

          是如何定義這些特殊功能寄存器和其中的相關(guān)控制位的名稱。好在 PICC 的定義標(biāo)準(zhǔn)基本上

          按照芯片的數(shù)據(jù)手冊(cè)中的名稱描述進(jìn)行,這樣就秉承了變量命名的一貫性。一個(gè)變量絕對(duì)定

          位的例子如下:

          unsigned char tmpData @ 0x20; //tmpData 定位在地址 0x20

          千萬注意,PICC 對(duì)絕對(duì)定位的變量不保留地址空間。換句話說,上面變量 tmpData 的

          地址是 0x20,但最后 0x20 處完全有可能又被分配給了其它變量使用,這樣就發(fā)生了地址沖

          突。因此針對(duì)變量的絕對(duì)定位要特別小心。從筆者的應(yīng)用經(jīng)驗(yàn)看,在一般的程序設(shè)計(jì)中用戶

          自定義的變量實(shí)在是沒有絕對(duì)定位的必要。

          如果需要,位變量也可以絕對(duì)定位。但必須遵循上面介紹的位變量編址的方式。如果一

          個(gè)普通變量已經(jīng)被絕對(duì)定位,那么此變量中的每個(gè)數(shù)據(jù)位就可以用下面的計(jì)算方式實(shí)現(xiàn)位變

          量指派:

          unsigned char tmpData @ 0x20; //tmpData 定位在地址 0x20

          bit 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 事先沒有被絕對(duì)定位,那就不能用上面的位變量定位方式。

          11.5.8 PICC 的其它變量修飾關(guān)鍵詞

          &O1540; extern 外部變量聲明

          如果在一個(gè) C 程序文件中要使用一些變量但其原型定義寫在另外的文件中,那么在本

          文件中必須將這些變量聲明成“extern”外部類型。例如程序文件 code1.c 中有如下定義:


          bank1 unsigned char var1, var2;


          //定義了 bank1 中的兩個(gè)變量


          在另外一個(gè)程序文件 code2.c 中要對(duì)上面定義的變量進(jìn)行操作,則必須在程序的開頭定義:

          extern bank1 unsigned char var1, var2; //聲明位于 bank1 的外部變量

          &O1540; volatile — 易變型變量聲明

          PICC 中還有一個(gè)變量修飾詞在普通的 C 語言介紹中一般是看不到的,這就是關(guān)鍵詞

          “volatile”。顧名思義,它說明了一個(gè)變量的值是會(huì)隨機(jī)變化的,即使程序沒有刻意對(duì)它進(jìn)

          行任何賦值操作。在單片機(jī)中,作為輸入的 IO 端口其內(nèi)容將是隨意變化的;在中斷內(nèi)被修

          改的變量相對(duì)主程序流程來講也是隨意變化的;很多特殊功能寄存器的值也將隨著指令的運(yùn)

          行而動(dòng)態(tài)改變。所有這種類型的變量必須將它們明確定義成“volatile”類型,例如:

          volatile unsigned char STATUS @ 0x03;

          volatile bit commFlag;

          “volatile”類型定義在單片機(jī)的 C 語言編程中是如此的重要,是因?yàn)樗梢愿嬖V編譯

          器的優(yōu)化處理器這些變量是實(shí)實(shí)在在存在的,在優(yōu)化過程中不能無故消除。假定你的程序定

          義了一個(gè)變量并對(duì)其作了一次賦值,但隨后就再也沒有對(duì)其進(jìn)行任何讀寫操作,如果是非

          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ì)出問題。只要將其

          定義成 volatile 類型后,編譯后的代碼就可以保證每次操作時(shí)直接從變量地址處取數(shù)。

          &O1540; const — 常數(shù)型變量聲明

          如果變量定義前冠以“const”類型修飾,那么所有這些變量就成為常數(shù),程序運(yùn)行過

          程中不能對(duì)其修改。除了位變量,其它所有基本類型的變量或高級(jí)組合變量都將被存放在程

          序空間(ROM 區(qū))以節(jié)約數(shù)據(jù)存儲(chǔ)空間。顯然,被定義在 ROM 區(qū)的變量是不能再在程序

          中對(duì)其進(jìn)行賦值修改的,這也是“const”的本來意義。實(shí)際上這些數(shù)據(jù)最終都將以“retlw”

          的指令形式存放在程序空間,但 PICC 會(huì)自動(dòng)編譯生成相關(guān)的附加代碼從程序空間讀取這些

          常數(shù),編程員無需太多操心。例如:

          const unsigned char name[]=”This is a demo”; //定義一個(gè)常量字符串

          如果定義了 “const”類型的位變量,那么這些位變量還是被放置在 RAM 中,但程序

          不能對(duì)其賦值修改。本來,不能修改的位變量沒有什么太多的實(shí)際意義,相信大家在實(shí)際編

          程時(shí)不會(huì)大量用到。

          &O1540; persistent 非初始化變量聲明

          按照標(biāo)準(zhǔn) C 語言的做法,程序在開始運(yùn)行前首先要把所有定義的但沒有預(yù)置初值的變

          量全部清零。PICC 會(huì)在最后生成的機(jī)器碼中加入一小段初始化代碼來實(shí)現(xiàn)這一變量清零操

          作,且這一操作將在 main 函數(shù)被調(diào)用之前執(zhí)行。問題是作為一個(gè)單片機(jī)的控制系統(tǒng)有很多

          變量是不允許在程序復(fù)位后被清零的。為了達(dá)到這一目的,PICC 提供了“persistent”修飾

          詞以聲明此類變量無需在復(fù)位時(shí)自動(dòng)清零,編程員應(yīng)該自己決定程序中的那些變量是必須聲

          明成“persisten”類型,而且須自己判斷什么時(shí)候需要對(duì)其進(jìn)行初始化賦值。例如:

          persistent unsigned char hour,minute,second; //定義時(shí)分秒變量

          經(jīng)常用到的是如果程序經(jīng)上電復(fù)位后開始運(yùn)行,那么需要將 persistent 型的變量初始化,

          如果是其它形式的復(fù)位,例如看門狗引發(fā)的復(fù)位,則無需對(duì) persistent 型變量作任何修改。

          PIC 單片機(jī)內(nèi)提供了各種復(fù)位的判別標(biāo)志,用戶程序可依具體設(shè)計(jì)靈活處理不同的復(fù)位情

          形。

          11.5.9 PICC 中的指針

          PICC 中指針的基本概念和標(biāo)準(zhǔn) C 語法沒有太多的差別。但是在 PIC 單片機(jī)這一特定的

          架構(gòu)上,指針的定義方式還是有幾點(diǎn)需要特別注意。

          &O1540; 指向 RAM 的指針

          如果是匯編語言編程,實(shí)現(xiàn)指針尋址的方法肯定就是用 FSR 寄存器,PICC 也不例外。

          為了生成高效的代碼,PICC 在編譯 C 原程序時(shí)將指向 RAM 的指針操作最終用 FSR 來實(shí)現(xiàn)

          間接尋址。這樣就勢(shì)必產(chǎn)生一個(gè)問題:FSR 能夠直接連續(xù)尋址的范圍是 256 字節(jié)(bank0/1

          或 bank2/3),要覆蓋最大 512 字節(jié)的內(nèi)部數(shù)據(jù)存儲(chǔ)空間,又該如何讓定義指針?PICC 還是

          將這一問題留給編程員自己解決:在定義指針時(shí)必須明確指定該指針?biāo)m用的尋址區(qū)域,例

          如:

          unsigned char *ptr0; //①定義覆蓋 bank0/1 的指針

          bank2 unsigned char *ptr1; //②定義覆蓋 bank2/3 的指針

          bank3 unsigned char *ptr2; //③定義覆蓋 bank2/3 的指針

          上面定義了三個(gè)指針變量,其中①指針沒有任何 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)類型匹配,

          下面的指針賦值將產(chǎn)生一個(gè)致命錯(cuò)誤:


          unsigned char *ptr0;

          bank2 unsigned char buff[8];

          程序語句:


          //定義指向 bank0/1 的指針

          //定義 bank2 中的一個(gè)緩沖區(qū)


          ptr0 = buff; //錯(cuò)誤!試圖將 bank2 內(nèi)的變量地址賦給指向 bank0/1 的指針

          若出現(xiàn)此類錯(cuò)誤的指針操作,PICC 在最后連接時(shí)會(huì)告知類似于下面的信息:

          Fixup overflow in expression_r(...)

          同樣的道理,若函數(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ú)寫一個(gè)函數(shù):

          void SendMessage_2(bank2 unsigned char *);

          這兩個(gè)函數(shù)從內(nèi)部代碼的實(shí)現(xiàn)來看可以一模一樣,但傳遞的參數(shù)類型不同。

          按筆者的應(yīng)用經(jīng)驗(yàn)體會(huì),如果你看到了“Fixup overflow”的錯(cuò)誤指示,幾乎可以肯定

          是指針類型不匹配的賦值所至。請(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 的指針


          反過來,下面的操作將是一個(gè)錯(cuò)誤,因?yàn)樵撝羔樦赶虻氖浅?shù)型變量,不能賦值。

          *romPtr = data; //往指針指向的地址寫一個(gè)數(shù)

          &O1540; 指向函數(shù)的指針

          單片機(jī)編程時(shí)函數(shù)指針的應(yīng)用相對(duì)較少,但作為標(biāo)準(zhǔn) C 語法的一部分,PICC 同樣支持

          函數(shù)指針調(diào)用。如果你對(duì)編譯原理有一定的了解,就應(yīng)該明白在 PIC 單片機(jī)這一特定的架

          構(gòu)上實(shí)現(xiàn)函數(shù)指針調(diào)用的效率是不高的:PICC 將在 RAM 中建立一個(gè)調(diào)用返回表,真正的

          調(diào)用和返回過程是靠直接修改 PC 指針來實(shí)現(xiàn)的。因此,除非特殊算法的需要,建議大家盡

          量不要使用函數(shù)指針。

          &O1540; 指針的類型修飾

          前面介紹的指針定義都是最基本的形式。和普通變量一樣,指針定義也可以在前面加上

          特殊類型的修飾關(guān)鍵詞,例如“persistent”、“volatile”等??紤]指針本身還要限定其作用域,

          因此 PICC 中的指針定義初看起來顯得有點(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)柑幍淖兞?;出現(xiàn)在后面的修飾詞其作用對(duì)象

          就是指針變量自己。

          11.6


          PICC 中的子程序和函數(shù)

          中檔系列的 PIC 單片機(jī)程序空間有分頁的概念,但用 C 語言編程時(shí)基本不用太多關(guān)心


          代碼的分頁問題。因?yàn)樗泻瘮?shù)或子程序調(diào)用時(shí)的頁面設(shè)定(如果代碼超過一個(gè)頁面)都由

          編譯器自動(dòng)生成的指令實(shí)現(xiàn)。

          11.6.1 函數(shù)的代碼長(zhǎng)度限制

          PICC 決定了 C 原程序中的一個(gè)函數(shù)經(jīng)編譯后生成的機(jī)器碼一定會(huì)放在同一個(gè)程序頁面

          內(nèi)。中檔系列的 PIC 單片機(jī)其一個(gè)程序頁面的長(zhǎng)度是 2K 字,換句話說,用 C 語言編寫的任

          何一個(gè)函數(shù)最后生成的代碼不能超過 2K 字。一個(gè)良好的程序設(shè)計(jì)應(yīng)該有一個(gè)清晰的組織結(jié)

          構(gòu),把不同的功能用不同的函數(shù)實(shí)現(xiàn)是最好的方法,因此一個(gè)函數(shù) 2K 字長(zhǎng)的限制一般不會(huì)

          對(duì)程序代碼的編寫產(chǎn)生太多影響。如果為實(shí)現(xiàn)特定的功能確實(shí)要連續(xù)編寫很長(zhǎng)的程序,這時(shí)

          就必須把這些連續(xù)的代碼拆分成若干函數(shù),以保證每個(gè)函數(shù)最后編譯出的代碼不超過一個(gè)頁

          面空間。

          11.6.2 調(diào)用層次的控制

          中檔系列 PIC 單片機(jī)的硬件堆棧深度為 8 級(jí),考慮中斷響應(yīng)需占用一級(jí)堆棧,所

          有函數(shù)調(diào)用嵌套的最大深度不要超過 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í)(沒有考慮中斷)。因?yàn)?/p>

          main 函數(shù)不可能返回,故其不用計(jì)算在嵌套級(jí)數(shù)中。其中有些函數(shù)調(diào)用是編譯代碼時(shí)自動(dòng)

          加入的庫函數(shù),這些函數(shù)調(diào)用從 C 原程序中無法直接看出,但在此嵌套指示圖上則一目了

          然。

          11.6.3 函數(shù)類型聲明

          PICC 在編譯時(shí)將嚴(yán)格進(jìn)行函數(shù)調(diào)用時(shí)的類型檢查。一個(gè)良好的習(xí)慣是在編寫程序代碼

          前先聲明所有用到的函數(shù)類型。例如:

          void Task(void);

          unsigned char Temperature(void);

          void BIN2BCD(unsigned char);

          void TimeDisplay(unsigned char, unsigned char);

          這些類型聲明確定了函數(shù)的入口參數(shù)和返回值類型,這樣編譯器在編譯代碼時(shí)就能保證生成

          正確的機(jī)器碼。筆者在實(shí)際工作中有時(shí)碰到一些用戶聲稱發(fā)現(xiàn) C 編譯器生成了錯(cuò)誤的代碼,

          最后究其原因就是因?yàn)闆]有事先聲明函數(shù)類型所致。

          建議大家在編寫一個(gè)函數(shù)的原代碼時(shí),立即將此函數(shù)的類型聲明復(fù)制到原文件的起始

          處,見例 11-1;或是復(fù)制到專門的包含頭文件中,再在每個(gè)原程序模塊中引用。

          11.6.4 中斷函數(shù)的實(shí)現(xiàn)

          PICC 可以實(shí)現(xiàn) C 語言的中斷服務(wù)程序。中斷服務(wù)程序有一個(gè)特殊的定義方法:

          void interrupt ISR(void);

          其中的函數(shù)名“ISR”可以改成任意合法的字母或數(shù)字組合,但其入口參數(shù)和返回參數(shù)類型

          必須是“void”型,亦即沒有入口參數(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é)束并返回




          關(guān)鍵詞: PIC單片機(jī)C語言編

          評(píng)論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();