PIC單片機C語言編程教程(1)
用
代碼的重復利用率高、便于跨平臺的代碼移植等等,因此 C 語言編程在單片機系統(tǒng)設計中已得到越
來越廣泛的運用。針對 PIC 單片機的軟件開發(fā),同樣可以用 C 語言實現(xiàn)。
但在單片機上用 C 語言寫程序和在 PC 機上寫程序絕對不能簡單等同。現(xiàn)在的 PC 機資
源十分豐富,運算能力強大,因此程序員在寫 PC 機的應用程序時幾乎不用關心編譯后的可
執(zhí)行代碼在運行過程中需要占用多少系統(tǒng)資源,也基本不用擔心運行效率有多高。寫單片機
的
果沒有對單片機體系結構和硬件資源作詳盡的了解,以筆者的愚見認為是無法寫出高質量實
用的 C 語言程序。這就是為什么前面所有章節(jié)中的的示范代碼全部用基礎的匯編指令實現(xiàn)
的原因,希望籍此能使讀者對
上再來討論 C 語言編程,就有水到渠成的感覺。
本書圍繞中檔系列 PIC 單片機來展開討論,Microchip 公司自己沒有針對中低檔系列 PIC
單片機的 C 語言編譯器,但很多專業(yè)的第三方公司有眾多支持 PIC 單片機的 C 語言編譯器
提供,常見的有 Hitech、CCS、IAR、Bytecraft 等公司。其中筆者最常用的是 Hitech 公司的
PICC 編譯器,它穩(wěn)定可靠,編譯生成的代碼效率高,在用 PIC 單片機進行系統(tǒng)設計和開發(fā)
的工程師群體中得到廣泛認可。其正式完全版軟件需要購置,但在其網站上有限時的試用版
供用戶評估。另外,Hitech 公司針對廣大 PIC 的業(yè)余愛好者和初學者還提供了完全免費的學
習版 PICC-Lite 編譯器套件,它的使用方式和完全版相同,只是支持的 PIC 單片機型號限制
在 PIC16F84、PIC16F877 和 PIC16F628 等幾款。這幾款 Flash 型的單片機因其所具備的豐富
的片上資源而最適用于單片機學習入門,因此筆者建議感興趣的讀者可從 PICC-Lite 入手掌
握 PIC 單片機的 C 語言編程。
在此列出幾個主要的針對 PIC 單片機的 C 編譯器相關連接網址,供讀者參考:
Hitech-PICC:
IAR:www.iar.com
CCS:www.ccsinfo.com/picc.shtml
ByteCraft:www.bytecraft.com/mpccaps.html
本章將介紹 Hitech-PICC 編譯器的一些基本概念,由于篇幅所限將不涉及 C 語言的標準
語法和基礎知識介紹,因為在這些方面都有大量的書籍可以參考。重點突出針對
機的特點而所需要特別注意的地方。
11.2
Hitech-PICC 編譯器
PICC 基本上符合 ANSI 標準,除了一點:它不支持函數(shù)的遞歸調用。其主要原因是因
為 PIC 單片機特殊的堆棧結構。在前面介紹 PIC 單片機架構時已經詳細說明了 PIC 單片機
中的堆棧是硬件實現(xiàn)的,其深度已隨芯片而固定,無法實現(xiàn)需要大量堆棧操作的遞歸算法;
另外在 PIC 單片機中實現(xiàn)軟件堆棧的效率也不是很高,為此,PICC 編譯器采用一種叫做“靜
態(tài)覆蓋”的技術以實現(xiàn)對
生出的機器代碼效率很高,按筆者實際使用的體會,當代碼量超過 4K 字后,C 語言編譯出
的代碼長度和全部用匯編代碼實現(xiàn)時的差別已經不是很大(<10%),當然前提是在整個
代碼編寫過程中須時時處處注意所編寫語句的效率,而如果沒有對 PIC 單片機的內核結構、
各功能模塊及其匯編指令深入了解,要做到這點是很難的。
11.3
MPLAB-IDE 內掛接 PICC
PICC 編譯器可以直接掛接在 MPLAB-IDE 集成開發(fā)平臺下,實現(xiàn)一體化的編譯連接和
原代碼調試。使用 MPLAB-IDE 內的調試工具 ICE2000、ICD2 和軟件模擬器都可以實現(xiàn)原
代碼級的程序調試,非常方便。
首先必須在你的計算機中安裝PICC編譯器,無論是完全版還是學習版都可以和
MPLAB-IDE掛接。安裝成功后可以進入IDE,選擇菜單項Project Set Language Tool
Locations…,打開語言工具掛接設置對話框,如圖 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
在對話框中選擇“HI-TECH PICC Toolsuite”欄,展開可執(zhí)行文件組“Executable”后,
列出了將被
“PICC Assembler”、C 原程序編譯器“PICC Compiler”和連接定位程序“PICC Linker”。同
時在此列表中還顯示了對應的可執(zhí)行程序名,請注意在這里都是“PICC.EXE”。用鼠標分別
點擊選中這三項可執(zhí)行文件,觀察對話框下面“Location”一欄中顯示的文件路徑,用
“Browse…”按紐,從計算機中已經安裝的 PICC 編譯器文件夾中選擇 PICC.EXE 文件。實
際上 PICC.EXE 只是一個調度管理程序,它會按照所輸入的文件擴展名自動調用對應的編譯
器和連接器,用戶要注意的是 C 語言原程序擴展名用“.c”,匯編原程序用“.as”即可。
工具掛接完成后,在建立項目時可以選擇語言工具為“HI-TECH PICC”,具體步驟可以
參閱第三章 3.1.3 節(jié),此處不再重復。項目建立完成后可以加入 C 或匯編原程序,也可以加
入已有的庫文件或已經編譯的目標文件。最常見的是只加入 C 原程序。用 C 語言編程的好
處是可以實現(xiàn)模塊化編程。程序編寫者應盡量把相互獨立的控制任務用多個獨立的 C 原程序文件實
現(xiàn),如果程序量較大,一般不要把所有的代碼寫在一個文件內。
圖 11-2 列出的是筆者建立的一個項目中所有 C 原程序模塊,其中主控、數(shù)值計算、I2C 總線操
作、命令按鍵處理和液晶顯示驅動等不同的功能分別在不同的獨立的原程序模塊中實現(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)境編寫 PIC 單片機程序的基本方式和標準 C 程序類似,程序一般由以
下幾個主要部分組成:
&O1540;
提供的“pic.h”文件,實現(xiàn)單片機內特殊寄存器和其它特殊符號的聲明;
&O1540;
&O1540;
匹配檢查;
&O1540;
&O1540;
下面的例 11-1 為一個 C 原程序的范例,供大家參考。
#include
#include “pc68.h”
//定義芯片工作時的配置位
__CONFIG (HS & PROTECT & PWRTEN & BOREN & WDTDIS);
//聲明本模塊中所調用的函數(shù)類型
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)
{
}
//清看門狗
//更新時鐘
//掃描鍵盤
//數(shù)據測量
//刷新特殊功能寄存器
11.5
PICC 中的變量定義
例 11-1
11.5.1
PICC 遵循 Little-endian
在高地址。
11.5.2
基于表 11-1 的基本變量,除了 bit 型位變量外,PICC 完全支持數(shù)組、結構和聯(lián)合等復
合型高級變量,這和標準的 C 語言所支持的高級變量類型沒有什么區(qū)別。例如:
數(shù)組:unsigned int data[10];
結構:struct commInData {
聯(lián)合:union int_Byte {
例 11-2
11.5.3
為了使編譯器產生最高效的機器碼,PICC 把單片機中數(shù)據寄存器的 bank 問題交由編程
員自己管理,因此在定義用戶變量時你必須自己決定這些變量具體放在哪一個 bank 中。如
果沒有特別指明,所定義的變量將被定位在 bank0,例如下面所定義的這些變量:
unsigned char buffer[32];
bit flag1,flag2;
float val[8];
除了 bank0 內的變量聲明時不需特殊處理外,定義在其它 bank 內的變量前面必須加上
相應的 bank 序號,例如:
bank1 unsigned char buffer[32];
bank2 bit flag1,flag2;
bank3 float
//變量定位在 bank2 中
//變量定位在 bank3 中
中檔系列 PIC 單片機數(shù)據寄存器的一個 bank 大小為 128 字節(jié),刨去前面若干字節(jié)的特
殊功能寄存器區(qū)域,在 C 語言中某一 bank 內定義的變量字節(jié)總數(shù)不能超過可用 RAM 字節(jié)
數(shù)。如果超過 bank 容量,在最后連接時會報錯,大致信息如下:
Error[000]
連接器告訴你總共有 0x12C(300)個字節(jié)準備放到 bank1 中但 bank1 容量不夠。顯然,只
有把一部分原本定位在 bank1 中的變量改放到其它 bank 中才能解決此問題。
雖然變量所在的 bank 定位必須由編程員自己決定,但在編寫原程序時進行變量存取操
作前無需再特意編寫設定 bank 的指令。C 編譯器會根據所操作的對象自動生成對應 bank 設
定的匯編指令。為避免頻繁的 bank 切換以提高代碼效率,盡量把實現(xiàn)同一任務的變量定位
在同一個 bank 內;對不同 bank 內的變量進行讀寫操作時也盡量把位于相同 bank 內的變量
歸并在一起進行連續(xù)操作。
11.5.4
PICC 把所有函數(shù)內部定義的 auto 型局部變量放在 bank0。為節(jié)約寶貴的存儲空間,它
采用了一種被叫做“靜態(tài)覆蓋”的技術來實現(xiàn)局部變量的地址分配。其大致的原理是在編譯
器編譯原代碼時掃描整個程序中函數(shù)調用的嵌套關系和層次,算出每個函數(shù)中的局部變量字
節(jié)數(shù),然后為每個局部變量分配一個固定的地址,且按調用嵌套的層次關系各變量的地址可
以相互重疊。利用這一技術后所有的動態(tài)局部變量都可以按已知的固定地址地進行直接尋
址,用 PIC 匯編指令實現(xiàn)的效率最高,但這時不能出現(xiàn)函數(shù)遞歸調用。PICC 在編譯時會嚴
格檢查遞歸調用的問題并認為這是一個嚴重錯誤而立即終止編譯過程。
既然所有的局部變量將占用 bank0 的存儲空間,因此用戶自己定位在 bank0 內的變量字
節(jié)數(shù)將受到一定的限制,在實際使用時需注意。
11.5.5
bit 型位變量只能是全局的或靜態(tài)的。PICC 將把定位在同一 bank 內的 8 個位變量合并
成一個字節(jié)存放于一個固定地址。因此所有針對位變量的操作將直接使用
操作匯編指令高效實現(xiàn)?;诖耍蛔兞坎荒苁蔷植孔詣有妥兞?,也無法將其組合成復合型
高級變量。
PICC 對整個數(shù)據存儲空間實行位編址,0x000 單元的第 0 位是位地址 0x0000,以此后
推,每個字節(jié)有 8 個位地址。編制位地址的意義純粹是為了編譯器最后產生匯編級位操作指
令而用,對編程人員來說基本可以不管。但若能了解位變量的位地址編址方式就可以在最后
程序調試時方便地查找自己所定義的位變量,如果一個位變量 flag1 被編址為 0x123,那么
實際的存儲空間位于:
字節(jié)地址=0x123/8 = 0x24
位偏移
即 flag1 位變量位于地址為 0x24 字節(jié)的第 3 位。在程序調試時如果要觀察 flag1 的變化,必
須觀察地址為 0x24 的字節(jié)而不是 0x123。
PIC
普通變量的操作也將以最簡單的位操作指令來實現(xiàn)。假設一個字節(jié)變量
地址 0x20,那么
tmp |= 0x80
tmp &= 0xf7
=>
=>
0x20,7
0x20,3
if (tmp&0xfe)
=>
即所有只對變量中某一位操作的 C 語句代碼將被直接編譯成匯編的位操作指令。雖然編程
時可以不用太關心,但如果能了解編譯器是如何工作的,那將有助于引導我們寫出高效簡介
的 C 語言原程序。
在有些應用中需要將一組位變量放在同一個字節(jié)中以便需要時一次性地進行讀寫,這一
功能可以通過定義一個位域結構和一個字節(jié)變量的聯(lián)合來實現(xiàn),例如:
union {
} myFlag;
例 11-3
需要存取其中某一位時可以
myFlag.oneBit.b3=1; //b3 位置 1
一次性將全部位清零時可以
myFlag.allBits=0;
當程序中把非位變量進行強制類型轉換成位變量時,要注意編譯器只對普通變量的最低
位做判別:如果最低位是 0,則轉換成位變量 0;如果最低位是 1,則轉換成位變量 1。而標
準的 ANSI-C 做法是判整個變量值是否為 0。另外,函數(shù)可以返回一個位變量,實際上此返
回的位變量將存放于單片機的進位位中帶出返回。
11.5.6
PICC 中描述浮點數(shù)是以 IEEE-754 標準格式實現(xiàn)的。此標準下定義的浮點數(shù)為 32 位長,
在單片機中要用 4 個字節(jié)存儲。為了節(jié)約單片機的數(shù)據空間和程序空間,PICC 專門提供了
一種長度為 24 位的截短型浮點數(shù),它損失了浮點數(shù)的一點精度,但浮點運算的效率得以提
高。在程序中定義的 float 型標準浮點數(shù)的長度固定為 24 位,雙精度 double 型浮點數(shù)一般
也是 24 位長,但可以在程序編譯選項中選擇 double 型浮點數(shù)為 32 位,以提高計算的精度。
一般控制系統(tǒng)中關心的是單片機的運行效率,因此在精度能夠滿足的前提下盡量選擇
24 位的浮點數(shù)運算。
評論