能讓彙編轉到C51的初學者有更清晰的認識的一篇文章()

 

8051 是傳統 CISC 架構微控制器的代表,而 PIC 則是現代 RISC 架構微控制器的佳作。
雖然說 RISC 架構是公認未來微控制器的主流,但是 8051 藉著累積多年的發展環境與資

源,特別是 C Compiler 的成熟,在未來仍然有相當大的想像空間。

希望這是一本可以活用的互動式電子書,以討論及分享 KEIL C51 的程式設計經驗為目的。

目前的內容有


記憶體

8051
特有的記憶體型態

特殊資料型態

指定絕對位址的變數

隱藏的初始化程序


程式範例


軟體模擬的 Single Master I 2C 介面程式


下載 KEIL C51 試用版


8051
特有的記憶體型態



code    
MOVC @A+DPTR 讀取的程式記憶體

data    
可以直接存取的內部資料記憶體

idata    
Mov @Rn 存取的內部資料記憶體

bdata    
可以位元定址(Bit Addressable)的內部記憶體

xdata    
MOVX @DPTR 存取的外部資料記憶體

pdata    
MOVX @Rn 存取的外部資料記憶體


特殊資料型態



bit    
一般位元(bit)變數


sbit    
絕對定址的位元(bit)變數


語法

sbit    my_flag    =    location;    
location 範圍從 0x00 ~ 0x 7F


範例

sbit    EA =         0xAF;

或是配合 bdata 宣告的位元(bit)變數


char    bdata        my_flags;
sbit    flag0 =              my_flags ^ 0;

(注意 sbit 前不可以加 static


sfr    
特殊功能暫存器(Special Function Register


語法

sfr    my_sfr    =    location;    
location 範圍從 0x80 ~ 0xFF


範例

sfr    P0    =    0x80;

指定絕對位址的變數



在單一模組內可以使用下面的語法宣告


[memory_space]    type    variable_name    _at_    location

範例

pdata        char    my_pdata    _at_    0x80;

如果該變數必須為多個模組所使用(Global Variable)則以


抽象指標(Abstract Pointer)的方式在標頭檔(Header File)定義較為方便。


#define    variable_name    *((data_type *)        location)

範例

#define    my_pdata    *((char pdata *)    0x80)

(注意 char pdata 的順序)


ABSACC.H
提供了下列方便的巨集(Macro)定義。


#define CBYTE ((unsigned char volatile code *) 0)
#define DBYTE ((unsigned char volatile data *) 0)
#define PBYTE ((unsigned char volatile pdata *) 0)
#define XBYTE ((unsigned char volatile xdata *) 0)
#define CWORD ((unsigned int volatile code *) 0)
#define DWORD ((unsigned int volatile data *) 0)
#define PWORD ((unsigned int volatile pdata *) 0)
#define XWORD ((unsigned int volatile xdata *) 0)

隱藏的初始化程序


80C 51
在電源重置後(Power On Reset)所執行的第一個程式模組並不是使用者的主程式

main()
,而是一個隱藏在 KEIL-C51 標準程式庫中稱為 startup.a51 的程式模組。

startup.a51
的主要工作是把包含 idataxdatapdata 在內的記憶體區塊清除為 0,並

且初始化遞迴指標。接著 startup.a51 被執行的仍然是一個隱藏在 KEIL-C51 標準程式庫

中稱為 init.a51 的程式模組。而 init.a51 的主要工作則是初始化具有非零初始值設定的

變數。


在完成上述的初始化程序之後,80C51 的控制權才會交給 main() 開始執行使用者的程式。

研究在 C51Lib 目錄下相關模組的組合語言程式碼,使用者將會對 KEIL-C51 的架構有進

一步的了解,同時更能掌握不同的高階應用技巧。


程式範例


軟體模擬的 Single Master I 2C 介面程式:
C51-I 2C .C

暫存器庫(Register Bank)切換的應用


暫存器庫(Register Bank)切換的最大應用是在中斷程序的處理。一般的軟體設計會在程

式進入中斷之後,利用切換暫存器庫的方式保持主程式 R0 ~ R7 暫存器的內容,不受中斷

程序的影響而改變。

 

 volatile 實例講解

volatile
的本意是一般有兩種說法--1.“暫態的2.“易變的
這兩種說法都有可行。但是究竟volatile是什麼意思,現舉例說明(以Keil-ca51為例
例子來自Keil FQA,看完例子後你應該明白volatile的意思了,如果還不明白,那只好
再看一遍了。


1.

void main (void)
{
volatile int i;
int j;

i = 1;  //1  
不被優化
i=1
i = 2;  //2  
不被優化
i=1
i = 3;  //3  
不被優化
i=1

j = 1;  //4  
被優化

j = 2;  //5  
被優化
j = 3;  //6  j = 3
}
---------------------------------------------------------------------
2.

函數:


void func (void)
{
unsigned char xdata xdata_junk;
unsigned char xdata *p = &xdata_junk;
unsigned char t1, t2;

t1 = *p;
t2 = *p;
}

編譯的彙編為:

0000 7E00    R     MOV     R6,#HIGH xdata_junk
0002 7F 00    R     MOV     R7,#LOW xdata_junk
;---- Variable 'p' assigned to Register 'R6/R7' ----

0004 8F 82          MOV     DPL,R7
0006 8E83          MOV     DPH,R6

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
注意
0008 E0            MOVX    A,@DPTR
0009 F 500    R     MOV     t1,A

000B F500    R     MOV     t2,A
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
000D 22            RET     

將函數變為:
void func (void)
{
volatile unsigned char xdata xdata_junk;
volatile unsigned char xdata *p = &xdata_junk;
unsigned char t1, t2;

t1 = *p;
t2 = *p;
}

編譯的彙編為:
0000 7E00    R     MOV     R6,#HIGH xdata_junk
0002 7F 00    R     MOV     R7,#LOW xdata_junk
;---- Variable 'p' assigned to Register 'R6/R7' ----

0004 8F 82          MOV     DPL,R7
0006 8E83          MOV     DPH,R6

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
0008 E0            MOVX    A,@DPTR
0009 F 500    R     MOV     t1,A        ;a


000B E0            MOVX    A,@DPTR
000C F500    R     MOV     t2,A
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

000E 22            RET     


比較結果可以看出來,未用volatile關鍵字時,只從*p所指的位址讀一次
如在a*p的內容有變化,則t2得到的則不是真正*p的內容。

---------------------------------------------------------------------
3


volatile unsigned char bdata var;  // use volatile keyword here
sbit var_0 = var^0;
sbit var_1 = var^1;
unsigned char xdata values[10];

void main (void)  {
  unsigned char i;

  for (i = 0; i < sizeof (values); i++)  {
    var = values[i];
    if (var_0)  {
      var_1 = 1; //a

      
      values[i] = var;  // without the volatile keyword, the compiler
                        // assumes that 'var' is unmodified and does not
                        // reload the variable content.
    }
  }
}


在此例中,如在a處到下一句運行前,var如有變化則不會,如var=0xff; 則在
values[i] = var;
得到的還是values[i] = 1;

---------------------------------------------------------------------
應用舉例:


1.
#define DBYTE ((unsigned char volatile data  *) 0)

說明:此處不用volatile關鍵字,可能得不到真正的內容。

---------------------------------------------------------------------

2.


#define TEST_VOLATILE_C

//***************************************************************
// verwendete Include Dateien
//***************************************************************
#if __C51__ < 600
  #error: !! Keil
版本不正確

#endif

//***************************************************************
//
函數 void v_IntOccured(void)
//***************************************************************
extern void v_IntOccured(void);

//***************************************************************
//
變數定義

//***************************************************************
char xdata cValue1;          //
全局xdata
char volatile xdata cValue2; //
全局
xdata

//***************************************************************
//
函數:
v_ExtInt0()
//
版本:

//
參數:
//
用途:cValue1++,cValue2++
//***************************************************************
void v_ExtInt0(void) interrupt 0 {
  cValue1++;
  cValue2++;
}

//***************************************************************
//
函數:
main()
//
版本:

//
參數:
//
用途:測試volatile
//***************************************************************

void main() {
char cErg;

//1.
使
cErg=cValue1;
cErg = cValue1;

//2.
在此處仿真時手動產生中斷INT0,使
cValue1++; cValue2++
if (cValue1 != cErg)
  v_IntOccured();

//3.
使
cErg=cValue2;
cErg = cValue2;

//4.
在此處仿真時手動產生中斷INT0,使
cValue1++; cValue2++
if (cValue2 != cErg)
  v_IntOccured();
  
//5.
完成

  while (1);
}

//***************************************************************
//
函數: v_IntOccured()
//
版本:

//
參數:
//
用途: 閉環
//***************************************************************
void v_IntOccured() {
  while(1);
}


仿真可以看出,在沒有用volatile時,即2處,程式不能進入v_IntOccured();
但在4處可以進入
v_IntOccured();


toprom200 / Xuite日誌 / 回應(0) / 引用(0)
沒有上一則|日誌首頁|沒有下一則