MSP430 單片機C語言和匯編語言混合編程
MSP430是一款16位的單片機,它具有超低功耗、豐富的片內(nèi)外圍模塊、多樣的可選型號、軟件對硬件的靈活控制能力等優(yōu)點。因此特別適合于以電池為電源的應(yīng)用場合或手持設(shè)備,目前在國內(nèi)主要應(yīng)用于三表系統(tǒng)和消防設(shè)備方面。MSP430單片機的開發(fā)軟件較常用的是IAR公司的IAR Embedded Workbench集成開發(fā)環(huán)境,它可以編輯、匯編和編譯匯編語言和C語言源文件,并且其C語言和匯編語言具有相同格式的頭文件,給開發(fā)帶來了靈活性。C 語言具有編程簡單,可以移植等優(yōu)點,但是產(chǎn)生代碼較長,對硬件的直接控制能力相對較弱;匯編語言產(chǎn)生的代碼較小,控制硬件靈活,但是可讀性差,移植困難,因此為了發(fā)揮各自優(yōu)點,產(chǎn)生高速度、高效率的代碼混合編程是最好的選擇。
1 IAR C語言編譯器的參數(shù)傳遞規(guī)則
1.1 寄存器應(yīng)用
C語言編譯器把單片機的寄存器分成兩組來使用:
(1)高速暫存器(R12-R15),這組寄存器專門用作參數(shù)傳遞,因此調(diào)用時不需要保護。
(2)其它普通寄存器(R4-R11),這組寄存器主要用作寄存器變量和保存中間結(jié)果,因此調(diào)用時必需保護,這一點C語言編譯器是自動處理的。
1.2 堆棧結(jié)構(gòu)和參數(shù)傳遞
每一次函數(shù)調(diào)用會創(chuàng)建一個如圖所示的堆棧結(jié)構(gòu)
一個調(diào)用者函數(shù)傳遞給被調(diào)用函數(shù)的參數(shù)按照從右到左的順序傳遞的,換句話說就是除了最左邊的兩個參數(shù)用寄存器傳遞外,其余參數(shù)用堆棧傳遞,并按從右到左的順序入棧。若最左邊的兩個參數(shù)屬于結(jié)構(gòu)或聯(lián)合類型,那么它們也用堆棧傳遞。函數(shù)的返回結(jié)果根據(jù)其類型存放在R12或R13:R12寄存器對,若返回結(jié)果屬于結(jié)構(gòu)或聯(lián)合類型,那么R12中存放的是指向返回結(jié)果的指針。
1.3 中斷函數(shù)
C語言編譯器編譯中斷函數(shù)時會自動保護所有用到的寄存器(包括R12-R15在內(nèi)),狀態(tài)寄存器SR的保護是中斷處理過程自動完成的。中斷函數(shù)中用到的任何寄存器都會用PUSH Rxx指令保護,中斷服務(wù)結(jié)束用后POP Rxx指令恢復(fù);RETI指令會自動恢復(fù)狀態(tài)寄存器SR和從中斷返回。
2 對匯編語言函數(shù)的約定
一個能被C語言函數(shù)調(diào)用的匯編語言函數(shù)必須做到以下幾點:
(1)符合C語言編譯器的參數(shù)傳遞規(guī)則。
(2)具有PUBLIC入口標號。
(3)對C語言調(diào)用者函數(shù)聲明為外部函數(shù),并且允許參數(shù)類型檢查和提升(可選)。
2.1 局部存儲分配
如果匯編語言函數(shù)需要局部存儲空間,有兩種分配方法:
(1)分配在硬件堆棧
(2)分配在靜態(tài)空間,但是函數(shù)不能重入。
2.2 中斷函數(shù)
因為中斷可能發(fā)生在程序執(zhí)行的任何期間,所以調(diào)用約定并不適用于中斷函數(shù)。因此必需注意以下幾點:
(1)必須保護所有用到的寄存器。
(2)必須用RETI返回。
(3)把SR中各標志位當做未定義來使用。
(4)中斷向量定義在INTVEC段
3 混合編程
明確了以上約定,混合編程就非常容易。基本做法是:
(1)C語言源文件用‘extren’關(guān)鍵字導(dǎo)入被匯編語言源文件導(dǎo)出的標號。
(2)匯編語言源文件用‘PUBLIC’關(guān)鍵字把標號導(dǎo)出給C語言源文件。
(3)匯編語言源文件用‘EXTREN’關(guān)鍵字導(dǎo)入被C語言源文件導(dǎo)出的標號。
(4)C語言源文件把標號導(dǎo)出給匯編語言文件,則不需要關(guān)鍵字。
(5)把寫好的C語言源文件和匯編語言源文件加入工程,并用各自調(diào)用函數(shù)的指令調(diào)用即可。
4 應(yīng)用實例
4.1 C 語言函數(shù)和匯編語言函數(shù)相互調(diào)用
在這個示例中C語言函數(shù)main()調(diào)用匯編語言函數(shù)get_rand()以得到一個隨機數(shù);匯編語言函數(shù)get_rand()首先調(diào)用C語言的標準庫函數(shù)rand()得到一個整型隨機值,然后用調(diào)用C語言函數(shù)mult()的方法把這個隨機值乘以main()函數(shù)傳遞給自己的實參,并把乘積值返回給 main()函數(shù)。
4.1.1 C語言源文件
/**************************************************************/
/* 文件名:c_source.c 2003-01-05 */
/* C語言和匯編語言混合編程,C源程序 */
/* 這段源程序調(diào)用匯編語言函數(shù)get_rand() */
/* 注意工程必需包含匯編語言源文件 "asm_source.s43" */
/**************************************************************/
#include <MSP430x14x.h> /* 頭文件 */
extern unsigned long get_rand(unsigned char seed); /* 匯編語言函數(shù)原型聲明 */
/****************************************************************/
/* 主函數(shù) */
/****************************************************************/
void main( void )
{
unsigned char seed; /* 局部變量定義*/
unsigned long value;
// === 系統(tǒng)初始化 ==========================================
IFG1 = 0; /* 清除中斷標志1 */
WDTCTL = WDTPW+WDTHOLD; /* 停止看門狗 */
P1DIR = 0xff;
// === 系統(tǒng)初始化結(jié)束========================================
seed = 0x55;
value = get_rand(seed); /* 調(diào)用匯編語言函數(shù)get_rand()得到一個隨機數(shù) */
while(1); /*程序結(jié)束*/
}
// === 主程序結(jié)束 ==================================================
/******************************************************************/
/* 乘法子程序,供匯編語言函數(shù)調(diào)用 */
/******************************************************************/
unsigned long mult(int x , int y)
{
return (x *y); /*x乘y */
}
// === 乘法子程序結(jié)束 ================================================
4.1.2 匯編語言源程序
; ******************************************************************
; 文件名: asm_source.s43
; C語言和匯編語言混合編程,匯編語言源程序
; 這段源程序調(diào)用兩個C語言函數(shù),標準庫函數(shù)rand()和用戶自定義函數(shù)mult()
; *******************************************************************
#include "msp430x14x.h" ; 頭文件
NAME asmfile
EXTERN rand ; C語言標準庫函數(shù)rand()
EXTERN mult ; c_source.c中用戶自定義函數(shù)
;====================================================================
; get_rand
;====================================================================
PUBLIC get_rand ; 導(dǎo)出函數(shù)名給C語言函數(shù)
RSEG CODE
get_rand;
push R11 ; 普通寄存器入棧保護
mov.b R12,R11 ; C 函數(shù)傳遞的實參在R12中,送入R16暫存
Call #rand ; 調(diào)用 C 函數(shù) rand()
; 函數(shù)值為整型返回在R12中
; rand()函數(shù)值作為mult()函數(shù)的第一實參
; 送入R12進行參數(shù)傳遞
mov R11,R14 ; C 函數(shù)傳遞的實參作為mult()函數(shù)的第二實參
; 送入R14進行參數(shù)傳遞
Call #mult ; mult()值返回在 R12 / R13寄存器對
pop R11 ; 出?;謴?fù)寄存器內(nèi)容
ret
END
4.2 匯編語言編寫中斷服務(wù)程序
為了提高整個系統(tǒng)響應(yīng)速度,要求中斷服務(wù)程序的執(zhí)行時間較短,執(zhí)行速度較快,因此最好的方法就是用匯編語言編寫中斷服務(wù)程序。但要注意:1、中斷服務(wù)程序不能有參數(shù)傳遞和返回值。2、中斷服務(wù)程序中所有被用到的寄存器都需要保護。本示例用匯編語言編寫了看門狗定時器的中斷服務(wù)程序,用C語言編寫了主程序。
4.2.1 C語言主程序
/********************************************************************/
/* 文件名:c_main.c 2003-01-08 */
/* C語言和匯編語言混合編程,C源程序 */
/* 這段源程序被看門狗定時器中斷后執(zhí)行匯編語言函數(shù)編寫的中斷服務(wù)程序 */
/* 注意工程必需包含匯編語言源文件 "wdt_int.s43" */
/********************************************************************/
#include <MSP430x14x.h> /* 頭文件 */
/********************************************************************/
/*主函數(shù) */
/********************************************************************/
void main( void )
{
// === 系統(tǒng)初始化 =============================================
IFG1=0; /* 清除中斷標志1 */
WDTCTL=WDT_MDLY_32; /* 看門狗的定時間隔為 32ms */
P1DIR = 0x01; /* P1.0 設(shè)置為輸出 */
IFG1 &= ~WDTIFG; /* 清除已掛起的看門狗定時器中斷 */
IE1 |= WDTIE; /* 允許看門狗定時器中斷 */
_EINT();
// === 系統(tǒng)初始化結(jié)束===========================================
while(1); /*主程序是一段死循環(huán)
}
// === 主函數(shù)結(jié)束 ==============================================
4.2.2 匯編語言中斷服務(wù)程序
;**********************************************************************
; 文件名: wdt_int.s43
; C語言和匯編語言混合編程,匯編語言源程序
; 看門狗定時器中斷服務(wù)程序
;***********************************************************************
NAME WDT_ISR
#include "msp430x14x.h" ; 頭文件
; ==============================================================
; 看門狗定時器中斷服務(wù)程序
;================================================================
PUBLIC wdt_isr ; 導(dǎo)出函數(shù)名給C語言函數(shù)
RSEG CODE
wdt_isr
xor.b #001h,&P1OUT ; 觸發(fā) P1.0,led 亮滅轉(zhuǎn)換
reti ; 中斷返回
;================================================================
COMMON INTVEC(1) ; 中斷向量段
;================================================================
ORG WDT_VECTOR
DW wdt_isr
END
5 結(jié)束語
以上方法已用于筆者的實際項目,取得良好效果,但是要注意編譯器的某些選項對程序生成代碼是有影響的。例如:匯編語言函數(shù)對標號大小寫敏感與否,影響C語言函數(shù)的變量名、程序名。若使用ROM MONTIOR,則C編譯器要用-ur45選項編譯,并且匯編語言中只要使用R4和R5,都要加以保護,否則無法返回ROM MONTIOR。
參考文獻
[1] IAR MSP430 C Compiler Programming Guide
[2] IAR MSP430 Assembler, Linker and Librarian Programming Guide
[3] MSP430x3xx Family User’s Guide, literature number SLAU012
[4] MSP430x1xx Family User’s Guide, literature number SLAU049
[5] MSP430x4xx Family User’s Guide, literature number SLAU056