翻譯人 :張?jiān)?/B> 指導(dǎo)校對(duì) : 翻譯修改時(shí)間:2007-4-20 地點(diǎn) :中國(guó)福州 重要說(shuō)明 :僅就PICC18V9.50PL3安裝目錄下的使用手冊(cè)的編程使用最緊密相關(guān)的部分做翻譯。本翻譯純粹為本人業(yè)余興趣所致。本人不承擔(dān)因翻譯錯(cuò)誤、偏差帶來(lái)的任何后果。并且保留在不做通知的情況下升級(jí)本翻譯文檔的權(quán)利。請(qǐng)查閱英文原始版本說(shuō)明書,本文僅做入門參考。另外,本文翻譯的目的在于加深對(duì)PICC18的認(rèn)識(shí)和理解,所以將大量采用意譯而非逐字翻譯。故可能和英文原版有較大的篇幅差別。 版本說(shuō)明 :增加了對(duì)指針的翻譯,中斷處理部分的翻譯。 3.1.1 與ANSI標(biāo)準(zhǔn)C的區(qū)別 受PIC18 MCU的硬件限制,PICC18不支持函數(shù)的遞歸調(diào)用。 3.1.2 同樣的C代碼可能在不同版本的編譯器或者不同的編譯器之間會(huì)編譯成不同的匯編代碼。 3.2.1 編輯安裝目錄下的pic-18.ini文件,可以增加用戶自定義的新的PIC18系列的MCU。 3.2.2 CONFIG的操作 PICC18可以在源代碼中配置CONFIG,由于PIC18 MCU的CONFIG有多個(gè)字節(jié),所以采用如下語(yǔ)法: __CONFIG(2, BW8 & PWRTDIS & WDTPS1 & WDTEN); 注意,前面是兩個(gè)下劃線,這是一個(gè)宏__CONFIG()。該宏的定義在系統(tǒng)文件 htc.h中,根據(jù)PICC18編譯器特性,如果再每個(gè)源文件中都使用了#include <pic18.h>,使用該宏則可不必再寫#include <stc.h>。這個(gè)宏,必須在函數(shù)外使用。 3.2.3 ID區(qū)定義 和CONFIG區(qū)操作類似,采用如下語(yǔ)法: __IDLOC(15F01); 注意,前面是兩個(gè)下劃線,這是一個(gè)宏__CONFIG()。該宏的定義在系統(tǒng)文件 htc.h中,根據(jù)PICC18編譯器特性,如果再每個(gè)源文件中都使用了#include <pic18.h>,使用該宏則可不必再寫#include <stc.h>。這個(gè)宏,必須在函數(shù)外使用。 3.2.4.1 EE區(qū)操作 很多時(shí)候,需要在MCU運(yùn)行前,事先在EE區(qū)燒入一定數(shù)據(jù),MCU上電后,則可從EE區(qū)讀出相關(guān)數(shù)據(jù),執(zhí)行相應(yīng)操作。這個(gè)操作和上面的CONFIG操作類似,采用一個(gè)系統(tǒng)定義的宏,采用如下語(yǔ)法: __EEPROM_DATA(0, 1, 2, 3, 4, 5, 6, 7); 注意,前面是兩個(gè)下劃線,這是一個(gè)宏__CONFIG()。該宏的定義在系統(tǒng)文件 htc.h中,根據(jù)PICC18編譯器特性,如果再每個(gè)源文件中都使用了#include <pic18.h>,使用該宏則可不必再寫#include <stc.h>。這個(gè)宏,必須在函數(shù)外使用。 使用這個(gè)宏,必須也只能一次性初使化8個(gè)字節(jié)。而且第一次調(diào)用這個(gè)宏就是只能是在0地址開始初使化。從0初使化到7,要想再初使化8個(gè)字節(jié),就再調(diào)用一次。比如如下: __EEPROM_DATA(0, 1, 2, 3, 4, 5, 6, 7); __EEPROM_DATA(8,9,10,11, 12,13,14,15); 即使不想初使化前8個(gè)字節(jié),只初使化第10個(gè)字節(jié),也要采用上述的寫法從0寫到15。不過(guò)一般沒(méi)有必要特意從某個(gè)地址開始初使化EE區(qū)。建議我們不要去挑戰(zhàn)編譯器的組織方式,把有這樣的系統(tǒng)調(diào)用宏全部放在主文件中,以便于修改。 如果是在MCU的運(yùn)行過(guò)程中需要對(duì)EE區(qū)操作,則可以考慮在程序中使用自己編寫讀寫EE區(qū)函數(shù),或者采用系統(tǒng)定義的一個(gè)宏(不是系統(tǒng)函數(shù))。 寫EE區(qū)address字節(jié)一個(gè)valus值,如下: EEPROM_WRITE(address,value); 從EE區(qū)的address地址讀回值,存入variable變量,如下: variable=EEPROM_READ(address); variable 為自定義的unsigned char變量。 這些宏為了保證操作,在過(guò)程中關(guān)斷了總中斷GIE。這可能會(huì)導(dǎo)致某些情況下一些實(shí)時(shí)控制系統(tǒng)崩潰——由于中斷不能即時(shí)響應(yīng)。比如發(fā)電機(jī)控制。PIC18 MCU寫EE區(qū)一個(gè)字節(jié)一共需要8MS,插除4MS,寫4MS。 3.2.4.2 FLASH操作 用來(lái)在程序中寫COPY FLASH區(qū)的一個(gè)塊到另外一個(gè)塊: flash_write(source_pointer, length, dest_pointer); 讀FLASH(程序)區(qū)的一個(gè)字節(jié) variable=flash_read(address); 3.2.5外擴(kuò)程序區(qū)操作 用PIC18的MCU,基本上無(wú)人使用它提供的這個(gè)功能。本章節(jié)不翻譯。有興趣請(qǐng)查閱英文原版。如果一定要外擴(kuò),建議使用51或者增強(qiáng)型51MCU。 3.2.6位指令 PICC18會(huì)盡可能使用位指令來(lái)提高編譯效率。 比如使用: unsigned int foo; foo |= 0x40; 會(huì)編譯成如下指令: bsf _foo,6 如果要清或者置某個(gè)整形變量的某個(gè)位,可以采用下面的系統(tǒng)定義的宏。 #define bitset(var,bitno) ((var) |= 1UL < < (bitno)) #define bitclr(var,bitno) ((var) &= ~(1UL < < (bitno))) 比如上面提到的操作,也可以用如下語(yǔ)法實(shí)現(xiàn): bitset(foo,6); 3.2.7 多字節(jié)變量的特殊功能寄存器 比如以16位定時(shí)器的讀寫來(lái)說(shuō),由于硬件特性,從PIC16到PIC18系列的MCU,都必須遵照寫TIMEX,先寫TMRXH,再寫TMRXL,讀則相反,則寫讀TMRXL,TMRXH。以讀TMR1為例子,采用如下語(yǔ)法實(shí)現(xiàn): unsigned char i; i=TMR1L; i+=TMR1H<<8; 3.3.5 運(yùn)行中的啟動(dòng)代碼 C程序在進(jìn)入MAIN()函數(shù)執(zhí)行前,會(huì)要求初使化一些東西,并使芯片從復(fù)位時(shí)候的狀態(tài)轉(zhuǎn)入一種確定的狀態(tài)。 通常說(shuō)來(lái),啟動(dòng)代碼是一段普通的預(yù)編譯的子程序,將鏈接到用戶程序中。即使用戶的程序不需要啟動(dòng)代碼的各個(gè)方面,多余的啟動(dòng)代碼照樣鏈接,雖然這是無(wú)害的,但占用了程序空間,延遲了用戶自編寫的程序的執(zhí)行。 PICC18采用了一種新穎的策略來(lái)識(shí)別什么樣的啟動(dòng)代碼是必要的,此處翻譯省略。 啟動(dòng)代碼在每次編譯的時(shí)候都會(huì)自動(dòng)鏈接,包含啟動(dòng)代碼的單獨(dú)的文件每次都會(huì)被刪除,如果要想在編譯后依然看到它,可以采用如下編譯選項(xiàng),--RUNTIME=default,+keep。這個(gè)文件名為startup.as,至于到底這個(gè)文件在什么位置出現(xiàn),由于本人沒(méi)使用過(guò),暫時(shí)不翻譯,可以使用搜索功能找到它。 這些過(guò)程是不需要用戶去干預(yù)的。 3.3.5.4上電子程序 在某些情況下,我們需要在一上電或者一復(fù)位的時(shí)候,通常是前幾個(gè)指令周期,就根據(jù)上電或者復(fù)位情況執(zhí)行特定代碼。這個(gè)用戶提供的匯編模塊會(huì)在復(fù)位后被馬上執(zhí)行,通常這個(gè)模塊是在C函數(shù)中使用嵌入?yún)R編代碼完成。一個(gè)虛擬的(空的)上電子程序被包含在powerup.as文件中。這個(gè)文件可以被拷貝,修改,添加到你的項(xiàng)目中。添加后不需要任何特殊的編譯,鏈接設(shè)置,或者用代碼指定跳到該文件。編譯器會(huì)自動(dòng)檢查你是否使用了上電子程序。并在復(fù)位后自動(dòng)跳轉(zhuǎn)。如果使用了上電代碼,則需要在初使化后添加一個(gè)跳轉(zhuǎn)到start。power.as文件在編譯器安裝目錄的source文件夾下可以找到。 強(qiáng)調(diào)一下,啟動(dòng)代碼(startup.as)和上電代碼(powerup.as)是不同的東西,啟動(dòng)代碼在所有的情況下都是需要的,主要是執(zhí)行變量的初使化。PICC18會(huì)在最開始的時(shí)候把所有的變量給個(gè)初值,如果你定義的時(shí)候給了初值,沒(méi)有給的,它會(huì)把變量清0。而上電代碼是為了應(yīng)付特殊的需求的,如果不添加文件,是不會(huì)自動(dòng)產(chǎn)生的。 3.4支持的數(shù)據(jù)類型和變量 多字節(jié)變量的存儲(chǔ)格式位,低字節(jié)低地址,高字節(jié)高地址。基本數(shù)據(jù)類型如下表。 進(jìn)制表示。 數(shù)據(jù)類型和進(jìn)制表示上用的是標(biāo)準(zhǔn)的C語(yǔ)言風(fēng)格,每種變量占用的內(nèi)存字節(jié)數(shù)都比較符合ANSI C,比如整形變量就是2個(gè)字節(jié)。某些單片機(jī)的C編譯器,整形變量則可能是一個(gè)字節(jié),這需要大家小心。另外增加了單片機(jī)需要的位變量類型。為了適應(yīng)單片機(jī)計(jì)算,浮點(diǎn)類型允許設(shè)置為24位或者32位(可以在編譯選項(xiàng)中設(shè)置)。 任何的整形常量將由一個(gè)最小的存儲(chǔ)長(zhǎng)度來(lái)存儲(chǔ)同時(shí)保證數(shù)據(jù)不會(huì)溢出。如果加上了后綴“L”或者“l(fā)”則表明此常量為unsigned long 或者signed long 。后綴“U”或者“u”將表明此常量為unsigned類型,如果為“UL”,則為unsigned long。 浮點(diǎn)常量將有兩種類型,除非有明確的后綴“L”或者“l(fā)”表示其為double類型,“F”或者“f”表示其為FLOAT類型。 字符串常量或者字符串?dāng)?shù)據(jù)都用雙引號(hào)來(lái)表示比如用“Hello world”。用const char *來(lái)定義一個(gè)字符串常量,并把這些數(shù)據(jù)存儲(chǔ)在程序區(qū)。把一串字符串常量分派給一個(gè)非常數(shù)字符指針,編譯器會(huì)產(chǎn)生警告,比如: char * cp= "one"; // "one" in ROM, 產(chǎn)生警告 const char * ccp= "two"; // "two" in ROM, 正確 定義一個(gè)非常量的字符數(shù)組可以采用如下方式: char ca[]= "two"; // "two" different to the above 則將在RAM中初使化two,two從程序中拷貝而來(lái)。 兩段分離開的常數(shù)則由編譯器自動(dòng)鏈接。比如中間只空一格的如下表達(dá): const char * cp = "hello " "world"; 將把"hello world"分配給cp。 3.4.2位數(shù)據(jù)類型和變量 PICC18用關(guān)鍵字bit來(lái)聲明一個(gè)位變量,只存儲(chǔ)0或者1。如果加上static,且在函數(shù)內(nèi)聲明,則只可在函數(shù)內(nèi)部使用,例如: static bit init_flag; 如果在函數(shù)外聲明,例如, bit init_flag; 則為全局函數(shù)。 位變量不能定義為一個(gè)局部變量,“auto”。所以當(dāng)在函數(shù)內(nèi)部定義一個(gè)位變量,包括main函數(shù),一定要加上static聲明其為局部靜態(tài)變量,如上所示范。位變量也不能做為一個(gè)函數(shù)的參數(shù)。但是,一個(gè)函數(shù)可以定義為bit類型來(lái)返回一個(gè)位的值。這個(gè)位變量值將放在STATUS寄存器的C位返回。 位變量在很多時(shí)候表現(xiàn)得和無(wú)符號(hào)字符型變量很相似,但他儲(chǔ)存0和1,所以這提供了一個(gè)方便高效的方法來(lái)存儲(chǔ)布爾符號(hào)而不需要消耗大量的RAM空間。盡管如此,不存在指向一個(gè)位變量的指針,也不能靜態(tài)初使化位變量。 把一個(gè)整形變量整體賦值給一個(gè)位變量,則最后一位將賦值給該位變量,這和ANSI C對(duì)布爾類型的轉(zhuǎn)換是不一樣的。比如: int data = 0x54; bit bitvar; bitvar = data; 由于data的最低位為0,所以bitvar將為0。 如果你想用類似ANSI C對(duì)布爾變量類型轉(zhuǎn)換時(shí)候的操作,即把整形數(shù)賦值給位變量,位變量為0還是1,取決于原始的整形數(shù)據(jù)是否為0,則可以采用如下語(yǔ)法: bitvar = data != 0; 位變量在啟動(dòng)代碼會(huì)被強(qiáng)制清0,如果你想對(duì)一個(gè)單獨(dú)的位變量給非0的初值,請(qǐng)?jiān)谧约旱挠脩舸a中處理。而不能指望在定義的時(shí)候給初值就可。 如果使用了——strict編譯選項(xiàng),則位變量不可用。 3.4.3 Using Bit-Addressable Registers 本章節(jié)介紹如何強(qiáng)制定位一個(gè)位變量到某個(gè)地址,一般這只對(duì)特殊寄存器有用,不建議對(duì)變量做這樣的操作,具體形式可以參考MCU的頭文件中對(duì)特殊功能寄存器各個(gè)位的定義,比如 static unsigned char RCON @ 0xFD0; static near bit PD @ (unsigned)&RCON*8+2; 3.4.4 8位整形數(shù)據(jù)類型和變量 PICC18同時(shí)支持有符號(hào)和無(wú)符號(hào)字符型(signed char and unsigned char),如果關(guān)鍵字signed 和unsigned 沒(méi)有標(biāo)明,則char默認(rèn)狀態(tài)下為unsigned char。但如果使用了--CHAR=signed,則char表示signed char。后續(xù)的英文原始文檔不過(guò)再次強(qiáng)調(diào)一個(gè)觀點(diǎn),所謂的字符型,實(shí)際上應(yīng)該完全看作一個(gè)8位的整形,對(duì)字符型的操作和對(duì)8位整形的操作沒(méi)有什么區(qū)別,就是符號(hào)型和整形之間是有天然的轉(zhuǎn)換關(guān)系。比如你可以寫char XXX=’A’,也可以寫char XXX=0X41。這兩者都是等效的。XXX在內(nèi)存中的表示形式都是0X41。對(duì)這點(diǎn)如果有不太熟悉的,可以參考潭浩強(qiáng)的C語(yǔ)言教材。 3.4.5 16位整形數(shù)據(jù)類型和變量 翻譯略。編程無(wú)特殊注意事項(xiàng)。 3.4.6 32位整形數(shù)據(jù)類型和變量 翻譯略。編程無(wú)特殊注意事項(xiàng)。 3.4.7 Floating Point Types and Variables 數(shù)據(jù)的存儲(chǔ)遵照如下格式。 浮點(diǎn)類型一般為有符號(hào)型,所以采用無(wú)符號(hào)聲明一個(gè)浮點(diǎn)數(shù)是非法的。 通過(guò)編譯選項(xiàng)--double=fast32可以有效提高浮點(diǎn)計(jì)算的速度,但以程序區(qū)的空間增大為代價(jià)。在MPLAB IDE下,需要注意設(shè)置觀察變量的屬性,才能正確觀察浮點(diǎn)數(shù)。具體方法如下: 以定義了一個(gè)double vat=10000;的浮點(diǎn)數(shù)(不是整形)的觀察為例子。 首先,HI-TECH的編譯器支持32位,但是為了節(jié)約空間提高運(yùn)算效率,在不對(duì)編譯器選項(xiàng)做設(shè)置的情況下,即使是double,不過(guò)是24位。另外HI-TECH采取的是IEEE754規(guī)范。由于各個(gè)編譯器廠家采取的浮點(diǎn)規(guī)范略有不同,就導(dǎo)致了我們?cè)谟^察的時(shí)候需要注意觀察屬性的設(shè)置,F(xiàn)在見附圖。使用MPLAB IDE英文版用戶,可參造找到適當(dāng)?shù)奈恢谩?/P>
3.4.8 結(jié)構(gòu)體和成員體 結(jié)構(gòu)體和聯(lián)合體,除了單獨(dú)的位變量不能做為其成員,使用上和標(biāo)準(zhǔn)C沒(méi)什么區(qū)別。 3.4.8.1 位結(jié)構(gòu)體 在匯編語(yǔ)言的情況下,我們會(huì)把幾個(gè)位變量放在一個(gè)字節(jié)內(nèi)。以方便管理。PICC18提供了更方便的位結(jié)構(gòu)體類型。在位結(jié)構(gòu)體中,你可以在任意位定義單獨(dú)的變量,并定義多個(gè)位的位段。這和標(biāo)準(zhǔn)C也是類似的。例如: struct { unsigned lo : 1; unsigned dummy : 6; unsigned hi : 1; } foo; lo定義在bit0(最低位),hi定義在bit7(最高位)。 如果你只想在bit0和bit7定義,中間六位暫時(shí)不想使用,可簡(jiǎn)化定義為: struct { unsigned lo : 1; unsigned : 6; unsigned hi : 1; } foo; 而初使化則可以這樣: struct { unsigned lo : 1; unsigned mid : 6; unsigned hi : 1; } foo = {1, 8, 0}; 則foo在的單元實(shí)際上為實(shí)際上為 0 001000 1 ,即0x11。 3.4.8.2 結(jié)構(gòu)體和聯(lián)合體的限制 如果整個(gè)結(jié)構(gòu)體為常量,則全部數(shù)據(jù)將仿在程序區(qū),顯然,將只能讀出。如果這樣,所有的成員必須被初使化。例如: const struct { int number; int *ptr; } record = { 0x55, &i}; 如果某個(gè)結(jié)構(gòu)體的內(nèi)部成員為常量,但結(jié)構(gòu)體不是常量,則整個(gè)結(jié)構(gòu)體將放在RAM中,但每個(gè)成員都變成只讀。比如: struct { const int number; int * const ptr; } record = { 0x55, &i}; 3.4.9 標(biāo)準(zhǔn)類型限制 PICC18支持ANSI C的限制以及為嵌入式專門設(shè)置的一些關(guān)鍵字。 3.4.9.1 Const and Volatile Type Qualifiers const會(huì)告訴編譯器,該變量為只讀,無(wú)法修改。常量被定義的時(shí)候就必須初使化。 volatile用來(lái)告訴編譯器一個(gè)參量成功訪問(wèn)后也未必能獲得值,這能避免啟動(dòng)代碼去錯(cuò)誤操作它。一般用于特殊寄存器的定義。 3.4.10 特殊類型限定 PICC18提供了一些特殊的限定字來(lái)把變量分配到特定的RAM空間。包括persisten, near , far。這些限定也可以用在指針上。但是不能用在局部自動(dòng)變量上。如果一定要用在局部變量上,只能用在局部靜態(tài)變量上。如: static persistent int intvar; PICC18依然支持關(guān)鍵字bank1,bank2,bank3,這些關(guān)鍵字的存在是為了讓代碼能無(wú)縫地從PICC(支持PIC12/16/17系列的編譯器)移植到PICC18。PICC18允許這些限定字,但這些限定不會(huì)對(duì)變量的定位發(fā)生任何影響。 3.4.10.1 Persistent類型限定 該類型告訴編譯器,在上電復(fù)位的時(shí)候這些變量不應(yīng)該被自動(dòng)清0。比如特殊功能寄存器,都加了此限定字。如果要實(shí)現(xiàn)某些功能,需要某些特殊變量在上電的時(shí)候不被清0,則可加此關(guān)鍵字進(jìn)行限定。 3.4.10.2 Near 類型限定 near類型用來(lái)把局部靜態(tài)變量指定到PIC18的ACCESS BANK。如: static near unsigned char fred; 3.4.10.3 Far類型限定 FAR是為了把變量定義在外擴(kuò)的ROM。不推薦使用。只有支持外擴(kuò)的MCU才支持FAR類型。 3.4.11 Bdata類型限定 該關(guān)鍵字只在程序采用small mode 編譯時(shí)有意義。在這樣的模式下,所有的局部靜態(tài)和全局變量都放在ACCESS BANK,bdata則能限制這些變量放在非ACCESS區(qū)。然后這些變量就表現(xiàn)得和在large mode下一樣。這些能防止ACCESS BANK在small mode下溢出,以免更換到large mode模式編譯。 3.4.12 指針類型 PICC18支持兩種基本指針,數(shù)據(jù)指針,函數(shù)指針。數(shù)據(jù)指針存儲(chǔ)變量的地址,以使變量可以為程序讀寫修改。函數(shù)指針存儲(chǔ)可執(zhí)行子函數(shù)的入口,以便通過(guò)指針間接調(diào)用。 3.4.12.1 RAM指針 RAM指針為16位,可以尋址PIC18全空間的RAM。 指向near的指針只有8位,只能訪問(wèn)near 變量(存儲(chǔ)在ACCESS RAM的變量)。 3.4.12.2 Const and Far Pointers 常量和FAR指針,可為16位或者24位,這由編譯選項(xiàng)--CP=24或者 --CP=16決定。項(xiàng)目中的所有模塊必須采用同樣寬度的指針。 指針指向FAR變量和常量是基本一樣,只有一點(diǎn)不同,指向FAR變量的,可能用來(lái)改變所指向的內(nèi)容,指向常量的是絕對(duì)不行的。 16位的常量和FAR指針可以訪問(wèn)全部的RAM空間和大部分的程序空間(64K字節(jié)以內(nèi))。在運(yùn)行時(shí),指針的內(nèi)容將被檢查。地址范圍超過(guò)RAM區(qū)上限的,將采用表讀表寫指令訪問(wèn)程序區(qū),小于RAM區(qū)上限的則訪問(wèn)RAM區(qū)。如果常量指針保存的地址在RAM區(qū),RAM的該地址的內(nèi)容不應(yīng)當(dāng)變化。默認(rèn)情況下,鏈接選項(xiàng)會(huì)把常量放在RAM的地址上限,以保證訪問(wèn)正確的存儲(chǔ)空間。 當(dāng)目標(biāo)MCU的程序區(qū)超過(guò)64K字節(jié),只有低64K字節(jié)能用16位指針訪問(wèn)。后續(xù)內(nèi)容為24位指針,牽涉到CONFIG區(qū)操作,翻譯暫緩。 3.4.12.3 函數(shù)指針 翻譯暫略,內(nèi)容與上面基本一致,講述了16位指針與24位指針的不同,但沒(méi)有給出具體的使用例程。 3.4.12.4 聯(lián)合類型限定與指針 類型限定可用來(lái)限定指針本身的類型以及指針指向的變量的類型。舉例如下: near int * nip ; int * near inp ; near int * near ninp ; nip是指所指向的變量為near int,即存儲(chǔ)在ACCESS RAM,則指針本身為8位,但是會(huì)存在全部RAM BANK的某些地方。 inp是指指向的變量為int,則可能在RAM的任何一個(gè)角落,則指針需要為16位,但加了near限定后,指針變量將放在ACCESS RAM。 ninp則指用存儲(chǔ)在ACCESS RAM的8位指針,指向存儲(chǔ)在ACCESS RAM的變量。 其他限定詞帶來(lái)的變量存儲(chǔ),指示,如上所示范。為了兼容PICC,PICC18允許BANK1,BANK2,BANK3這些關(guān)鍵字的存在,但是這些關(guān)鍵字不影響PICC18的代碼。 3.5 存儲(chǔ)類型和參數(shù)分配 與標(biāo)準(zhǔn)C基本相同,無(wú)重要信息,暫時(shí)不翻譯,請(qǐng)查看英文文檔。 3.6 函數(shù) 與編程操作基本無(wú)關(guān),無(wú)重要信息,暫時(shí)不翻譯,請(qǐng)查看英文文檔。 3.7 與編程操作基本無(wú)關(guān),無(wú)重要信息,暫時(shí)不翻譯,請(qǐng)查看英文文檔。 3.8 操作數(shù) 3.8.1 整形提升 整形提升是值得認(rèn)真學(xué)習(xí)的。C不比匯編,編譯器有時(shí)會(huì)自動(dòng)擴(kuò)充變量長(zhǎng)度,以做中間計(jì)算使用,每款單片機(jī)的C編譯器,都必須充分學(xué)習(xí)其整形提升的語(yǔ)法規(guī)則。否則則請(qǐng)大量使用中間變量,或者手動(dòng)控制整形提升。筆者初次獨(dú)立編寫PID和一些數(shù)學(xué)處理函數(shù),就死在了對(duì)整形提升沒(méi)有認(rèn)真體會(huì)上。 當(dāng)數(shù)學(xué)算式中存在不只一個(gè)操作數(shù)時(shí),典型情況下它們的類型應(yīng)當(dāng)嚴(yán)格一致,否則編譯器會(huì)自動(dòng)進(jìn)行類型轉(zhuǎn)換。轉(zhuǎn)換的原則是向“大”的方向轉(zhuǎn)。即使操作數(shù)都是同樣的類型,但在某些情況下,也會(huì)發(fā)生整型提升。如果你不注意的話,那么異常的結(jié)果就發(fā)生了。整形提升會(huì)在枚舉類型,有符號(hào)無(wú)符號(hào)字符型,有符號(hào)無(wú)符號(hào)整形的變量下發(fā)生。例如: unsigned char count, a=0, b=50; if(a - b < 10) count++; a-b的結(jié)果是206(這個(gè)結(jié)果不是比10小),因?yàn)閍,b在進(jìn)行減法前都被轉(zhuǎn)化成signed int 類型了。然后再?gòu)?50轉(zhuǎn)換成了unsigned int。 實(shí)際上,很多情況下,一個(gè)變量的范圍都是受限制的,并不會(huì)達(dá)到其類型的上下限。按筆者的經(jīng)驗(yàn),這段代碼可以改造為如下。 unsigned int temp_b=b; temp_b+=10; if(a<temp_b) count++; 因?yàn)閍,b本身就為8位,所以轉(zhuǎn)換一個(gè)就夠了。為了避免下溢(出現(xiàn)負(fù)號(hào),出現(xiàn)負(fù)號(hào)則需要進(jìn)行有符號(hào)計(jì)算,比較浪費(fèi)代碼),強(qiáng)烈建議采用這樣的無(wú)符號(hào)提升,并做加法處理。 再看一個(gè)例子: unsigned char count, c; c = 0x55; if( ~c == 0xAA) count++; 似乎理論上對(duì)c取反,8位下,55取反應(yīng)該就是0XAA,但是編譯器做了整形提升,結(jié)果為0XFFAA,出錯(cuò)了。 那么其實(shí)可以用變通的寫法。 unsigned char count, c; c = 0x55; c=~c; if( c == 0xAA) count++; 由于C取反又給了C,所以沒(méi)有整形提升,則判斷不出錯(cuò)。我的觀點(diǎn)是,在不是很了解編譯器什么時(shí)候進(jìn)行整形提升時(shí),我們盡量避免在判斷語(yǔ)句中進(jìn)行計(jì)算后的比較判定。而只做簡(jiǎn)單的判定。另外,用對(duì)0XFF的異或,來(lái)取代求反;蛘哂妙愃频暮陞R編來(lái)完成求反。 3.8.2 移位過(guò)程中的提升 這里的關(guān)鍵是對(duì)有符號(hào)數(shù)進(jìn)行移位的時(shí)候會(huì)發(fā)生什么。無(wú)符號(hào)數(shù)則與ANSI C是一致的。 如果需要直接和匯編指令掛鉤的操作,可以采用系統(tǒng)定義的宏或者自己用嵌入式匯編。 ANSI C,對(duì)于有符號(hào)數(shù)右移時(shí),符號(hào)位可能被清0,或者保持。(應(yīng)該是取決于C編譯器的版本)。 右移動(dòng)有符號(hào)數(shù),PICC18按上述的后者進(jìn)行,即先整體移動(dòng),然后保持符號(hào)位。 右移動(dòng)無(wú)符號(hào)數(shù),最高位清0。 左移動(dòng)無(wú)符號(hào)或者有符號(hào)數(shù),最低為清0。 3.8.3 除以一個(gè)整數(shù)和對(duì)一個(gè)整數(shù)求模(取余數(shù)) 除0將導(dǎo)致結(jié)果為0。 3.9 PSECT用法 沒(méi)研究清楚,暫時(shí)不翻譯。 3.10 中斷的處理 高中斷的聲明和PICC一致,用關(guān)鍵字interrupt。低中斷的聲明用interrupt low_priority。聲明了低中斷處理函數(shù),并不會(huì)把什么中斷自動(dòng)變?yōu)榈椭袛,每個(gè)中斷的高低,還是由程序的代碼中修改。 3.10.2 中斷中的變量保護(hù) PIC18 MCU在發(fā)生中斷時(shí),只將PC壓入堆棧。其他的變量保護(hù)需要由軟件完成。PICC18會(huì)自動(dòng)檢測(cè)需要保存的變量,在軟件上做保存。如果中斷中調(diào)用了某個(gè)函數(shù),而這個(gè)函數(shù)定義在中斷函數(shù)之前,那么編譯器將適當(dāng)包保存必要的變量,否則,最壞的情況就是全部可能用到變量都會(huì)被保存。 PICC18不掃描內(nèi)嵌匯編代碼,中斷中內(nèi)嵌匯編代碼用到的變量,編譯器不會(huì)自動(dòng)保存。 高中斷,編譯器會(huì)動(dòng)用影子寄存器進(jìn)行變量保護(hù)還原,但是指明了compile for icd2,則不會(huì)動(dòng)用影子寄存器,因?yàn)镮CD2會(huì)占用這些空間。 關(guān)于高低中斷將變量保護(hù)到哪個(gè)段,與編程無(wú)關(guān),在此不翻譯。 3.10.2 中斷中的變量還原 變量自動(dòng)還原,無(wú)需我們關(guān)心。 3.10.4 中斷水平 沒(méi)有深入研究,暫時(shí)不翻譯。 3.11 C和匯編的混合編程 |