新聞中心

EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > PIC單片機(jī)C語(yǔ)言編程教程(1)

PIC單片機(jī)C語(yǔ)言編程教程(1)

作者: 時(shí)間:2016-11-22 來(lái)源:網(wǎng)絡(luò) 收藏
PIC 單片機(jī) C 語(yǔ)言編程簡(jiǎn)介

本文引用地址:http://m.butianyuan.cn/article/201611/319595.htm

C 語(yǔ)言來(lái)開(kāi)發(fā)單片機(jī)系統(tǒng)軟件最大的好處是編寫(xiě)代碼效率高、軟件調(diào)試直觀、維護(hù)升級(jí)方便、

代碼的重復(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ī)

C 程序最關(guān)鍵的一點(diǎn)是單片機(jī)內(nèi)的資源非常有限,控制的實(shí)時(shí)性要求又很高,因此,如

果沒(méi)有對(duì)單片機(jī)體系結(jié)構(gòu)和硬件資源作詳盡的了解,以筆者的愚見(jiàn)認(rèn)為是無(wú)法寫(xiě)出高質(zhì)量實(shí)

用的 C 語(yǔ)言程序。這就是為什么前面所有章節(jié)中的的示范代碼全部用基礎(chǔ)的匯編指令實(shí)現(xiàn)

的原因,希望籍此能使讀者對(duì) PIC 單片機(jī)的指令體系和硬件資源有深入了解,在這基礎(chǔ)之

上再來(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: www.htsoft.com


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ì) PIC 單片

機(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ì) C 語(yǔ)言函數(shù)中的局部變量分配固定的地址空間。經(jīng)這樣處理后產(chǎn)

生出的機(jī)器代碼效率很高,按筆者實(shí)際使用的體會(huì),當(dāng)代碼量超過(guò) 4K 字后,C 語(yǔ)言編譯出

的代碼長(zhǎng)度和全部用匯編代碼實(shí)現(xiàn)時(shí)的差別已經(jīng)不是很大(<10%),當(dāng)然前提是在整個(gè) C

代碼編寫(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 MPLAB-IDE 語(yǔ)言工具設(shè)置對(duì)話(huà)框

在對(duì)話(huà)框中選擇“HI-TECH PICC Toolsuite”欄,展開(kāi)可執(zhí)行文件組“Executable”后,

列出了將被 MPLAB-IDE 后臺(tái)調(diào)用的編譯器所用到的所有可執(zhí)行文件,其中有匯編編譯器

“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 C 語(yǔ)言多模塊編程

11.4 PIC 單片機(jī)的 C 語(yǔ)言原程序基本框架

基于 PICC 編譯環(huán)境編寫(xiě) PIC 單片機(jī)程序的基本方式和標(biāo)準(zhǔn) C 程序類(lèi)似,程序一般由以

下幾個(gè)主要部分組成:

&O1540; 在程序的最前面用#include 預(yù)處理指令引用包含頭文件,其中必須包含一個(gè)編譯器

提供的“pic.h”文件,實(shí)現(xiàn)單片機(jī)內(nèi)特殊寄存器和其它特殊符號(hào)的聲明;

&O1540; 用“__CONFIG”預(yù)處理指令定義芯片的配置位;

&O1540; 聲明本模塊內(nèi)被調(diào)用的所有函數(shù)的類(lèi)型,PICC 將對(duì)所調(diào)用的函數(shù)進(jìn)行嚴(yán)格的類(lèi)型

匹配檢查;

&O1540; 定義全局變量或符號(hào)替換;

&O1540; 實(shí)現(xiàn)函數(shù)(子程序),特別注意 main 函數(shù)必須是一個(gè)沒(méi)有返回的死循環(huán)。

下面的例 11-1 為一個(gè) C 原程序的范例,供大家參考。

#include //包含單片機(jī)內(nèi)部資源預(yù)定義

#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)

{

SetSFR();

PORTC = 0x00;

TMR1H += TMR1H_CONST;

LED1 = LED_OFF;

LCD_Test();

//程序工作主循環(huán)

while(1) {

asm(“clrwdt”);

Clock();

KeyScan();

Measure();

SetSFR();

}

}

//清看門(mén)狗

//更新時(shí)鐘

//掃描鍵盤(pán)

//數(shù)據(jù)測(cè)量

//刷新特殊功能寄存器

11.5

PICC 中的變量定義


例 11-1 C 語(yǔ)言原程序框架舉例

11.5.1 PICC 中的基本變量類(lèi)型


PICC 遵循 Little-endian 標(biāo)準(zhǔn),多字節(jié)變量的低字節(jié)放在存儲(chǔ)空間的低地址,高字節(jié)放

在高地址。

11.5.2 PICC 中的高級(jí)變量

基于表 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 {

unsigned char inBuff[8];

unsigned char getPtr, putPtr;

};

聯(lián)合:union int_Byte {

unsigned char c[2];

unsigned int i;

};

例 11-2 C 語(yǔ)言高級(jí)變量舉例

11.5.3 PICC 對(duì)數(shù)據(jù)寄存器 bank 的管理

為了使編譯器產(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]; //變量定位在 bank1 中


bank2 bit flag1,flag2;

bank3 float val[8];


//變量定位在 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] : Cant find 0x12C words for psect rbss_1 in segment BANK1

連接器告訴你總共有 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 中的局部變量

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 PICC 中的位變量

bit 型位變量只能是全局的或靜態(tài)的。PICC 將把定位在同一 bank 內(nèi)的 8 個(gè)位變量合并

成一個(gè)字節(jié)存放于一個(gè)固定地址。因此所有針對(duì)位變量的操作將直接使用 PIC 單片機(jī)的位

操作匯編指令高效實(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

位偏移 =0x123%8 = 3

即 flag1 位變量位于地址為 0x24 字節(jié)的第 3 位。在程序調(diào)試時(shí)如果要觀察 flag1 的變化,必

須觀察地址為 0x24 的字節(jié)而不是 0x123。

PIC 單片機(jī)的位操作指令是非常高效的。因此,PICC 在編譯原代碼時(shí)只要有可能,對(duì)

普通變量的操作也將以最簡(jiǎn)單的位操作指令來(lái)實(shí)現(xiàn)。假設(shè)一個(gè)字節(jié)變量 tmp 最后被定位在

地址 0x20,那么


tmp |= 0x80

tmp &= 0xf7


=> bsf

=> bcf


0x20,7

0x20,3


if (tmp&0xfe)


=> btfsc 0x20,0


即所有只對(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 {

struct {

unsigned b0: 1;

unsigned b1: 1;

unsigned b2: 1;

unsigned b3: 1;

unsigned b4: 1;

unsigned b5: 1;

unsigned : 2; //最高兩位保留

} oneBit;

unsigned char allBits;

} myFlag;

例 11-3 定義位變量于同一字節(jié)

需要存取其中某一位時(shí)可以

myFlag.oneBit.b3=1; //b3 位置 1

一次性將全部位清零時(shí)可以

myFlag.allBits=0; //全部位變量清 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ù)

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)論


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

關(guān)閉