天天看點

《嵌入式C程式設計:PIC單片機和C程式設計技術與應用》一3.3 非标準編譯訓示

3.3.1 #warning

它和#error相似,但隻抛出警告資訊,不會停止編譯。

3.3.2 #use delay

該指令告訴編譯器,硬體使用的是什麼類型的晶振。編譯器根據它為晶片設定正确的熔絲位,同時還會生成一些控制時間的内置函數。我們已經見過這類函數了,如delay_ms(time),其中的#use delay在檔案e3.h中。這裡給出了一些例子:

《嵌入式C程式設計:PIC單片機和C程式設計技術與應用》一3.3 非标準編譯訓示

ccs c編譯器的#use...指令驅使編譯器在編譯階段根據程式員的指定選項生成新的内置函數。這些#use庫包括rs-232、i2c、觸摸闆等,具體細節将會在本書後面部分介紹。

對于大多數非嵌入式的傳統c編譯器來說,通常都将像delay_ms這樣的通用函數封裝成函數庫。但是由于pic mcu的記憶體比較緊張,在編譯時生成這些函數會更有效率,這樣隻有在程式員真正需要的時候才會被包含進來。

該指令對于整個程式都非常重要,因為有時編譯器為了滿足特定器件接口的需求,需要加入延時。例如,pic的寄存器在設定和讀取一個比特之間需要稍作延時。編譯器從該指令中擷取到時鐘資訊,進而知道如何實作延時。

有些晶片有雙晶振以及其他的時鐘特性。該指令能夠處理所有器件的各種選項。

3.3.3 關于頻率

頻率的機關是赫茲(hertz),簡寫為hz。1hz表示每秒鐘發生一次;2hz表示每秒鐘重複出現兩次。下面給出了頻率的常用機關:

hz ???每秒1次

khz ??每秒1000次

mhz 每秒1?000?000次

ghz ??每秒1?000?000?000次

3.3.4 #use rs232(options)

該指令會建立出一組用于接收和發送序列槽異步資料的函數,有很多選項。下面給出個簡單的例子:

《嵌入式C程式設計:PIC單片機和C程式設計技術與應用》一3.3 非标準編譯訓示

該指令會生成putc()和getc(),分别用來從c6、c7引腳以9600 baud的波特率傳輸和接收資料。如果使用的是pic的uart引腳,那麼通信由硬體完成。否則,編譯器會建立一個函數來實作bit-bang模式。第22章将會詳細介紹異步通信和#use rs232指令。

3.3.5 #fuses options

options會根據硬體不同而變化,device.h檔案中列出了相關硬體的選項。

pic微控制器有一塊非易失性記憶體用來存放配置資訊。這塊區域稱為熔絲位或配置位,可以設定晶振類型,開關各種硬體特性,有的還可以設定某些引腳的功能。在下載下傳程式到晶片上時,可以修改熔絲位。隻有重新下載下傳程式才能改變熔絲位的設定。不同晶片的設定内容不同。在ide的view>fuses中,可以找到相應晶片支援哪些設定。使用#use delay指令,ccs c編譯器會自動設定所需的晶振熔絲位。下面是所有晶片通用的兩個常用熔絲位:

《嵌入式C程式設計:PIC單片機和C程式設計技術與應用》一3.3 非标準編譯訓示

在程式寫入晶片之後,該熔絲位用來防止從晶片記憶體中讀取程式。通常用于産品中,用來防止終端客戶從晶片中複制程式。

《嵌入式C程式設計:PIC單片機和C程式設計技術與應用》一3.3 非标準編譯訓示

該熔絲位用于激活晶片的看門狗定時器(watchdog timer)。在該定時器沒有達到預設值時,晶片正常工作。一旦定時器到期,晶片将會被複位。正常情況下,程式将會周期性地重置定時器(複位到起始狀态)。隻要定時器不停地被重置,程式就會一直運作下去。如果程式卡在某個地方,看門狗定時器得不到重置,就會在到期之後重新開機程式。函數setup_wdt()用來設定定時器時間,restart_wdt()用來重置定時器。

3.3.6 #locate id = address

id 一個有效的c辨別符

address pic微控制器資料記憶體中的位址

通常情況下,定義一個c變量之後,編譯器在ram中尋找一個空位置來存放該變量。該指令能夠強制将變量放置到記憶體中的某個特殊位置。我們可以使用它将c變量指向pic的特定功能寄存器。例如,e3闆子上的pic控制器的狀态寄存器位于ram中的4056位置。下面這段代碼建立了一個變量指向該位置:

《嵌入式C程式設計:PIC單片機和C程式設計技術與應用》一3.3 非标準編譯訓示

那麼,後面的這行代碼就可以将狀态寄存器清零:

《嵌入式C程式設計:PIC單片機和C程式設計技術與應用》一3.3 非标準編譯訓示

ccs c編譯器中的getenv()宏可以用于擷取pic寄存器的位置。下面這條指令和前面的#locate功能一樣,并且可以用于所有的晶片:

《嵌入式C程式設計:PIC單片機和C程式設計技術與應用》一3.3 非标準編譯訓示

雖然c語言的ansi标準中不允許這麼聲明,但是c語言還有個嵌入式擴充。在該擴充标準中,對于嵌入式程式,我們将其稱為named register(有名寄存器)。ccs c編譯器支援該擴充,而且,很少有編譯器支援它。下面是使用named register的另外一種方法:

《嵌入式C程式設計:PIC單片機和C程式設計技術與應用》一3.3 非标準編譯訓示

下劃線前的名字(register)就是寄存器的位址。

3.3.7 #byte id=x和#word id=x

id 一個有效的c辨別符

x  一個c變量或常量

該指令功能和#locate一樣,但除了定位變量,它還聲明了一個c變量。#byte用于聲明8位的變量,#word則是16位。下面這行指令和前面的#locate功能一樣:

3.3.8 #bit id=x.y

id 一個有效的c辨別符

x 一個c變量或常量

y 可以是0~7中的任意一個數

該指令功能和#byte類似,但是它隻能聲明一位的變量。c變量的每個位元組都可以對應到記憶體中的每位,bit0就是一個位元組的最低位。例如:

《嵌入式C程式設計:PIC單片機和C程式設計技術與應用》一3.3 非标準編譯訓示

3.3.9 #reserve address

該指令用于在ram中預留白間,編譯器不會使用這段空間。當同一個晶片中還存在其他程式(如bootloader)時,我們會用到該指令。

3.3.10 引導加載程式

引導加載程式(bootloader)是位于程式存儲器中的一段獨立的程式,通常用于向程式存儲器中加載應用程式。引導加載程式使用序列槽、usb口,或者一些其他手段來加載程式。通常,在重新開機的時候會啟動bootloader,用于檢測是否需要加載新程式或者程式是否開始運作。有時,bootloader也提供一些基礎功能給應用程式使用。以本書的e3闆子為例,bootloader用于加載應用程式并提供了usb相關函數,以幫助應用程式和pc之間傳輸資料。

3.3.11 #rom address={data}

該指令用于在晶片記憶體的特定位置寫入資料。最常見的用法是初始化晶片上的eeprom。許多pic微控制器中都有一個内部資料存儲器,在掉電之後仍然能夠儲存資料。編譯器為我們提供了函數來讀寫這些存儲器;然而,有時在程式啟動前需要在記憶體中寫入某些資料。該存儲器位址在每個晶片上都不一樣,預編譯宏可以幫助我們在預編譯階段擷取到這些位址。getenv()可以傳回很多資訊,本例中用它來擷取eeprom的資料起始位址:

《嵌入式C程式設計:PIC單片機和C程式設計技術與應用》一3.3 非标準編譯訓示

對于pic16f887,該指令相當于:

《嵌入式C程式設計:PIC單片機和C程式設計技術與應用》一3.3 非标準編譯訓示

rom還有個特性,能夠計算整個程式寄存器的校驗和并将其存儲到某個位置。在應用程式中使用該指令周期性地計算程式寄存器的校驗和并與最初的值相比,進而保證記憶體中的内容沒有變動。如果想将校驗和存儲在記憶體的最後位置,可以這麼做:

《嵌入式C程式設計:PIC單片機和C程式設計技術與應用》一3.3 非标準編譯訓示

因為位址是從0開始的,是以這裡需要-1。如果晶片上有1024字的記憶體,它的位址範圍就是0~1023。

這個校驗和計算的是除存放校驗和的記憶體之外的整個記憶體。

rom的另一個應用是在指定位址中存放資料,因為這些資料可能會根據外部條件發生變化。例如,某些特殊客戶可能需要在産品中使用加密代碼。#rom還可以用于指定裝置的配置資料,如産品中所用引腳的數量。在程式中可以通過函數read_program_memory()讀取#rom寫入的資料,該函數使用程式存儲器的絕對位址來讀取資料。

3.3.12 #id data

許多晶片上還有一塊特殊記憶體(可能隻有一個字長),程式員可以在其中添加某種類型的辨別。即使程式存儲器由于讀保護而不能讀的情況下,這塊區域仍然可以讀取。是以,它可以用來存儲固件版本或者存放校驗和:

《嵌入式C程式設計:PIC單片機和C程式設計技術與應用》一3.3 非标準編譯訓示

3.3.13 其他編譯訓示

下面是其他一些常用的預編譯指令。在編譯器參考手冊的預編譯器小節中可以找到完整的指令清單。

《嵌入式C程式設計:PIC單片機和C程式設計技術與應用》一3.3 非标準編譯訓示

繼續閱讀