精品伊人久久大香线蕉,开心久久婷婷综合中文字幕,杏田冲梨,人妻无码aⅴ不卡中文字幕

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
MSC-51單片機學習筆記之1
MSC-51單片機學習筆記之4
標簽單片機  2011-06-04 23:04
第5章 MCS-51 C語言程序設計
不知道一本學習匯編語言的書怎么會花一章的時間帶上C語言,不過也但是給自己的C語言鞏固下
本章要點
MCS-51 C語言的特殊數據類型
MCS-51 C語言程序的存儲模式
MCS-51 C語言程序的結構
C語言與匯編語言的混合編程
5.1 C語言與MCS-51單片機
5.1.1 C語言的開發過程
一般的C語言開發套件中,包括編譯器、連接器和符號轉換程序,編譯器將源程序翻譯為可重定位的目標代碼文件(也可產生等價的匯編語言程序);連接器將目標代碼文件連接為絕對目標文件;符號轉換程序可將絕對目標文件轉換為Intel HEX格式文件,編程到程序存儲器中運行,若使用提供了集成開發環境(IDE)的套件,則編輯、編譯、連接、符號轉換,甚至調試可在一個窗口中完成。
5.1.2 C語言的特點
單片機的C語言符合ANSI C標準,可以產生緊湊的目標碼,效率可以與匯編媲美,與匯編語言相比,C語言不有以下優點
1、不必詳細了解單片機的指令系統
2、僅要求對MCS-51存儲器結構有初步了解
3、寄存器分配、不同存儲器區域的尋址及數據類型等細節由編譯程序管理
4、程序具有規范的結構和固有的模塊化思想
5、運算符和關鍵字用接近于自然語言的方式表示
6、提供包含大量標準子程序的函數庫,具有較強的數據處理能力
7、在對執行效率要求較高的場合,可以嵌入匯編,也可以與匯編語言協同開發
5.1.3單片機C語言的移植
移植的難點就是單片機C語言要解決的問題
1、MCS-51存儲器的非馮·諾依曼結構,加上內部有位尋址空間,對存儲器變量的使用提出了挑戰
2、內部的數據存儲器存儲空間太小,而外部還可擴展存儲容量,編譯程序如何根據實際情況合理使用這些空間
3、內部各功能單元采用特殊功能寄存器集中管理,在C語言中如何實現寄存器訪問
4、MCS-51單片機派生各類繁多,硬件配置不統一,但要求必須能夠使用C語言操控所有硬件資源
5、MXD-51內部只有一個堆棧,且存儲空間有限,傳統的利用堆棧傳遞參數的方法難以奏效
隨著技術的發展和各軟件廠商的努力,以上問題都得到了解決,C語言日趨成熟,成為專業化的實用高級語言
5.2單片機C語言的擴充
5.2.1數據類型
無論是出現在表達式中的常量,還是程序自己定義的變量,都有數據類型,特別是變量,數據類型是編譯程序進行存儲器分配的依據之一
數據類型 位數 字節數 范圍
bit 1   0~1
signed char 8 1 -128~+127
unsinged char 8 1 0~255
enum 8/16 1/2 -128~+127或-32768~+32767
signed short 16 2 -32768~+32767
unsinged short 16 2 0~+65535
signed int 16 2 -32768~+32767
unsigned int 16 2 0~+65535
signed long 32 4 -2147483648~+21473647
unsigned long 32 4 0~4294967295
float 32 4 ±1.175494E-38~±3.402823E+38
sbit 1   0~1
sfr 8 1 0~255
sfr16 16 2 0~65535
表中大部分類型是C語言中的標準類型,像字符型、枚舉型、各種整形和單精度浮點型等,枚舉型根據實際枚舉常量的多少同編譯程序確定其長度,而bit、sbit、sfr和sfr16是為訪問MCS-51硬件中的內部RAM中的位、SRF中的位以及8位SRF和16位SFR(如DPTR)所特有的類型,它們不是ANSI C的一部分,不能用指針對它們進行訪問,也不能定義包含這些類型元素的數組、結構體、聯合體等
例:MCS-51系統中需要處理以下幾個數據:一個是從擴展的I/O端口輸入的8位開關量狀態數據in_data,一個是記錄系統運行時間的log_time(以秒為單位),還有一個是保存設備是否正常運行的標志ok_flag,它們以變量形式存儲,使用哪種類型最合適?
in_data為8位無符號數據,使用unsingned char最合適;log_time需要記錄較長時間若用16位的整形變量,只能累計幾個小時,用long型最好,也應該是無符號的;log_time有一位即可,可用bit型,具體變量定義時,可使用以下語句
unsigned char  in_data;
unsigned long  log_time;
bit            ok_flag;
實際上,只要不是必需,在MCS-51的C語言程序中應該盡量使用較短的、無符號的類型,unsigned char是第一選擇,因為編譯成機器碼后最適合單片機處理的是字節數據。
5.2.2存儲器類型
C語言中的變量的存儲位置通常同編譯程序根據一定的約定進行分配,如果編程時比較清楚某些變量的屬性,程序員也可在變量定義進指定其存儲區域
存儲類型 與硬件存儲器空間的對應關系
code 程序存儲器:使用MOVC @A+DPTR指令訪問
data 直接尋址的內部數據存儲器:訪問速度最快(128字節)
idata 間接訪問的內部數據存儲器:可以訪問所有的內部存儲器空間(256字節)
bdata 可位尋址的內部數據存儲器:可以字節方式也可以位方式訪問(16字節)
xdata 外部數據存儲器(64KB),能過MOVX @DPTR指令訪問
pdata 外部數據存儲器的一頁(256字節),使用MOVX @Ri指令訪問
例:指出以下變量的存儲位置
char data            varl;
char code            text[]="ENTER PARAMETER";
unsigned long xdata  array[100];
float idata          x,y,z;
unsigned int pdata   dimension;
unsigned char xdata  vector[10][4][4];
char bdata           flags;
varl保存于內部RAM中;"ENTER PARAMETER"存儲于程序存儲器中;其首地址以text表示,程序運行期間該符號串不能修改;100個長整形元素的數組array只能存于外部RAM中,占400個字節;單精度浮點數變量XYZ保存于內部RAM內;無符號數變量dimension則存儲于外部RAM的某一頁內;有160個字節數據的三維數組vector也只能存儲于外部RAM中;由多個標志位組成的標志字節flags存儲于位尋址區。
若定義變量時指定了存儲器的類型,編譯程序按要求為其分配存儲空間;若未指定,編譯程序按照存儲器模式自動為變量選擇默認存儲器類型。
5.2.3存儲模式
常用的存儲模式有以下幾種,
1、SMALL模式
SMALL模式下,所有的變量默認存放于內部RAM中,相當于定義時使用了data類型,這時的變量訪問速度最快、效率最高,但是所有對像(包括堆棧)必須能夠存入內部RAM的128字節
2、COMPACT模式
COMPACT模式下,所有變量默認存放于外部RAM的一頁中,相當于定義時使用了pdata類型,這種存儲模式可以滿足最多256字節的變量,由于對變量的訪問必須使用間接尋址方式,所以速度也比訪問內部RAM慢一些,COMPACT模式產生的機器碼不如SMALL模式的快,但是比LARGE模式要好
3、LARGE模式
LARGE械下,所有變量默認存放于外部RAM中,最多可以有64KB,相當于定義時使用了xdata類型,數據指針DPTR用來尋址變量。這種訪問方式效率不高,特別是當變量長度超過一個字節時。尋址方式直接影響代碼長度,產生的機器碼比SMALL和COMPACT模式產生的都要多
如果沒有說明,編譯程序默認使用SMALL模式,由于各種存儲模式在訪問效率、代碼長度、變量總長度等方面各有優缺點,現在常用的C編譯程序通常允許使用混合模式,即不管存儲模式如何,把經常使用的變量強制存放于內部RAM,大塊數據則存放于外部RAM而將其指針存放于內部RAM中,可以使用存儲器類型說明符指定。
5.2.4硬件資料訪問
1、特殊功能寄存器
MCS-51 C語言使用sfr、sfr16和sbit數據類型訪問特殊功能寄存器
sfr P0=0x80;  /*P0口,地址為80H*/
sfr P1=0x90;  /*P1口,地址為90H*/
sfr P2=0xA0;  /*P2口,地址為A0H*/
sfr P3=0xB0;  /*P3口,地址為B0H*/
其中,P0、P1、P2、P3是定義的特殊功能寄存器名字。實際上任何合法的標識符號都可以做為sfr定義中的特殊功能寄存器的變量名,等號后的地址必須是數值常數,而且一定要在特殊功能寄存器區域內(0x80~0xFF).
大多數C環境附帶了一些C頭文件,比較典型的是在reg51.h中對所有51子系統中的特殊功能寄存器進行了sfr定義。
2、特殊功能寄存器中的位
任何合法的標識符都可以作為sbit名字,等號右邊的表達式為該標識符賦予了一個位地址,指定地址有三種方式
A、sfr名字^整形常,該方式使用先前已經定義的sfr名字作為sbit的基地址,要求該sfr地址必須為8位的倍數(即該sfr確實是可以按位訪問的)。在^符號后的整形常數指定該sbit在sfr中的位置,范圍是0~7,其中0為最低有效位。
例:下面定義的三個符號名稱的含義各是什么?
srf  PSW   =0xD0;
sbit OV    =PWS^2;
sbit P     =PWS^0;
它們分別代表程序狀態字PSW(地址為D0H)、溢出標志OV和奇偶標志P
B、整形常數^整形常數
sbit OV    =0xD0^2;
sbit P     =0xD0^0;
C、整形常數,這種方式直接指定位變量的位地址
sbit OV    =0xD2;
sbit P     =0xD0;
3、內部RAM中的位尋址資源
將一個變量定義為bit型后,C編譯程序就會在位尋址區為其分配一位的空間,定義一個其他類型變量時若指定了bdata存儲器類型,C編譯程序也會在內部RAM的位尋址地區為其分配存儲空間,這個變量中的位也可以單獨訪問,但必須先行定義。
4、指定絕對地址的變量
在某些與硬件密切相關的應用中,可能需要指定變量在系統中的絕對地址,而不是讓編譯程序自行分配。在MCS-51的C語言程序中,可使用_at_滿足這一要求,其格式如下:
[存儲器類型]  變量類型  變量名  _at_  地址常數;
例:在某MCS-51系統中,擴展的外部數據存儲器地址2000H~20FFH共256個字節單元作為通信中的接收緩沖區,請對該區域進行定義。
若以r_buf命名該區域,可以如下定義
xdata  nusigned char r_buf[256]  _at_  ox20000;
對于外部擴展的I/O口,所占外部RAM空間的地址已由硬件設計決定,必須指定絕對地址
5、存儲器絕對地址的訪問
單片機C語言頭文件absacc.h中包含了一些宏定義,使用這些宏可以顯示使用存儲器絕對地址,把每個存儲區定義成一個字節或字數組,對指定地址的訪問使用數組元素引用的形式
例:使用存儲器絕對地址訪問的方式,怎么實現以上兩個例子的功能》
#include <absaacc.h>
#define  r_buf   (XBYTE+0x20000)
#define  data_reg XBYTE[0xFF80]
#define  con_reg  XBYTE[0xFF81]
定義后,對數組r_buf和寄存器data_reg、con_reg的訪問方式沒有變化
5.2.5指針
C語言程序中可以使用指針變量或指針常量,其值為所指類型變量的地址,也可以是該類型數組的起始地址
1、基于存儲器的指針
基于存儲器的指針類型與源程序中存儲器類型有關,編譯進即可確定其長度。這種指針的長度可以為1個字節(dtat*、idtat*、pdata*)或2個字節(code*、xdata*)
例:指出下面指針定義的作用
char   data   *str;    /*指向data字符的指針*/
int    xdata  *numtab; /*指向xdata整形數據的指針*/
long   code   *powtab; /*指向data長整形的指針*/
所定義的str numtab powtab三個指針變量長度分別為1個字節、2字節、2字節,它們自身所占用的存儲位置由存儲模式確定。
同普通變量一樣,在定義指針變量時可以指定其存儲類型
例:指出下面指針定義的含義
char   data  *xdata  str;   /*指針變量位于xdata*/
int    xdata *data   numtab;/*指針變量位于data*/
long   code  *idata  powtab;/*指針變量位于idata*/
2、通用指針
定義和標準C指針定義相同,凡是指針定義中未對指向的對象存儲器類型進行修飾說明的,編譯程序都將其作為通用指針,使用3個字節存儲指針內部,第一個字節存放存儲器類型,第二和第三個字節分別存放該指針所指對象地址的高字節和低字節
例:指出以下代碼段中各變量的含義以及變化情況
xdata  int  x;
int    *data px,*data py;
px=&x;
py=0x021234;
*px=1000;
*py=-1;
整形變量X位于外部RAM中;兩個通用指針px和py,通過賦值語句使px指向x,而py指向外部RAM的1234H單元,最后兩條賦值語句使x值成為1000,1234單元內容成為FFFFH。
這兩節的內部概念性的東西太多了,一時很難記的住,我都看幾邊了也沒有記住不知道是不是方法有問題。
繼續這章內容
5.3 C語言程序結構
程序入口為main函數,每個函數內部可以使用結構化程序設計技術的三種結構
5.3.1函數
1、函數定義
C語言一般采用模塊化設計,最基本的模塊就是同函數表示,MCS-51的C語言程序中,在定義函數時還可以指定是否為中斷算是函數、是否為可重入函數,可以選擇工作寄存器組以及確定其存儲模式,函數定義的基本格式如下
[返回值類型]   函數名稱(表達式)  [{small ︱ compact ︱ large}]
[reentrant] [interrupt n] [using n]
若省略返回值類型部分,則默認為整形( int),可以指定該函數的存儲模式,以取代默認值;若使用using,編譯程序將產生切換工作寄存器組的代碼;對于有返回值的函數,不能使用using,因為返回值是通過寄存器傳遞的。
2、參數傳遞
參數用于幾函數傳遞數據,作為函數的輸入,MSC-51參數傳遞是通過存儲器和寄存器傳遞的,通過寄存器傳遞速度快是默認的傳遞方式,傳遞時所使用的寄存器分配如下表,這時最多能3個參數,若函數參數較多,寄存器不足以傳遞所有參數則使用固定地址的存儲器單元作為函數的存放位置,當第一個參數是bit型時,無法用寄存器傳遞參數。若參數個數不超過3個,可以將bit型參數放在參數表最后
表5-4傳遞參數的寄存器分配
參數個數
char或字節指針
int或2個字節指針
long或float
通用指針
1
R7
R6&R7
R4~R7
R1~R3
2
R5
R4&R5
R4~R7
R1~R3
3
R3
R2&R3
R1~R3
3、返回值
與傳遞參數不同,函數的返回值總是通過寄存器送回的如下表
表5-5函數返回值所用寄存器分配
返回值類型
寄存器
描述
bit
CY標志
char,unsigned char,或1個字節指針
R7
int,unsigned int,或2個字節指針
R6&R7
最高有效位在R6中,最低有效位在R7中
long或unsigned long
R4~R7
最高有效位在R4中,最低有效位在R7中
float
R4~R7
32位IEEE格式
通用指針
R1~R3
存儲器類型在R3中,最高有效位在R2中,最低有效位在R1中
4、內部函數和外部函數
如果一個函數只能在其定義的文件中被調用,則稱為內部函數,也稱為靜太函數,定義內部函數時需要用static存儲類型說明
Static unsigned int fun(unsigned char the_byte,bit the_flag)
函數fun在包含其定義的文件外不可訪問。
允許在其它文件中調用的函數為外部函數,可以使用extern存儲類型說明符指明。函數定義時若無存儲類型說明,默認為外部函數。
5、可重入函數
單片機的C編譯程序通常的局部變量分配在存儲器的固定位置,如果正在執行該函數時發生了中斷,而中斷服務程序中也調用該函數,先前的局部變量值便會被破壞,類似的情況在實現函數遞歸調用時也會發生,對于一個函數,如果確實需要遞歸調用,或者確實非中斷服務程序代碼與中斷服務程序都要調用,應當將它定義為可重入函數,使編譯程序產生能夠保護局部變量的代碼,可以重入函數是使用reentrant來說明。
例:有一個延時函數,在程序中多次被調用,包括中斷服務程序,請將其定義為可重入函數。
void delay(void)reentrant
{
int i;
for(i=0;i<1000;i++)
;
}
其實若非遞歸調用,也可以不編寫可重入函數,而是將同一函數改寫為非中斷服務程序調用和中斷服務程序調用的兩個函數,變量所需存儲空間沒有顯著減少,代碼腳加長了
6、中斷處理函數
中斷處理函數也稱作中斷服務程序,是CPU響應中斷后要執行的一段程序,在C語言中組織成一個函數的形式,編寫中斷處理函數時,程序員只需要中斷類型號和寄存器組的選擇,編譯程序會自動產生中斷向量和返回地址的入棧及出棧代碼。在函數定義時可以使用interrupt將其指定為一個中斷處理函數,還可以用using分配中斷處理函數所使用的寄存器組。
例:說明下面函數定義的作用
unsigned int interruptcnt;
unsigned char second;
void timer0 (void) interrupt 1 using 2
{
if(++interruptcnt= =1000)
{
second++;
interruptcnt=0;
}
}
函數timer0是一個中斷處理函數,所對應的中斷類型號為1,使用第二組工作寄存器
7、intrinsic函數
在MSC-51 C語言中,intrinsic函數是一類用匯編語言代碼實現的短小函數,若C語言程序中有對intrinsic函數的調用,編譯程序將會直接用被調用函數代碼替換函數調用語句。
常見intrinsic函數的原型如下,它們一般在intris.h文件中
extern void          _nop_    (void);
extern bit           _testbit_   (bit);
extern unsigned char  _cror_    (unsigned char,unsigned char);
extern unsigned int   _iror_     (unsigned int,unsigned int);
extern unsigned long  _lror_     (unsigned long,unsigned long);
extern unsigned char  _crol_     (unsigned char,unsigned char);
extern unsigned int    _irol_     (unsigned int,unsigned int);
extern unsigned long   _lrol_    (unsigned long,unsigned long);
這些函數名稱前后都有下劃線,這是與其它庫函數的最明顯區別,以上函數實現的功能分別是空操作、位測試以及字符型、整形和長整形數據的左、右移位。
例:編寫代碼,若位變量flag值為1,則8位位數據data8右移兩位并將flag清零,否則左移3位。
if(_teatbit_(flag))
data8=_cror_(data8,2);
else
data8=_crol_(data8,3);
5.3.2流程控制
1、分支,C語言有兩種分支方式
A、if語句
If(表達式) 語句1
這種形式實現了單分攤結構,若表達式值非0,則執行后面的語句1,然后繼續往下執行,若表達式的值為0,則跳過語句1直接往下執行
兩個分支的if語句形式為
if(表達式)
語句1
else
語句2
若表達式值是非0,則執行后的語句1,然后執行表達式語句2后面的語句,若表過式的值為0則跳過語句1執行語句2,然后繼續執行下面語句
多分支if語句形式為
if(表達式)
語句1
else if(表達式2)
語句2
else if(表達式3)
語句3
…………..
else
語句n
多選結構n個語句中只能執行一個,即第一個值非0表達式后面的語句。
以上三種形式中,所有語句都可以是復合語句,即用花括號引起來的語句組。
B、switch-case結構
當選擇較多時使用if語句和程序結構會變得臃腫,switch-case結構是比較簡潔的寫法形式為
switch(表達式)
{
csae   常量表達式1:語句組1;break;
csae   常量表達式2:語句組2;break;
………
csae   常量表達式n:語句組n;break;
default;              語句組n+1;break;
}
Switch后的表達式可以是整形或字符型、枚舉型數據,case后的各常量大達式須與其類型相同或可以相互轉換,當前者的值與某一case后表達式的值相等時,執行其后的語句組,然后執行break退出switch語句,若所有case后表達式與之皆不相等,則執行default后語句組,case后表達式須各不相乖。
2、循環
C語言中實現循環結構的語句也有多種
A、 goto語句:用來實現轉移,結合if語句,可以實現簡單的循環,類似于指令系統中的條件轉移指令的作用,但是goto語句可以轉向程序中任何位置,所以受到結構化程序設計支持者的強烈抵制
B、 while結構,形式為
while(表達式)
語句
其中表達式為循環條件,語句構成循環體。若循環條件值非0,則執行循環體,一種常見的形式為
while(1)
{
……….
}
這種形式可以稱為無限循環,一般單片機軟件就是這種形式,如下代碼
while(!(P1&0x01))
;
實現的是等待P1.0為1,循環體部分為空語句,循環條件是輸入的P1值最低有效位為0.
C、 do….while結構,形式為
do
語句
While(表達式);
不像while結構先判斷條件,do….while結構是先執行一次語句(循環體),然后再判斷條件,若條件表達式值非0,則繼續下次循環。
D、for結構,for結構是使用最靈活的循環控制語句,形式為
for(表達式1;表達式2;表達式3)
語句
for結構的執行過程為:先對表達式1求值;再對表達式2求值,若表達式2的值是非0,則執行一次語句,然后對表達式3求值,再一次對表達式2求值,若非0,則在此形成循環,直到表達式2的值為0,則循環結束。如下代碼
for(;P1&0x01;)
;
實現的也是等待P1.0輸入為1,而
for(i=0;(i<10000)&&(P1&0x01);i++)
;
實現的是有時間限制的等待P1.0為1,具體時間可以通過檢查編譯產生的代碼計算得到,或者在仿真器上設置斷點觀察得知。
E、 break和continue語句
break語句不公能夠跳出switch結構,還可以從循環體中跳出,提前結束循環而執行循環后面的語句。Break只能用在循環語句(包括while、do….whilet和for結構)和switch語句中。
Continue語句則是提前結束本次循環,跳過循環體中continue后面未執行的語句,接著進行一次循環條件的判定。
break和continue語句其實是結構化程序設計方法中實現非結構化的一種手段,在退出循環或提前結束循環的條件不易表達時,這類語句可以使程序更容易理解。
5.3.3輸入與輸出
一些C開發環境提供了流式輸入/輸出函數,可以實現通過串口或用戶自定義I/O接口的輸入/輸出操作,例如getchar、gets、scanf、putchar、puts、printf等,輸入/輸出功能需要調用_getkey和putchar兩個函數,這兩個函數的默認實現是通過串行口實現的,所以如果使用輸入/輸出函數,還需要在程序中加入一些代碼,以便調用時已經對串行口進行了適當的初始公工作。
例:說明以下代碼的運行結果
#include   <reg51.h>    /*初始化時要用到SFR*/
#include   <stdio.h>    /*引入輸入/輸出函數原型*/
Void main(void)
{
int x,y            /*變量*/
SCON  =0x50;    /*開始對串行口的初始化代碼*/
PCON &=0x7F;
TMOD &=0xCF;
TMOD &=0x20;
TH1   =0xFD;
TR1   =1;
TI     =1;        /*初始化結束*/
While(1)
{
scanf(“%d%d,&x,&y);              /*輸入*/
printf(“x=%04x,y=%04x\n”,x,y);     /*輸出*/
}
}
該程序接收用戶輸入的十進制數值,然后從串行口以十六進制格式輸出
程序員可以根據系統輸入/輸出接口的配置情況重寫_getkey和putchar兩個函數,其他函數功能保持不變。
5.3.4程序的入口
C語言程序的入口是main函數,而單片機復位后是從0000H開始執行代碼程序,main函數位于系統程序存儲器的何處呢?下面觀察一下例子程序的可執行代碼
0000H
010012
LJMP
0012H
0003H
EF
MOV
A,R7
0004H
C4
SWAP
A
0005H
540F
ANL
A,#0FH
0007H
75F00A
MOV
B,#0AH
000AH
A4
MUL
AB
000BH
FE
MOV
R6,A
000CH
EF
MOV
A,R7
000DH
540F
ANL
A,#0FH
000EH
2E
ADD
A,R6
000FH
FF
MOV
R7,A
0010H
22
RET
0012H
787F
MOV
R0,7FH
0014H
E4
CLR
A
0015H
F6
MOV
@R0,A
0016H
DBFD
DJNZ
R0,0015H
0018H
758108
MOV
SP,#08H
001BH
02001E
LJMP
001EH
001EH
7F56
MOV
R7,#56H
0020H
120003
LCALL
0003H
0023H
8F08
MOV
08H,R7
0025H
80FE
SJMP
0025H
最左邊一列是程序存儲器地址,第二列是指令機器碼,最右邊一列為助記符表示,可以看到單片機復位后,先轉移到0012H,將內部RAM單元00H~7FH清零,置SP為08H后,轉移到main函數(001EH)處執行,即在main函數執行之前,已經做了一些初始化處理。
這是默認的初始化操作,至于堆棧,取決于編譯程序在內部RAM中為局部變量分配空間的大小,若有在main函數執行之前就應當初始化的資源,或者需要將存儲區初始化謎團特定的值,程序員可以在匯編語言程序STARTUP.A51中修改或添加代碼,在使用C語言開發的單片機軟件中,單片機程序的入口其實還是0000H,在STARTUP.51中初始化代碼的最后一條指令才轉向main函數執行。
5.4 C語言與匯編語言的混合編程
C語言和匯編語言各有優缺點,C語言中數據類型豐富,程序結構清晰,但是在執行速度、精確定時、控制硬件等方面不如匯編語言方便,如果要在各方面都獲得滿意的結果,可以使用C語言與匯編語言的混合編程。
用C語言調用匯編語言程序時,被調用函數(匯編語言函數)要在調用函數(C語言函數)所在文件中說明,對于匯編語言程序有以下要求
1、  要使用SEGMENT偽指令定義可重定的CODE段。
2、  要根據不同的情況對函數名進行轉換,見下表5-6.
3、  須使用PUBLIC偽指令將被調用函數說明為外部可用函數
4、  若有參數傳遞,按照表5-4所列的規則使用參數。
5、  若有返回值,按照表5-5所列的規則存儲寄存器。
表5-6函數名轉換規則
函數首部
符號名
說明
void func(void)
FUNC
無參數傳遞或不含寄存器參數的函數名不作改變
void func(char)
_FUNC
帶寄存器參數的函數名加“_“
void func(void) reentrant
_?FUNC
可重入函數前加“_?”
例:編寫匯編語言函數max,參數為兩個8位無符號數,功能是求出其中的大數返回。
在C語言中可按如下方式聲明和調用
extern   unsigned  char  max(unsigned, unsigned); /*聲明*/
void  main(void)
{
unsigned char  x,y;
x=130;y=131;
x=max(x,y);               /*調用*/
while(1);
}
兩個參數分別在R7和R5中傳遞到子程序,返回值應保存在R7中,匯編語言程序文件可如下編寫,
PUBLIC  _MAX               ;聲明
MIXED   SEGMENT   CODE     ;定義一個可重定位的段MIXED
ESEG      MIXED     ;選擇MIXED為當前段
_MAX:
MOV       A,R7      ;第一個參數
CLR       C
SUBB      A,R5      ;減去第二個參數
JNC       _MAX_RET  ;無借位,第一個參數值大
MOV       A,R5      ;有借位,第二個參數值大
MOV       R7,A      ;返回值在R7
_MAX_RET:
RET
EDN
例:編寫匯編語言函數delayms,參數為一個8位無符號數,功能是按照參數指定的毫秒數實現延時。這個函數有參數傳遞,但是無返回值。
匯編語言文件可如下編寫
PUBLIC  _DELAYMS                  ;聲明
HAHA    SEGMENT   CODE            ;定義一個可重定位的段HAHA
RSEG      HAHA            ;選擇HAHA為當前段
_DELAYMS:
MOV   R6,#2               ;以下實現約1ms的延時
_DELAY_NEXT:
MOV   R5,#248
DJNZ  R5,$
DJNZ  R6,_DELAY_NEXT
DJNZ  R7,_DELAYMS
RET
END                        ;返回
例:編寫匯編語言函數delay10ms,沒有參數,功能是延時10ms,這個問題與上例的區別在于無參數傳遞,所以函數名無需加“_”。
匯編語言文件可寫成如下形式
PUBILIC  EDLAY10MS
MIXED    SEGMENT     CODE
RSEG        MIXED
DELAY10MS:
MOV     R6,#20
DELAY_NEXT:
MOV     R5,#248
DJNZ    R5,$
DJNZ    R6,DELAY_NEXT
RET
END
以上例子中,只是各編寫了一個供C語言程序調用的匯編語言函數,若需要多個,也是可以寫在同一個匯編語言程序文件中,匯編語言程序中也可以調用C語言函數,但不常用。
本站僅提供存儲服務,所有內容均由用戶發布,如發現有害或侵權內容,請點擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Keil C51學習過程
Keil C51庫函數及學習附錄
【單片機C語言基礎入門】第三章:變量與數據類型
變量 keil存儲器類型
單片機C語言關鍵字、算術運算符、邏輯運算符、復合運算符
手把手教你如何優化C語言程序
更多類似文章 >>
生活服務
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯系客服!

聯系客服

主站蜘蛛池模板: 会宁县| 开阳县| 繁峙县| 瑞金市| 新河县| 宁陵县| 互助| 长乐市| 宣城市| 县级市| 龙游县| 安乡县| 正镶白旗| 湘乡市| 新田县| 大理市| 和政县| 深水埗区| 新昌县| 彭山县| 晋宁县| 津南区| 阜城县| 东源县| 额济纳旗| 石柱| 若尔盖县| 阳江市| 怀仁县| 泽普县| 惠州市| 永春县| 阳城县| 广宁县| 鸡西市| 英超| 连州市| 石柱| 嘉兴市| 建昌县| 泸西县|