PIC單片機(jī)C語(yǔ)言編程教程(1)
用
代碼的重復(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)單等同?,F(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ī)
的
果沒(méi)有對(duì)單片機(jī)體系結(jié)構(gòu)和硬件資源作詳盡的了解,以筆者的愚見(jiàn)認(rèn)為是無(wú)法寫(xiě)出高質(zhì)量實(shí)
用的 C 語(yǔ)言程序。這就是為什么前面所有章節(jié)中的的示范代碼全部用基礎(chǔ)的匯編指令實(shí)現(xiàn)
的原因,希望籍此能使讀者對(duì)
上再來(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:www.iar.com
CCS:www.ccsinfo.com/picc.shtml
ByteCraft:www.bytecraft.com/mpccaps.html
本章將介紹 Hitech-PICC 編譯器的一些基本概念,由于篇幅所限將不涉及 C 語(yǔ)言的標(biāo)準(zhǔn)
語(yǔ)法和基礎(chǔ)知識(shí)介紹,因?yàn)樵谶@些方面都有大量的書(shū)籍可以參考。重點(diǎn)突出針對(duì)
機(jī)的特點(diǎn)而所需要特別注意的地方。
11.2
Hitech-PICC 編譯器
PICC 基本上符合 ANSI 標(biāo)準(zhǔn),除了一點(diǎn):它不支持函數(shù)的遞歸調(diào)用。其主要原因是因
為 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ì)
生出的機(jī)器代碼效率很高,按筆者實(shí)際使用的體會(huì),當(dāng)代碼量超過(guò) 4K 字后,C 語(yǔ)言編譯出
的代碼長(zhǎng)度和全部用匯編代碼實(shí)現(xiàn)時(shí)的差別已經(jīng)不是很大(<10%),當(dāng)然前提是在整個(gè)
代碼編寫(xiě)過(guò)程中須時(shí)時(shí)處處注意所編寫(xiě)語(yǔ)句的效率,而如果沒(méi)有對(duì) PIC 單片機(jī)的內(nèi)核結(jié)構(gòu)、
各功能模塊及其匯編指令深入了解,要做到這點(diǎn)是很難的。
11.3
MPLAB-IDE 內(nèi)掛接 PICC
PICC 編譯器可以直接掛接在 MPLAB-IDE 集成開(kāi)發(fā)平臺(tái)下,實(shí)現(xiàn)一體化的編譯連接和
原代碼調(diào)試。使用 MPLAB-IDE 內(nèi)的調(diào)試工具 ICE2000、ICD2 和軟件模擬器都可以實(shí)現(xiàn)原
代碼級(jí)的程序調(diào)試,非常方便。
首先必須在你的計(jì)算機(jī)中安裝PICC編譯器,無(wú)論是完全版還是學(xué)習(xí)版都可以和
MPLAB-IDE掛接。安裝成功后可以進(jìn)入IDE,選擇菜單項(xiàng)Project Set Language Tool
Locations…,打開(kāi)語(yǔ)言工具掛接設(shè)置對(duì)話(huà)框,如圖 11-1 所示:
%C3%82%C2%B3%C3%83%C2%8C%C3%82%C2%BD%C3%83%C2%8C%C3%82%C2%B3%C3%83%C2%8C.files/7.jpg" src="file:///F:/data/%C3%83%C2%8F%C3%83%C2%82%C3%83%C2%94%C3%83%C2%98/PIC%C3%82%C2%B5%C3%82%C2%A5%C3%83%C2%86%C3%82%C2%AC%C3%82%C2%BB%C3%83%C2%BAC%C3%83%C2%93%C3%83%C2%AF%C3%83%C2%91%C3%83%C2%94%C3%82%C2%B1%C3%83%C2%A0%3Cwbr%3E%C3%82%C2%B3%C3%83%C2%8C%C3%82%C2%BD%C3%83%C2%8C%C3%82%C2%B3%C3%83%C2%8C.files/7.jpg" />
圖 11-1
在對(duì)話(huà)框中選擇“HI-TECH PICC Toolsuite”欄,展開(kāi)可執(zhí)行文件組“Executable”后,
列出了將被
“PICC Assembler”、C 原程序編譯器“PICC Compiler”和連接定位程序“PICC Linker”。同
時(shí)在此列表中還顯示了對(duì)應(yīng)的可執(zhí)行程序名,請(qǐng)注意在這里都是“PICC.EXE”。用鼠標(biāo)分別
點(diǎn)擊選中這三項(xiàng)可執(zhí)行文件,觀察對(duì)話(huà)框下面“Location”一欄中顯示的文件路徑,用
“Browse…”按紐,從計(jì)算機(jī)中已經(jīng)安裝的 PICC 編譯器文件夾中選擇 PICC.EXE 文件。實(shí)
際上 PICC.EXE 只是一個(gè)調(diào)度管理程序,它會(huì)按照所輸入的文件擴(kuò)展名自動(dòng)調(diào)用對(duì)應(yīng)的編譯
器和連接器,用戶(hù)要注意的是 C 語(yǔ)言原程序擴(kuò)展名用“.c”,匯編原程序用“.as”即可。
工具掛接完成后,在建立項(xiàng)目時(shí)可以選擇語(yǔ)言工具為“HI-TECH PICC”,具體步驟可以
參閱第三章 3.1.3 節(jié),此處不再重復(fù)。項(xiàng)目建立完成后可以加入 C 或匯編原程序,也可以加
入已有的庫(kù)文件或已經(jīng)編譯的目標(biāo)文件。最常見(jiàn)的是只加入 C 原程序。用 C 語(yǔ)言編程的好
處是可以實(shí)現(xiàn)模塊化編程。程序編寫(xiě)者應(yīng)盡量把相互獨(dú)立的控制任務(wù)用多個(gè)獨(dú)立的 C 原程序文件實(shí)
現(xiàn),如果程序量較大,一般不要把所有的代碼寫(xiě)在一個(gè)文件內(nèi)。
圖 11-2 列出的是筆者建立的一個(gè)項(xiàng)目中所有 C 原程序模塊,其中主控、數(shù)值計(jì)算、I2C 總線操
作、命令按鍵處理和液晶顯示驅(qū)動(dòng)等不同的功能分別在不同的獨(dú)立的原程序模塊中實(shí)現(xiàn)。
%C3%82%C2%B3%C3%83%C2%8C%C3%82%C2%BD%C3%83%C2%8C%C3%82%C2%B3%C3%83%C2%8C.files/8.jpg" src="file:///F:/data/%C3%83%C2%8F%C3%83%C2%82%C3%83%C2%94%C3%83%C2%98/PIC%C3%82%C2%B5%C3%82%C2%A5%C3%83%C2%86%C3%82%C2%AC%C3%82%C2%BB%C3%83%C2%BAC%C3%83%C2%93%C3%83%C2%AF%C3%83%C2%91%C3%83%C2%94%C3%82%C2%B1%C3%83%C2%A0%3Cwbr%3E%C3%82%C2%B3%C3%83%C2%8C%C3%82%C2%BD%C3%83%C2%8C%C3%82%C2%B3%C3%83%C2%8C.files/8.jpg" />
圖 11-2
11.4
基于 PICC 編譯環(huán)境編寫(xiě) PIC 單片機(jī)程序的基本方式和標(biāo)準(zhǔn) C 程序類(lèi)似,程序一般由以
下幾個(gè)主要部分組成:
&O1540;
提供的“pic.h”文件,實(shí)現(xiàn)單片機(jī)內(nèi)特殊寄存器和其它特殊符號(hào)的聲明;
&O1540;
&O1540;
匹配檢查;
&O1540;
&O1540;
下面的例 11-1 為一個(gè) C 原程序的范例,供大家參考。
#include
#include “pc68.h”
//定義芯片工作時(shí)的配置位
__CONFIG (HS & PROTECT & PWRTEN & BOREN & WDTDIS);
//聲明本模塊中所調(diào)用的函數(shù)類(lèi)型
void SetSFR(void);
void Clock(void);
void KeyScan(void);
void Measure(void);
void LCD_Test(void);
void LCD_Disp(unsigned char);
//定義變量
unsigned char second, minute, hour;
bit flag1,flag2;
//函數(shù)和子程序
void main(void)
{
}
//清看門(mén)狗
//更新時(shí)鐘
//掃描鍵盤(pán)
//數(shù)據(jù)測(cè)量
//刷新特殊功能寄存器
11.5
PICC 中的變量定義
例 11-1
11.5.1
PICC 遵循 Little-endian
在高地址。
11.5.2
基于表 11-1 的基本變量,除了 bit 型位變量外,PICC 完全支持?jǐn)?shù)組、結(jié)構(gòu)和聯(lián)合等復(fù)
合型高級(jí)變量,這和標(biāo)準(zhǔn)的 C 語(yǔ)言所支持的高級(jí)變量類(lèi)型沒(méi)有什么區(qū)別。例如:
數(shù)組:unsigned int data[10];
結(jié)構(gòu):struct commInData {
聯(lián)合:union int_Byte {
例 11-2
11.5.3
為了使編譯器產(chǎn)生最高效的機(jī)器碼,PICC 把單片機(jī)中數(shù)據(jù)寄存器的 bank 問(wèn)題交由編程
員自己管理,因此在定義用戶(hù)變量時(shí)你必須自己決定這些變量具體放在哪一個(gè) bank 中。如
果沒(méi)有特別指明,所定義的變量將被定位在 bank0,例如下面所定義的這些變量:
unsigned char buffer[32];
bit flag1,flag2;
float val[8];
除了 bank0 內(nèi)的變量聲明時(shí)不需特殊處理外,定義在其它 bank 內(nèi)的變量前面必須加上
相應(yīng)的 bank 序號(hào),例如:
bank1 unsigned char buffer[32];
bank2 bit flag1,flag2;
bank3 float
//變量定位在 bank2 中
//變量定位在 bank3 中
中檔系列 PIC 單片機(jī)數(shù)據(jù)寄存器的一個(gè) bank 大小為 128 字節(jié),刨去前面若干字節(jié)的特
殊功能寄存器區(qū)域,在 C 語(yǔ)言中某一 bank 內(nèi)定義的變量字節(jié)總數(shù)不能超過(guò)可用 RAM 字節(jié)
數(shù)。如果超過(guò) bank 容量,在最后連接時(shí)會(huì)報(bào)錯(cuò),大致信息如下:
Error[000]
連接器告訴你總共有 0x12C(300)個(gè)字節(jié)準(zhǔn)備放到 bank1 中但 bank1 容量不夠。顯然,只
有把一部分原本定位在 bank1 中的變量改放到其它 bank 中才能解決此問(wèn)題。
雖然變量所在的 bank 定位必須由編程員自己決定,但在編寫(xiě)原程序時(shí)進(jìn)行變量存取操
作前無(wú)需再特意編寫(xiě)設(shè)定 bank 的指令。C 編譯器會(huì)根據(jù)所操作的對(duì)象自動(dòng)生成對(duì)應(yīng) bank 設(shè)
定的匯編指令。為避免頻繁的 bank 切換以提高代碼效率,盡量把實(shí)現(xiàn)同一任務(wù)的變量定位
在同一個(gè) bank 內(nèi);對(duì)不同 bank 內(nèi)的變量進(jìn)行讀寫(xiě)操作時(shí)也盡量把位于相同 bank 內(nèi)的變量
歸并在一起進(jìn)行連續(xù)操作。
11.5.4
PICC 把所有函數(shù)內(nèi)部定義的 auto 型局部變量放在 bank0。為節(jié)約寶貴的存儲(chǔ)空間,它
采用了一種被叫做“靜態(tài)覆蓋”的技術(shù)來(lái)實(shí)現(xiàn)局部變量的地址分配。其大致的原理是在編譯
器編譯原代碼時(shí)掃描整個(gè)程序中函數(shù)調(diào)用的嵌套關(guān)系和層次,算出每個(gè)函數(shù)中的局部變量字
節(jié)數(shù),然后為每個(gè)局部變量分配一個(gè)固定的地址,且按調(diào)用嵌套的層次關(guān)系各變量的地址可
以相互重疊。利用這一技術(shù)后所有的動(dòng)態(tài)局部變量都可以按已知的固定地址地進(jìn)行直接尋
址,用 PIC 匯編指令實(shí)現(xiàn)的效率最高,但這時(shí)不能出現(xiàn)函數(shù)遞歸調(diào)用。PICC 在編譯時(shí)會(huì)嚴(yán)
格檢查遞歸調(diào)用的問(wèn)題并認(rèn)為這是一個(gè)嚴(yán)重錯(cuò)誤而立即終止編譯過(guò)程。
既然所有的局部變量將占用 bank0 的存儲(chǔ)空間,因此用戶(hù)自己定位在 bank0 內(nèi)的變量字
節(jié)數(shù)將受到一定的限制,在實(shí)際使用時(shí)需注意。
11.5.5
bit 型位變量只能是全局的或靜態(tài)的。PICC 將把定位在同一 bank 內(nèi)的 8 個(gè)位變量合并
成一個(gè)字節(jié)存放于一個(gè)固定地址。因此所有針對(duì)位變量的操作將直接使用
操作匯編指令高效實(shí)現(xiàn)。基于此,位變量不能是局部自動(dòng)型變量,也無(wú)法將其組合成復(fù)合型
高級(jí)變量。
PICC 對(duì)整個(gè)數(shù)據(jù)存儲(chǔ)空間實(shí)行位編址,0x000 單元的第 0 位是位地址 0x0000,以此后
推,每個(gè)字節(jié)有 8 個(gè)位地址。編制位地址的意義純粹是為了編譯器最后產(chǎn)生匯編級(jí)位操作指
令而用,對(duì)編程人員來(lái)說(shuō)基本可以不管。但若能了解位變量的位地址編址方式就可以在最后
程序調(diào)試時(shí)方便地查找自己所定義的位變量,如果一個(gè)位變量 flag1 被編址為 0x123,那么
實(shí)際的存儲(chǔ)空間位于:
字節(jié)地址=0x123/8 = 0x24
位偏移
即 flag1 位變量位于地址為 0x24 字節(jié)的第 3 位。在程序調(diào)試時(shí)如果要觀察 flag1 的變化,必
須觀察地址為 0x24 的字節(jié)而不是 0x123。
PIC
普通變量的操作也將以最簡(jiǎn)單的位操作指令來(lái)實(shí)現(xiàn)。假設(shè)一個(gè)字節(jié)變量
地址 0x20,那么
tmp |= 0x80
tmp &= 0xf7
=>
=>
0x20,7
0x20,3
if (tmp&0xfe)
=>
即所有只對(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 {
} myFlag;
例 11-3
需要存取其中某一位時(shí)可以
myFlag.oneBit.b3=1; //b3 位置 1
一次性將全部位清零時(shí)可以
myFlag.allBits=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ù)是以 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)算。
評(píng)論