<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

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

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

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

本文引用地址:http://dyxdggzs.com/article/201611/319596.htm

首先必須強調,在用 C 語(yǔ)言寫(xiě)程序時(shí)變量一般由編譯器和連接器最后定位,在寫(xiě)程序

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

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

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

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

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

位的例子如下:

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

千萬(wàn)注意,PICC 對絕對定位的變量不保留地址空間。換句話(huà)說(shuō),上面變量 tmpData 的

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

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

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

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

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

量指派:

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

bit tmpBit0 @ tmpData*8+0; //tmpBit0 對應于 tmpData 第 0 位

bit tmpBit1 @ tmpData*8+1; //tmpBit0 對應于 tmpData 第 1 位

bit tmpBit2 @ tmpData*8+2; //tmpBit0 對應于 tmpData 第 2 位

如果 tmpData 事先沒(méi)有被絕對定位,那就不能用上面的位變量定位方式。

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 中要對上面定義的變量進(jìn)行操作,則必須在程序的開(kāi)頭定義:

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

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

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

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

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

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

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

volatile unsigned char STATUS @ 0x03;

volatile bit commFlag;

“volatile”類(lèi)型定義在單片機的 C 語(yǔ)言編程中是如此的重要,是因為它可以告訴編譯

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

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

volatile 型變量,優(yōu)化后的結果是這個(gè)變量將有可能被徹底刪除以節約存儲空間。另外一種

情形是在使用某一個(gè)變量進(jìn)行連續的運算操作時(shí),這個(gè)變量的值將在第一次操作時(shí)被復制到

中間臨時(shí)變量中,如果它是非 volatile 型變量,則緊接其后的其它操作將有可能直接從臨時(shí)

變量中取數以提高運行效率,顯然這樣做后對于那些隨機變化的參數就會(huì )出問(wèn)題。只要將其

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

&O1540; const — 常數型變量聲明

如果變量定義前冠以“const”類(lèi)型修飾,那么所有這些變量就成為常數,程序運行過(guò)

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

序空間(ROM 區)以節約數據存儲空間。顯然,被定義在 ROM 區的變量是不能再在程序

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

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

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

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

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

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

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

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

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

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

作,且這一操作將在 main 函數被調用之前執行。問(wèn)題是作為一個(gè)單片機的控制系統有很多

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

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

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

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

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

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

PIC 單片機內提供了各種復位的判別標志,用戶(hù)程序可依具體設計靈活處理不同的復位情

形。

11.5.9 PICC 中的指針

PICC 中指針的基本概念和標準 C 語(yǔ)法沒(méi)有太多的差別。但是在 PIC 單片機這一特定的

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

&O1540; 指向 RAM 的指針

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

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

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

或 bank2/3),要覆蓋最大 512 字節的內部數據存儲空間,又該如何讓定義指針?PICC 還是

將這一問(wèn)題留給編程員自己解決:在定義指針時(shí)必須明確指定該指針所適用的尋址區域,例

如:

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í)際上兩者是一樣的,因為一個(gè)指針可

以同時(shí)覆蓋兩個(gè) bank 的存儲區域。另外,上面三個(gè)指針變量自身都存放在 bank0 中。我們

將在稍后介紹如何在其它 bank 中存放指針變量。

既然定義的指針有明確的 bank 適用區域,在對指針變量賦值時(shí)就必須實(shí)現類(lèi)型匹配,

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


unsigned char *ptr0;

bank2 unsigned char buff[8];

程序語(yǔ)句:


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

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


ptr0 = buff; //錯誤!試圖將 bank2 內的變量地址賦給指向 bank0/1 的指針

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

Fixup overflow in expression_r(...)

同樣的道理,若函數調用時(shí)用了指針作為傳遞參數,也必須注意 bank 作用域的匹配,

而這點(diǎn)往往容易被忽視。假定有下面的函數實(shí)現發(fā)送一個(gè)字符串的功能:

void SendMessage(unsigned char *);

那么被發(fā)送的字符串必須位于 bank0 或 bank1 中。如果你還要發(fā)送位于 bank2 或 bank3 內的

字符串,必須再另外單獨寫(xiě)一個(gè)函數:

void SendMessage_2(bank2 unsigned char *);

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

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

是指針類(lèi)型不匹配的賦值所至。請重點(diǎn)檢查程序中有關(guān)指針的操作。

&O1540; 指向 ROM 常數的指針

如果一組變量是已經(jīng)被定義在 ROM 區的常數,那么指向它的指針可以這樣定義:


const unsigned char company[]=”Microchip”;

const unsigned char *romPtr;

程序中可以對上面的指針變量賦值和實(shí)現取數操作:

romPtr = company; //指針賦初值

data = *romPtr++; //取指針指向的一個(gè)數,然后指針加 1


//定義 ROM 中的常數

//定義指向 ROM 的指針


反過(guò)來(lái),下面的操作將是一個(gè)錯誤,因為該指針指向的是常數型變量,不能賦值。

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

&O1540; 指向函數的指針

單片機編程時(shí)函數指針的應用相對較少,但作為標準 C 語(yǔ)法的一部分,PICC 同樣支持

函數指針調用。如果你對編譯原理有一定的了解,就應該明白在 PIC 單片機這一特定的架

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

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

量不要使用函數指針。

&O1540; 指針的類(lèi)型修飾

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

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

因此 PICC 中的指針定義初看起來(lái)顯得有點(diǎn)復雜,但只要了解各部分的具體含義,理解一個(gè)

指針的實(shí)際用圖就變得很直接。

㈠ bank 修飾詞的位置含義

前面介紹的一些指針有的作用于 bank0/1,有的作用于 bank2/3,但它們本身的存放位置

全部在 bank0。顯然,在一個(gè)程序設計中指針變量將有可能被定位在任何可用的地址空間,

這時(shí),bank 修飾詞出現的位置就是一個(gè)關(guān)鍵,看下面的例子:

//定義指向 bank0/1 的指針,指針變量為于 bank0 中

unsigned char *ptr0;

//定義指向 bank2/3 的指針,指針變量為于 bank0 中

bank2 unsigned char *ptr0;

//定義指向 bank2/3 的指針,指針變量為于 bank1 中

bank2 unsigned char * bank1 ptr0;

從中可以看出規律:前面的 bank 修飾詞指明了此指針的作用域;后面的 bank 修飾詞定義了

此指針變量自身的存放位置。只要掌握了這一法則,你就可以定義任何作用域的指針且可以

將指針變量放于任何 bank 中。

㈡ volatile、persistent 和 const 修飾詞的位置含義

如果能理解上面介紹的 bank 修飾詞的位置含義,實(shí)際上 volatile、persistent 和 const 這

些關(guān)鍵詞出現在前后不同位置上的含義規律是和 bank 一詞相一致的。例如:

//定義指向 bank0/1 易變型字符變量的指針,指針變量位于 bank0 中且自身為非易變型

volatile unsigned char *ptr0;

//定義指向 bank2/3 非易變型字符變量的指針,指針變量位于 bank1 中且自身為易變型

bank2 unsigned char * volatile bank1 ptr0;

//定義指向 ROM 區的指針,指針變量本身也是存放于 ROM 區的常數

const unsigned char * const ptr0;

亦即出現在前面的修飾詞其作用對象是指針所指處的變量;出現在后面的修飾詞其作用對象

就是指針變量自己。

11.6


PICC 中的子程序和函數

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


代碼的分頁(yè)問(wèn)題。因為所有函數或子程序調用時(shí)的頁(yè)面設定(如果代碼超過(guò)一個(gè)頁(yè)面)都由

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

11.6.1 函數的代碼長(cháng)度限制

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

內。中檔系列的 PIC 單片機其一個(gè)程序頁(yè)面的長(cháng)度是 2K 字,換句話(huà)說(shuō),用 C 語(yǔ)言編寫(xiě)的任

何一個(gè)函數最后生成的代碼不能超過(guò) 2K 字。一個(gè)良好的程序設計應該有一個(gè)清晰的組織結

構,把不同的功能用不同的函數實(shí)現是最好的方法,因此一個(gè)函數 2K 字長(cháng)的限制一般不會(huì )

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

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

面空間。

11.6.2 調用層次的控制

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

有函數調用嵌套的最大深度不要超過(guò) 7 級。編程員必須自己控制子程序調用時(shí)的嵌套深

度以符合這一限制要求。

PICC 在最后編譯連接成功后可以生成一個(gè)連接定位映射文件(*.map),在此文件

中有詳細的函數調用嵌套指示圖“call graph”,建議大家要留意一下。其信息大致如下

(取自于一示范程序的編譯結果):

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 函數調用層次圖

上面所舉的信息表明整個(gè)程序在正常調用子程序時(shí)嵌套最多為兩級(沒(méi)有考慮中斷)。因為

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

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

然。

11.6.3 函數類(lèi)型聲明

PICC 在編譯時(shí)將嚴格進(jìn)行函數調用時(shí)的類(lèi)型檢查。一個(gè)良好的習慣是在編寫(xiě)程序代碼

前先聲明所有用到的函數類(lèi)型。例如:

void Task(void);

unsigned char Temperature(void);

void BIN2BCD(unsigned char);

void TimeDisplay(unsigned char, unsigned char);

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

正確的機器碼。筆者在實(shí)際工作中有時(shí)碰到一些用戶(hù)聲稱(chēng)發(fā)現 C 編譯器生成了錯誤的代碼,

最后究其原因就是因為沒(méi)有事先聲明函數類(lèi)型所致。

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

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

11.6.4 中斷函數的實(shí)現

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

void interrupt ISR(void);

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

必須是“void”型,亦即沒(méi)有入口參數和返回參數,且中間必須有一個(gè)關(guān)鍵詞“interrupt”。

中斷函數可以被放置在原程序的任意位置。因為已有關(guān)鍵詞“interrupt”聲明,PICC 在

最后進(jìn)行代碼連接時(shí)會(huì )自動(dòng)將其定位到 0x0004 中斷入口處,實(shí)現中斷服務(wù)響應。編譯器也

會(huì )實(shí)現中斷函數的返回指令“retfie”。一個(gè)簡(jiǎn)單的中斷服務(wù)示范函數如下:

void interrupt ISR(void) //中斷服務(wù)程序

{


if (T0IE && T0IF)

{

T0IF = 0;

//在此加入 TMR0 中斷服務(wù)

}


//判 TMR0 中斷

//清除 TMR0 中斷標志


if (TMR1IE && TMR1IF) //判 TMR1 中斷

{


TMR1IF0;

//在此加入 TMR1 中斷服務(wù)

}

}


//清除 TMR1 中斷標志

//中斷結束并返回




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

評論


技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>