劉金平,葉賽風(fēng)
摘要:文章通過(guò)9個(gè)程序詳細(xì)介紹PIC單片機(jī)在PICC編譯器環(huán)境下,C語(yǔ)言與匯編語(yǔ)言混合編程的方法并對(duì)出現(xiàn)的情況進(jìn)行分析解決,最后對(duì)C語(yǔ)言與匯編語(yǔ)言混合編程的優(yōu)劣提出自已的看法。
關(guān)鍵詞:PIC單片機(jī);PICC編譯器;C語(yǔ)言;匯編語(yǔ)言
中圖分類號(hào):TP312文獻(xiàn)標(biāo)識(shí)碼:A文章編號(hào):1009-3044(2010)11-2640-05
Analysis Based on C Language and Assembler Language Mix Programming in PICC Compiler
LIU Jin-ping, YE Sai-feng
(Fujian Electric Vocational and Technical College, Quanzhou 362000, China)
Abstract: Articles detailed C language and assembly language mixed programming methodology and anslysis and settlement according to these problems of PIC singlechip in PICC compiler environment through 9 program and lastly presented their own views on the advantages and disadvantages of C language and assembly language mixed programming .
Key words: PICC singlechip; PICC compiler; C language; assembler language
目前,最常用PIC單片機(jī)C語(yǔ)言編程環(huán)境是MATLAB調(diào)試環(huán)境和HI-TECH編譯器PICC。雖然還有眾多支持PIC單片機(jī)的C語(yǔ)言編譯器,但PICC因?yàn)榉€(wěn)定、可靠、編譯生成的代碼效率高,而受到青睞。用C語(yǔ)言來(lái)開(kāi)發(fā)PIC單片機(jī)系統(tǒng)軟件最大的好處是編寫代碼效率高,軟件高度直觀、維護(hù)升級(jí)方便、代碼的重復(fù)利用率高、便于跨平臺(tái)的代碼移植等。因此用C語(yǔ)言編程,在進(jìn)行系統(tǒng)設(shè)計(jì)和開(kāi)發(fā)的工程師群體中得到廣泛認(rèn)可。既然C語(yǔ)言是一種強(qiáng)有力的程序設(shè)計(jì)語(yǔ)言,那么是不是就可以拋棄匯編語(yǔ)言?寫單片機(jī)C程序最關(guān)鍵的一點(diǎn)是單片機(jī)內(nèi)設(shè)資源非常有限,同時(shí),要求控制的實(shí)時(shí)性高,這就要求對(duì)單片機(jī)體系結(jié)構(gòu)和硬件資源有詳盡的了解,而這些恰恰是匯編語(yǔ)言的優(yōu)勢(shì),所以對(duì)于PIC愛(ài)好者,尤其初學(xué)者而言,C語(yǔ)言與匯編語(yǔ)言混編是不可避免的。實(shí)際上,C語(yǔ)言和匯編語(yǔ)言混合編程可以使單片機(jī)應(yīng)用程序的開(kāi)發(fā)效率和程序本身的運(yùn)行效率達(dá)到最佳的配合。
1 PICC對(duì)RAM的特殊要求
下面,我們通過(guò)一個(gè)引例的不同編程來(lái)看PICC對(duì)RAM的特殊要求。
例:將RAM中的BANK0的0X20~0X2F共16個(gè)單元置為55H。
1.1 采用匯編語(yǔ)言編程
程序1:
INCLUDE "P16F877.INC"
MOVLW 0X20
MOVWF FSR
MOVLW 55H
ABC MOVWFINDF
INCF FSR ,F
BTFSSFSR ,4
GOTOABC
GOTO$
END
程序1經(jīng)過(guò)調(diào)試,運(yùn)行后能實(shí)現(xiàn)引例功能。
1.2 采用C語(yǔ)言編程
程序2:
#include"pic.h"
unsigned char i;
unsigned char *tmp;
void main()
{
tmp=0x20;
for(i=1;i<17;i++)
{
(*tmp)=0x55;
tmp++;
}
}
程序2中運(yùn)用指針知識(shí),經(jīng)過(guò)調(diào)試沒(méi)有發(fā)現(xiàn)錯(cuò)誤,但運(yùn)行后根本無(wú)法滿足引例的要求。
修改程序2第2行為:“bank2 unsigned char i;”,即變量i改到RAM的BANK2。運(yùn)行的結(jié)果是將0X56~0X64共15個(gè)單元置為55H。再次將程序2第6行修改為:“tmp=0x21;”,運(yùn)行后,RAM的0X21~0X30共16個(gè)單元置為55H,離要求還有一點(diǎn)距離。
1.3 采用C語(yǔ)言絕對(duì)定位技術(shù)
程序3:
#include"pic.h"
bank2 unsigned char i;
static volatile unsigned char *tmp @ 0x30;
void main()
{
tmp=0x20;
for(i=1;i<17;i++)
{
(*tmp)=0x55;
tmp++;
}
}
程序3將定義的指針定位在BANK0的0X30單元開(kāi)始的區(qū)域,與0X20~0X2F區(qū)域沒(méi)有沖突。經(jīng)過(guò)調(diào)試運(yùn)行能實(shí)現(xiàn)引例功能。
1.4 采用C語(yǔ)言與匯編語(yǔ)言混編
程序4:
#include"pic.h"
bank2 unsigned char i;
void main()
{ #asm
MOVLW0X20
MOVWF_FSR
MOVLW0X55
MOVWF_INDF
#endasm
for(i=1;i<16;i++)
{ #asm
INCF _FSR,F
MOVLW0X55
MOVWF _INDF
#endasm
}
}
程序4中唯一變量i 定義在RAM的BANK2區(qū)域,程序經(jīng)過(guò)調(diào)試運(yùn)行能實(shí)現(xiàn)引例功能。要說(shuō)明的是,C語(yǔ)言與匯編語(yǔ)言混編時(shí),匯編語(yǔ)言部分的特殊功能寄存器名必須采用下劃線引導(dǎo)和大寫字母。
從以上不同方法的編程出現(xiàn)的結(jié)果,可以看出在C語(yǔ)言環(huán)境下,PICC對(duì)File Registers(RAM數(shù)據(jù)存儲(chǔ)器)中的20H~7FH單元具有絕對(duì)使用權(quán),這些單元位于BANK0的最后,即通用寄存器的96個(gè)字節(jié)。所以,在編程時(shí)若要應(yīng)用這部分單元,那么,則在C語(yǔ)言中定義的所有變量必須放在除BANK0外的RAM中,否則運(yùn)行結(jié)果不可預(yù)料。
2 PICC混編時(shí)對(duì)傳遞參數(shù)的要求
2.1 采用局部變量進(jìn)行主函數(shù)與自定義函數(shù)之間的數(shù)據(jù)傳遞
PICC規(guī)定,應(yīng)用局部變量傳遞數(shù)據(jù),在匯編語(yǔ)言部分引用參數(shù)時(shí),第2 個(gè)形參要寫成?_函數(shù)名,第3個(gè)形參要寫成?_函數(shù)名+1,以此類推。而第1個(gè)形參以及函數(shù)內(nèi)部自定義變量必須分別命名為?a_函數(shù)名,?a_函數(shù)名+1,……,至于?a_函數(shù)名先分配給第1個(gè)形參或是第1個(gè)自定義變量則不確定。
程序5:
#include
void Test(unsigned char inVar1, unsigned char inVar2,unsigned char inVar3,unsigned char inVar4)
{
unsigned char tmp1=0;
inVar1++;
asm("incf ?a_Test+1,f");
asm("decf ?_Test,f");//inVar2--;
#asm //inVar3+=5;
movf ?_Test+1,w
addlw 0x5
movwf ?_Test+1
#endasm
#asm //inVar4-=0x5左移一位,低位補(bǔ)1;
bsf _STATUS,0
rlf ?_Test+2,f
#endasm
#asm //tmp1=inVar1;
movf ?a_Test+1,w
movwf ?a_Test
#endasm
}
void main()
{
unsigned char t1,t2;
t1=t2=10;
Test(t1,t2,t1,t2);
}
程序5中的自定義函數(shù)名為Test,共有inVar1~inVar4等4個(gè)形參,函數(shù)內(nèi)部還有1個(gè)自定義變量tmp1。在匯編語(yǔ)言中inVar1寫成?a_Test+1、inVar2~inVar4分別寫成?_Test~?_Test+2、而tmp1寫成?a_Test。需要說(shuō)明的是:
1) 程序中C語(yǔ)言部分若不對(duì)第1個(gè)形參inVar1進(jìn)行操作,則在匯編語(yǔ)言部分中對(duì)inVar1處理是無(wú)效的!也就是不分配內(nèi)在單元給inVar1。
2) 程序中C語(yǔ)言部分若不對(duì)函數(shù)內(nèi)部的自定義tmp1進(jìn)行賦值或運(yùn)算,則在匯編部分對(duì)tmp1處理是無(wú)效的,也就是不分配RAM空間。
以上兩點(diǎn),在進(jìn)行C語(yǔ)言與匯編語(yǔ)言混編時(shí)應(yīng)特別關(guān)注。
2.2 采用全局變量進(jìn)行主函數(shù)與自定義函數(shù)之間的數(shù)據(jù)傳遞
程序6:
#include
volatile unsigned char tmp;
volatile bank1 unsigned char tmp1;
volatile bank2 unsigned char tmp2;
void Test(void)
{
#asm
clrf _STATUS
movlw 0x55
movwf _tmp
#endasm
}
void Test1(void)
{
#asm
clrf _STATUS
movlw 0x10
movwf _tmp
bcf _STATUS,6
BSF _STATUS,5
movlw 0x20
movwf _tmp1^0x80;非BANK0變量,在匯編語(yǔ)言中使用要如此
#endasm
}
void main()
{
while(1)
{
Test1();
if(tmp==0x10)
{
tmp=0xab;
}
if(tmp1==0x20)
{
tmp1=0xcd;
}
Test();
tmp2=tmp+tmp1;
}
}
程序6采用全局變量傳遞數(shù)據(jù),共定義tmp、tmp1、tmp2三個(gè)全局變量,分別定義在RAM的BANK0、BANK1、BANK2,其中tmp、tmp1用于函數(shù)之間傳遞函數(shù),在匯編語(yǔ)言部分要使用這兩個(gè)全局變量時(shí),只要在變量名前加下劃線作為引導(dǎo)就可以了。但要注意的是,若這些全局變量定義在非BANK0,如程序中的tmp,則在匯編語(yǔ)言部分使用時(shí),必須寫成_tmp1^0x80。若是定義在BANK1,就得寫成_tmp1^0x100,若是定義在BANK2,就得寫成_tmp1^0x180。
程序7:
#include
volatile unsigned char inVar1,inVar2,inVar3,inVar4;
void Test()
{
unsigned char tmp1=0;
//inVar1-=1;
asm("incf _inVar1,f");
//inVar2--;
asm("decf _inVar2,f");
//inVar3+=5;
#asm
movf _inVar3,w
addlw 0x5
movwf _inVar3
#endasm
//inVar4-=0x5左移一位,低位補(bǔ)1;
#asm
bsf _STATUS,0
rlf _inVar4,f
#endasm
//tmp1=inVar1;
#asm
movf _inVar1,w
movwf ?a_Test
#endasm
}
void main()
{
unsigned char tmp1=2;
inVar1=inVar2=inVar3=inVar4=10;
Test();
inVar1=inVar4;inVar3=tmp1;
}
程序7除定義了全局變量,還在自定義函數(shù)中定義了一個(gè)自定義變量tmp1,同樣在匯編部分要換名為?a_Test,需要說(shuō)明的是,若對(duì)tmp1不賦初值,那么在匯編時(shí)會(huì)出現(xiàn)“找不到?a_Test”錯(cuò)誤信息,而無(wú)法通過(guò)匯編。
使用全局變量最大的好處是尋址直觀,只需在 C 語(yǔ)言定義的變量名前增加一個(gè)下劃線,即可在匯編語(yǔ)句中尋址,使用全局變量進(jìn)行參數(shù)傳遞效率比形參傳遞高。
3 C語(yǔ)言主函數(shù)內(nèi)含匯編語(yǔ)言的情況
程序8:
#include
void main()
{
unsigned char tmp,i;
while(1)
{
tmp=1;
#asm
movf ?a_main+1,w
movwf ?a_main
#endasm
i++;
} }
程序8在主函數(shù)內(nèi)定義兩個(gè)變量,這兩個(gè)變量在匯編語(yǔ)言部分使用時(shí),必須換名為 ?a_main和 ?a_main+1,需要說(shuō)明的是,第1 個(gè)自定義變量tmp,在匯編語(yǔ)言部分使用?a_main前,在C語(yǔ)言部分必須先進(jìn)行賦初值或計(jì)算,否則會(huì)出現(xiàn)錯(cuò)誤。
程序9:
#include
volatileunsigned char tmp,i;
void main()
{
while(1)
{
//tmp=1;
#asm
movf _i,w
movwf _tmp
#endasm
i++;
} }
程序9在主函數(shù)外定義了兩個(gè)全局變量,它們?cè)趨R編語(yǔ)言部分使用比較簡(jiǎn)單,只要在變量前加下劃線引導(dǎo)就可以。
4 結(jié)束語(yǔ)
可以看出,C語(yǔ)言和匯編語(yǔ)言混合編程在具體實(shí)施時(shí),一定要注意許多細(xì)節(jié)問(wèn)題,否則運(yùn)算結(jié)果不可預(yù)料。
一般來(lái)說(shuō),在項(xiàng)目開(kāi)發(fā)過(guò)程中,程序量較大且PICC 進(jìn)行后道編譯優(yōu)化,采用C語(yǔ)言編寫的代碼不會(huì)比全部用匯編編寫的代碼差多少。所以,既然用了C語(yǔ)言編程,就盡量避免使用嵌入?yún)R編指令或整個(gè)地編寫匯編指令模塊文件。對(duì)于非得采用C語(yǔ)言和匯編語(yǔ)言混合編程,一定要注意:1)盡量使用嵌入?yún)R編;2)盡量使用全局變量進(jìn)行參數(shù)傳遞。
參考文獻(xiàn):
[1] 李榮正.PIC單片機(jī)原理及應(yīng)用[M].北京:北京航空航天大學(xué)出版社,2006.
[2] 周堅(jiān).PIC單片機(jī)輕松入門[M].北京:北京航空航天大學(xué)出版社,2009.
[3] 張明峰.PIC單片機(jī)入門與實(shí)戰(zhàn)[M].北京:北京航空航天大學(xué)出版社,2004.
電腦知識(shí)與技術(shù)2010年11期