一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Keil C51中變量和函數(shù)的絕對地址定位問題:

 昵稱5169677 2011-05-09

Keil C51中變量和函數(shù)的絕對地址定位問題:

 

1.  變量絕對地址定位

       1)    在定義變量時使用 _at_ 關鍵字加上地址就可.

              e.g.

                     unsigned char idata myvar _at_ 0x40;

              把變量 myvar 定義在 idata 0x40 , M51 文件中可以找到這麼一行

            IDATA   0040H     0001H     ABSOLUTE    

              表示有變量在 idata 0x0040 處絕對地址定位.

       2)    使用 KeilC 編譯器定義絕對地址的變量, 方法待查.

 

2.  函數(shù)絕對地址定位

       1)    在程序中編寫一函數(shù) myTest

              void myTest(void)

              {

                     // Add your code here

              }

       2)    使用 KeilC 編譯器定位絕對地址的函數(shù), 打開 Project -> Options for Target 菜單,

              選中 BL51 Locate 選項卡, Code: 中輸入:

              PR?myTest?MAIN(0x4000)

              把函數(shù) myTest 定位到程序區(qū)的 0x4000 ,

              再次編譯就可以了.

       3)    一次定位多個函數(shù)的方法

              同樣地, 在程序中再編寫另外一個函數(shù) myTest1

              void myTest1(void)

              {

                     // Add your code here

              }

              Options for Target 菜單的 BL51 Locate 選項卡的 Code: 中輸入:

              PR?myTest1?MAIN(0x3900), ?PR?myTest?MAIN(0x4000)

              把函數(shù) myTest1 定位在程序區(qū)的 0x3900 , 把函數(shù) myTest 定義在程序區(qū)的 0x4000 ,

              重新編譯就可以了.

              M51 文件中可以找到下面的內(nèi)容:

 

              >> 3.obj TO Reader RAMSIZE (256) CODE (?PR?MYTEST1?MAIN (0X3900), ?PR?MYTEST?MAIN (0X4000))

 

                    3665H     029BH                  *** GAP ***

            CODE    3900H     0014H     UNIT         PR?MYTEST1?MAIN

                    3914H     06ECH                  *** GAP ***

            CODE    4000H     0014H     UNIT         PR?MYTEST?MAIN

       4)    函數(shù)的調用:

              程序中直接調用函數(shù)的方式就不說明了, 這里重點講使用函數(shù)指針調用絕對地址處的函數(shù)的方法.

              (1)   定義調用的函數(shù)原形

                     typedef void (*CALL_MYTEST)(void);

              這是一個回調函數(shù)的原形, 參數(shù)為空.

              (2)   定義相應的函數(shù)指針變量

                     CALL_MYTEST    myTestCall = NULL;

              (3)   函數(shù)指針變量賦值, 指向我們定位的絕對地址的函數(shù)

                     myTestCall = 0x3900;

              指向函數(shù) myTest1

              (4)   函數(shù)指針調用

                     if (myTestCall != NULL)

                     {

                            myTestCall();                // 調用函數(shù)指針處的函數(shù) myTest1, PC 指針為 0x3900

                     }

              檢查編譯生成的 bin 文件, 0x3900 處可以看到 myTest1 的內(nèi)容, 0x4000 處可以看到 myTest 的內(nèi)容,

              (5)   其它說明:

                     如果在 0x3000 0x3900 的程序空間沒有內(nèi)容時, myTestCall 的地址指針指到 0x3800

                     ( 0x3000 0x3900 之間) , 會從 0x3900 處開始執(zhí)行.

              至於在 Load 中調用 AP 中的函數(shù)的方法與此類似, 但是相應的參數(shù)傳遞可能要另尋方法.

 

************************************

 

keil C51絕對地址訪問

 

在利用keil進行8051單片機編程的時,常常需要進行絕對地址進行訪問.特別是對硬件操作,DA AD 采樣 ,LCD 液晶操作,打印操作.等等.
C51
提供了三種訪問絕對地址的方法:
1.
絕對宏:
在程序中,用include<absacc.h>”即可使用其中定義的宏來訪問絕對地址,包括:
CBYTE
、XBYTEPWORD、DBYTECWORD、XWORDPBYTE、DWORD
具體使用可看一看absacc.h便知

例如:
rval=CBYTE[0x0002];
指向程序存貯器的0002h地址
rval=XWORD [0x0002];
指向外RAM0004h地址
2. _at_
關鍵字
直接在數(shù)據(jù)定義后加上_at_ const即可,但是注意:
(1)
絕對變量不能被初使化;
(2)bit
型函數(shù)及變量不能用_at_指定。

例如:

idata struct link list _at_ 0x40;
指定list結構從40h開始。
xdata char text[25b] _at_0xE000
;指定text數(shù)組從0E000H開始
提示:如果外部絕對變量是I/O端口等可自行變化數(shù)據(jù),需要使用volatile關鍵字進行描述,請參考absacc.h。

3.
連接定位控制
此法是利用連接控制指令code xdata pdata \data bdata地址進行,如要指定某具體變量地址,則很有局限性,不作詳細討論。

:(c51)


/*--------------------------------------------------------------------------
ABSACC.H

Direct access to 8051, extended 8051 and Philips 80C51MX memory areas.
Copyright (c) 1988-2002 Keil Elektronik GmbH and Keil Software, Inc.
All rights reserved.
--------------------------------------------------------------------------*/

#ifndef __ABSACC_H__
#define __ABSACC_H__

#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)


#ifdef __CX51__
#define FVAR(object, addr) (*((object volatile far *) (addr)))
#define FARRAY(object, base) ((object volatile far *) (base))
#define FCVAR(object, addr) (*((object const far *) (addr)))
#define FCARRAY(object, base) ((object const far *) (base))
#else
#define FVAR(object, addr) (*((object volatile far *) ((addr)+0x10000L)))
#define FCVAR(object, addr) (*((object const far *) ((addr)+0x810000L)))
#define FARRAY(object, base) ((object volatile far *) ((base)+0x10000L))
#define FCARRAY(object, base) ((object const far *) ((base)+0x810000L))
#endif

#endif

:(c166)

/*--------------------------------------------------------------------------
ABSACC.H

Direct access to 166 memory areas for C166/EC++ Version 5.
Copyright (c) 1992-2004 Keil Elektronik GmbH and Keil Software, Inc.
All rights reserved.
--------------------------------------------------------------------------*/

#ifndef __ABSACC_H__
#define __ABSACC_H__

#if (__MODEL__ == 0)
#define MVAR(object, addr) (*((object volatile *) (addr)))
#define MARRAY(object, base) ((object volatile *) (base))
#else
#define MVAR(object, addr) (*((object volatile far *) (addr)))
#define MARRAY(object, base) ((object volatile far *) (base))

#define HVAR(object, addr) (*((object volatile huge *) (addr)))
#define HARRAY(object, base) ((object volatile huge *) (base))
#define XVAR(object, addr) (*((object volatile xhuge *) (addr)))
#define XARRAY(object, base) ((object volatile xhuge *) (base))
#endif

#endif

 

以下來自轉載:

使用KeilC51軟件,可以很方便地將代碼或者數(shù)據(jù)絕對定位到某個地址。
1
、代碼定位:
方法1:使用偽指令CSEG。比如要將MyFunc1定位到代碼區(qū)C:0x1000,則新建一個A51文件,添加以下內(nèi)容:
PUBLIC MYFUNC1
CSEG AT 1000H
MYFUNC1:
;
其它代碼
RET
在其它源文件中,就可以調用MyFunc()函數(shù)了。需要注意的是,編譯器不檢測傳遞參數(shù)的數(shù)目,僅檢測函數(shù)是否有返回值。
方法2:使用BL51 Locate選項。比如在main.c中定義了一個MyFunc2函數(shù),并且要將該函數(shù)定位到代碼區(qū)C:0x2000,則從菜單中選擇Project->Options for Target 'Target1',在彈出的對話框中選擇BL51 Locate頁,在下面的code欄中寫上PR?MYFUNC2?MAIN(0x2000)即可。
如果想定位多個函數(shù),也可以使用*通配符。
2
、變量定位:
只有全局變量可以絕對定位,局部變量無法實現(xiàn)絕對定位。
方法1:使用_at_關鍵字。聲明一個全局變量unsigned char data MyBuf1[8] _at_ 0x20;
方法2:使用BL51 Locate選項。比如將main.c中定義的所有data型的全局變量定位到數(shù)據(jù)區(qū)D:0x28開始的空間,則從菜單中
選擇Project->Options for Target 'Target1',在彈出的對話框中選擇BL51 Locate頁,在下面的data欄中寫上DT?MAIN(0x28)即可。
如果是idata,則使用ID?MAIN(0x28),如果是xdata,則使用XD?MAIN(0x28),如果是pdata,則使用PD?MAIN(0x28)
3
、堆棧定位:
STARTUP.A51文件中定義了堆棧區(qū)STACK,其起始地址同樣可以在BL51 Locate頁中設置,在Stack欄寫上STACK(0x80)

BL51 locate
選項卡中

code range
xdata range如果不填寫,編譯默認將程序中相應代碼和變量從空間前面取起

網(wǎng)上看到有人提到在keil中使用_at_進行絕對地址定位問題,我簡單介紹一下它的用法。

使用_at_關鍵字對存儲器進行絕對地址定位程序如下

i nclude<reg51.h>

char xdata LED_Data[50] _at_ 0x8000;

main()

{

LED_Data[0] = 0x23;

}

keil中運行以上程序可以在存儲器窗口中輸入 x0x8000 可以看到0x8000地址中的值為0x23.

值得指出的幾點是

1.
在給變量LED_Data[50]定位絕對地址空間時,不能對其賦初值。

2.char xdata LED_Data[50] _at_ 0x8000;
這條語句不能主函數(shù)中。有些網(wǎng)友提到在按著keil說明中用_at_進行絕對地址定位時,編譯會出現(xiàn)錯誤274,就是將這條語句放在主函數(shù)中的原因。

3.keil
中地址是自動分配的,所以除非特殊情況否則不提倡使用絕對地址定位。初學者因帖別注意。不要把c當作匯編使用。

對需要/RST復位后要保持變量不變,防止意外改變(比如升級到新程序,變量地址可能被編譯器優(yōu)化到其他地方),比較有用?。。。?/span>


STARTUP.A51
這個文件有什么用,有必要添加到工程嗎?

如果不添加"startup.a51"文件,編譯器就會自動加入一段初始化內(nèi)存以及堆棧等的代碼,這時的內(nèi)存初始化部分你就無法去控制了,當然這在大部分情況下沒什么關系。但是如果你想你的程序在復位后,內(nèi)存里面的信息依然還保存著(所說的熱復位),那么你就需要添加該啟動文件,并且去里面修改內(nèi)存初始化部分,不要初始化你需要保留的部分內(nèi)存。

 

請問如何在keil編譯器里,編程時指定函數(shù)的絕對地址 (無內(nèi)容)

不好意思啊,我還從來沒有接觸過有這樣要求情況,不過從網(wǎng)上其他地方找了一篇你參考一下吧,、函數(shù)定位:
假如要把C源文件 tools.c 中的函數(shù)
int BIN2HEX(int xx)
{
  ...
}
放在CODE MEMORY0x1000處,先編譯該工程,然后打開該工程的M51文件,在
* * *   C O D E   M E M O R Y   * * *
行下找出要定位的函數(shù)的名稱,應該形如:
CODE    xxxxH     xxxxH     UNIT         ?PR?_BCD2HEX?TOOLS
然后在:
Project->Options for Target ...->BL51 Locate
Code
中填寫如下內(nèi)容:
PR?_BCD2HEX?TOOLS(0x1000)
再次Build,在M51中會發(fā)現(xiàn)該函數(shù)已放在CODE MEMORY0x1000處了
2
、賦初值的變量定位:
要將某變量定位在一絕對位置且要賦初值,此時用 _at_ 不能完成,則如下操作:
在工程中建立一個新的文件,如InitVars.c,在其中對要處理的變量賦初值(假設是code
量):
char code myVer = {"COPYRIGHT 2001-11"};
然后將該文件加入工程,編譯,打開M51文件,若定義的是code型,則在
* * *   C O D E   M E M O R Y   * * *
下可找到:
CODE    xxxxH     xxxxH     UNIT         ?CO?INITVARS
然后在:
Project->Options for Target ...->BL51 Locate
Code
中填入:
CO?INITVARS(0x200)
再次編譯即可。
相應地,如為xdata變量,則InitVars.c中寫:
char xdata myVer = {"COPYRIGHT 2001-11"};
然后將該文件加入工程,編譯,打開M51文件,在
* * *  X D A T A   M E M O R Y  * * *
下可找到:
XDATA   xxxxH     xxxxH     UNIT         ?XD?INITVARS
然后在:
Project->Options for Target ...->BL51 Locate
Xdata
中填入:
XD?INITVARS(0x200)
再次編譯即可。相應地,若定義的是data/idata等變量,則相應處理即可。
3
、若有多個變量或函數(shù)要進行絕對地址定位,則應按地址從低到高的順序排列。

**********************************

PIC 51 混編 C18指定數(shù)據(jù)絕對地址

 

51:

 

RSEG是段選擇指令,要想明白它的意思就要了解段的意思。

段是程序代碼或數(shù)據(jù)對象的存儲單位。程序代碼放到代碼段,數(shù)據(jù)對象放到數(shù)據(jù)段。段分兩種,一是絕對段,一是再定位段。絕對段在匯編語言中指定,在用L51聯(lián)接的時候,地址不會改變。用于如訪問一個固定存儲器的i/o,或提供中斷向量的入口地址。而再定位段的地址是浮動的。它的地址有L51對程序模塊連接時決定,C51對源程序編譯所產(chǎn)生的段都是再定位段,它都有段名和存儲類型。絕對段沒有段名。

說了這么多,大家可能還是不明白段是什么意思。別急,接著往下看。

例如,你寫用C寫了一個函數(shù) void test_fun(void) { } , 存在test.c中,用編譯器編譯以后. SRC FILE中看到:

 

   PR?test_fun?TEST SEGMENT CODE //(函數(shù)放到代碼段中)

 

    寫這個函數(shù)體的時候: RSEG ?PR?test_fun?TEST //選擇已定位的代碼段為當前段 test_fun:

 

     ……//代碼

所以函數(shù)的表達模式是這樣: PR?函數(shù)名文件名

而函數(shù)名又分: 1:無參函數(shù) PR?函數(shù)名文件名

2:有參函數(shù) PR?_函數(shù)名文件名

3:再入函數(shù) PR?_?函數(shù)名文件名

又例如 你定義了全局變量 unsigned char data temp1,temp2; unsigned char xdata temp3; test.c文件中,編譯器會為每個文件分0到多個全局數(shù)據(jù)段,相同類型的全局變量被存到同一段中。所以上面會編譯成如下:

RSEG ? DT? TEST

. temp1: DS 1

. temp2: DS 1

;

RSEG ?XD? TEST

. temp3: DS 1

//下面是各個類型的數(shù)據(jù)全局段的表示

CO? 文件名 //常數(shù)段

XD? FILE_NAME //XDATA 數(shù)據(jù)段

DT? FILE_NAME //DATA 數(shù)據(jù)段

ID? FILE_NAME //IDATA…..

BI? FILE_NAME // BIT …..

BA? FILE_NAME //BDATA….

PD? FILE_NAME //PDATA…..

看到這里大家應該明白段的意思了吧。也許你會問,這有什么作用哪?它就是用在當你需要用匯編語言寫一部份程序的時候,把匯編寫的函數(shù)放在這個問件中,改名xxx.a51,按上面的規(guī)則寫。

編譯就好。

既然知道了段的意思,現(xiàn)在我們回到SEG的用法上來。

A51中有兩種段選擇指令 再定位段選擇指令 絕對段選擇指令. 它們用來選擇當前段是再定位段還是絕對段。使用不同的段選擇指令,將使程序定位在不同的地址空間之內(nèi)。

 

1: 再定位段的選擇指令是: RSEG 段名

它用來選擇一個在前面已經(jīng)定義過的再定位段作為當前段。

用法就像我們上面的例子,先申明了一個函數(shù)段,后面寫這個函數(shù)段。

2: 絕對段選擇指令

CSEG [AT 絕對地址表達式] //絕對代碼段

DSEG [AT 絕對地址表達式] //內(nèi)部絕對數(shù)據(jù)段

XSEG [AT 絕對地址表達式] //外部絕對數(shù)據(jù)段

ISEG [AT 絕對地址表達式] //內(nèi)部間接尋址絕對數(shù)據(jù)段

BSEG [AT 絕對地址表達式] //絕對位尋址段

它們的用法我舉一個例子:

例如我們寫串口中斷程序,起始地址是0x23.就這樣寫

CSEG AT 0X23

LJMP serialISR

RSEG ?PR?serialISR?TEST

. serialISR:

 

 

PIC:

 

匯編函數(shù)使用同一個工程C文件中的變量,例如ICFLAGC文件中定義,則匯編文件中定義方式為

 

;定義外部變量

EXTERN ICFLAG

 

定義函數(shù)

 

例如

CARDATR:

 

...........

 

RET

 

 

GLOBAL CARDATR

 

 

在同一個工程文件下調用匯編中的函數(shù)CARDATR

 

則應該定義函數(shù)extern void CARDATR(void);

 

C18指定數(shù)據(jù)絕對地址

例如:

#pragma udata overlay RECBUFS =0x190 //200

UINT8 NUMBER;

UINT8 REC_BUF[31];

#pragma udata

 

************************************

**************************************8

標簽: KEIL  C51  編程  

KEIL C51高級編程

KEIL C51高級編程

第一節(jié) 絕對地址訪問
C51
提供了三種訪問絕對地址的方法:

1. 絕對宏:
在程序中,用“#include”即可使用其中定義的宏來訪問絕對地址,包括:

CBYTE、XBYTE、PWORD、DBYTE、CWORD、XWORD、PBYTE、DWORD

具體使用可看一看absacc.h便知

例如:

rval=CBYTE[0x0002];指向程序存貯器的0002h地址

rval=XWORD [0x0002];指向外RAM0004h地址

2. _at_關鍵字
直接在數(shù)據(jù)定義后加上_at_ const即可,但是注意:

(1)絕對變量不能被初使化;

(2)bit型函數(shù)及變量不能用_at_指定。

例如:

idata struct link list _at_ 0x40;指定list結構從40h開始。

xdata char text[25b] _at_0xE000;指定text數(shù)組從0E000H開始

提示:如果外部絕對變量是I/O端口等可自行變化數(shù)據(jù),需要使用volatile關鍵字進行描述,請參考absacc.h。

3. 連接定位控制
此法是利用連接控制指令code xdata pdata \data bdata對“段”地址進行,如要指定某具體變量地址,則很有局限性,不作詳細討論。

第二節(jié) Keil C51與匯編的接口
1.
模塊內(nèi)接口
方法是用#pragma語句具體結構是:

#pragma asm

匯編行

#pragma endasm

這種方法實質是通過asmndasm告訴C51編譯器中間行不用編譯為匯編行,因而在編譯控制指令中有SRC以控制將這些不用編譯的行存入其中。

2. 模塊間接口
C
模塊與匯編模塊的接口較簡單,分別用C51A51對源文件進行編譯,然后用L51obj文件連接即可,關鍵問題在于C函數(shù)與匯編函數(shù)之間的參數(shù)傳遞問題,C51中有兩種參數(shù)傳遞方法。

(1) 通過寄存器傳遞函數(shù)參數(shù)

最多只能有3個參數(shù)通過寄存器傳遞,規(guī)律如下表:

參數(shù)數(shù)目
char
int
long,float
一般指針

1

2

3
R7

R5

R3
R6 & R7

R4 & R5

R2 & R3
R4
R7

R4R7
R1
R3

R1R3

R1R3

(2) 通過固定存儲區(qū)傳遞(fixed memory)

這種方法將bit型參數(shù)傳給一個存儲段中:

?function_name?BIT

將其它類型參數(shù)均傳給下面的段:?function_name?BYTE,且按照預選順序存放。

至于這個固定存儲區(qū)本身在何處,則由存儲模式默認。

(3) 函數(shù)的返回值

函數(shù)返回值一律放于寄存器中,有如下規(guī)律:

return type
Registev
說明

bit
標志位
由具體標志位返回

char/unsigned char 1_byte
指針
R7
單字節(jié)由R7返回

int/unsigned int 2_byte
指針
R6 & R7
雙字節(jié)由R6R7返回,MSBR6

long&unsigned long
R4
R7
MSB
R4, LSBR7

float
R4
R7
32Bit IEEE
格式

一般指針
R1
R3
存儲類型在R3 高位R2 R1

(4) SRC控制

該控制指令將C文件編譯生成匯編文件(.SRC),該匯編文件可改名后,生成匯編.ASM文件,再用A51進行編譯。

第三節(jié) Keil C51軟件包中的通用文件
C51\LiB目錄下有幾個C源文件,這幾個C源文件有非常重要的作用,對它們稍事修改,就可以用在自己的專用系統(tǒng)中。

1. 動態(tài)內(nèi)存分配
init_mem.C
:此文件是初始化動態(tài)內(nèi)存區(qū)的程序源代碼。它可以指定動態(tài)內(nèi)存的位置及大小,只有使用了init_mem( )才可以調回其它函數(shù),諸如malloc calloc,realloc等。

calloc.c:此文件是給數(shù)組分配內(nèi)存的源代碼,它可以指定單位數(shù)據(jù)類型及該單元數(shù)目。

malloc.c:此文件是malloc的源代碼,分配一段固定大小的內(nèi)存。

realloc.c:此文件是realloc.c源代碼,其功能是調整當前分配動態(tài)內(nèi)存的大小。

2. C51啟動文件STARTUP.A51
啟動文件STARTUP.A51中包含目標板啟動代碼,可在每個project中加入這個文件,只要復位,則該文件立即執(zhí)行,其功能包括:

l 定義內(nèi)部RAM大小、外部RAM大小、可重入堆棧位置

l 清除內(nèi)部、外部或者以此頁為單元的外部存儲器

l 按存儲模式初使化重入堆棧及堆棧指針

l 初始化8051硬件堆棧指針

l main( )函數(shù)交權

開發(fā)人員可修改以下數(shù)據(jù)從而對系統(tǒng)初始化

常數(shù)名 意義

IDATALEN 待清內(nèi)部RAM長度

XDATA START 指定待清外部RAM起始地址

XDATALEN 待清外部RAM長度

IBPSTACK 是否小模式重入堆棧指針需初始化標志,1為需要。缺省為0

IBPSTACKTOP 指定小模式重入堆棧頂部地址

XBPSTACK 是否大模式重入堆棧指針需初始化標志,缺省為0

XBPSTACKTOP 指定大模式重入堆棧頂部地址

PBPSTACK 是否Compact重入堆棧指針,需初始化標志,缺省為0

PBPSTACKTOP 指定Compact模式重入堆棧頂部地址

PPAGEENABLE P2初始化允許開關

PPAGE 指定P2

PDATASTART 待清外部RAM頁首址

PDATALEN 待清外部RAM頁長度

提示:如果要初始化P2作為緊湊模式高端地址,必須:PPAGEENAGLE1,PPAGEP2值,例如指定某頁1000H10FFH,則PPAGE10H,而且連接時必須如下:

L51

 

PDATA(1080H),其中1080H1000H10FFH中的任一個值。

以下是STARTUP.A51代碼片斷,紅色是經(jīng)常可能需要修改的地方:

;------------------------------------------------------------------------------

; This file is part of the C51 Compiler package

; Copyright KEIL ELEKTRONIK GmbH 1990

;------------------------------------------------------------------------------

; STARTUP.A51: This code is executed after processor reset.

;

; To translate this file use A51 with the following invocation:

;

; A51 STARTUP.A51

;

; To link the modified STARTUP.OBJ file to your application use the following

; L51 invocation:

;

; L51 , STARTUP.OBJ

;

;------------------------------------------------------------------------------

;

; User-defined Power-On Initialization of Memory

;

; With the following EQU statements the initialization of memory

; at processor reset can be defined:

;

; ; the absolute start-address of IDATA memory is always 0

IDATALEN EQU 80H ; the length of IDATA memory in bytes.

;

XDATASTART EQU 0H ; the absolute start-address of XDATA memory

XDATALEN EQU 0H ; the length of XDATA memory in bytes.

;

PDATASTART EQU 0H ; the absolute start-address of PDATA memory

PDATALEN EQU 0H ; the length of PDATA memory in bytes.

;

; Notes: The IDATA space overlaps physically the DATA and BIT areas of the

; 8051 CPU. At minimum the memory space occupied from the C51

; run-time routines must be set to zero.

;------------------------------------------------------------------------------

;

; Reentrant Stack Initilization

;

; The following EQU statements define the stack pointer for reentrant

; functions and initialized it:

;

; Stack Space for reentrant functions in the SMALL model.

IBPSTACK EQU 0 ; set to 1 if small reentrant is used.

IBPSTACKTOP EQU 0FFH+1 ; set top of stack to highest location+1.

;

; Stack Space for reentrant functions in the LARGE model.

XBPSTACK EQU 0 ; set to 1 if large reentrant is used.

XBPSTACKTOP EQU 0FFFFH+1; set top of stack to highest location+1.

;

; Stack Space for reentrant functions in the COMPACT model.

PBPSTACK EQU 0 ; set to 1 if compact reentrant is used.

PBPSTACKTOP EQU 0FFFFH+1; set top of stack to highest location+1.

;

;------------------------------------------------------------------------------

;

; Page Definition for Using the Compact Model with 64 KByte xdata RAM

;

; The following EQU statements define the xdata page used for pdata

; variables. The EQU PPAGE must conform with the PPAGE control used

; in the linker invocation.

;

PPAGEENABLE EQU 0 ; set to 1 if pdata object are used.

PPAGE EQU 0 ; define PPAGE number.

;

;------------------------------------------------------------------------------

3. 標準輸入輸出文件
putchar.c

putchar.c是一個低級字符輸出子程,開發(fā)人員可修改后應用到自己的硬件系統(tǒng)上,例如向CLDLEN輸出字符。

缺?。?/span>putchar.c是向串口輸出一個字符XON|XOFF是流控標志,換行符“\*n”自動轉化為回車/換行“\r\n”。

getkey.c

getkey函數(shù)是一個低級字符輸入子程,該程序可用到自己硬件系統(tǒng),如矩陣鍵盤輸入中,缺省時通過串口輸入字符。 4. 其它文件
還包括對Watch-Dog有獨特功能的INIT.A51函數(shù)以及對8×C751適用的函數(shù),可參考源代碼。

第四節(jié) 段名協(xié)定與程序優(yōu)化
1.
段名協(xié)定(Segment Naming Conventions)
C51
編譯器生成的目標文件存放于許多段中,這些段是代碼空間或數(shù)據(jù)空間的一些單元,一個段可以是可重定位的,也可以是絕對段,每一個可重定位的段都有一個類型和名字,C51段名有以下規(guī)定:

每個段名包括前綴與模塊名兩部分,前綴表示存儲類型,模塊名則是被編譯的模塊的名字,例如:

?COmain1 :表示main1模塊中的代碼段中的常數(shù)部分

?PR?function1?module module模塊中函數(shù)function1的可執(zhí)行段,具體規(guī)定參閱手冊。

2. 程序優(yōu)化
C51
編譯器是一個具有優(yōu)化功能的編譯器,它共提供六級優(yōu)化功能。確保生成目標代碼的最高效率(代碼最少,運行速度最快)。具體六級優(yōu)化的內(nèi)容可參考幫助。

C51中提供以下編譯控制指令控制代碼優(yōu)化:

OPTIMIZE(SJXE):盡量采用子程序,使程序代碼減少。

NOAREGS:不使用絕對寄存器訪問,程序代碼與寄存器段獨立。

NOREGPARMS:參數(shù)傳遞總是在局部數(shù)據(jù)段實現(xiàn),程序代碼與低版本C51兼容。

OPTIMIZE(SIZE)AK OPTIMIZE(speed)提供6級優(yōu)化功能,缺省為: OPTIMIZE(6,SPEED)

 

第五節(jié)  Keil C51的代碼效率

一、存儲模式的影響
存儲模式?jīng)Q定了缺省變量的存儲空間,而訪問各空間變量的匯編代碼的繁簡程度決定了代碼率的高低。

例如:一個整形變量i,如放于內(nèi)存18H19H空間,則++i的操作編譯成四條語句:

INC 0x19

MOV A,0x19

JNZ 0x272D

INC 0x18

0x272D

而如果放于外存空間0000H0001H++i的操作編譯成九條語句:

MOV DPTR,0001

MOVX A@ DPTR

INC A

MOVX @ DPTR,A

JNz #5

MOV OPTR,#0000

MOVX A,@DPTR

INC A

MOVX @ DPTR,A

就匯編之后的語句而言,對外部存儲器的操作較內(nèi)部存儲器操作代碼率要低得多,生成的語句為內(nèi)存的兩倍以上,而程序中有大量的這種操作,可見存儲模式對代碼率的響了。

因此程序設計的原則是

1、存儲模式從small-Compact-large依次選擇,實在是變量太多,才選large模式。

2、即使選擇了large模式,對一些常用的局部的或者可放于內(nèi)存中的變量,最好放于內(nèi)存中,以盡量提高程序的代碼率。

二、 程序結構的影響
程序的結構單元包括模塊、函數(shù)等等。同樣的功能,如果結構越復雜,其所涉及的操作、變量、功能模塊函數(shù)等就越多,較之結構性好,代碼簡單的程序其代碼率自然就低得多。

此外程序的運行控制語句,也是影響代碼率的關鍵因素,例如:switch -case語句,許多編譯器都把它們譯得非常復雜,Keil C51也不例外,相對較為簡易的Switch-case語句,編譯成跳轉指令形式,代碼率較高,但對較為復雜的Switch-Case,則要調用一個系統(tǒng)庫函數(shù)C?ICASE進行處理,非常復雜。

再如if( ),while( ),等語句也是代碼相對較低的語句,但編譯以后比switch-case要高得多。

因此建議設計者盡量少用switch-case之類語句來控制程序結構,以提高代碼率。

除以上兩點外,其它因素也會對代碼率產(chǎn)生影響,例如:

是否用寄存器傳遞參數(shù) NOAREGS選項是否有

是否包括調試信息:即DEBUG選項

是否包括擴展的調試信息:即BJECTEXTEND

 

第六節(jié) 如何優(yōu)化C語言代碼(程序員必讀)

1、選擇合適的算法和數(shù)據(jù)結構
應該熟悉算法語言,知道各種算法的優(yōu)缺點,具體資料請參見相應的參考資料,有
很多計算機書籍上都有介紹。將比較慢的順序查找法用較快的二分查找或亂序查找
法代替,插入排序或冒泡排序法用快速排序、合并排序或根排序代替,都可以大大
提高程序執(zhí)行的效率。.選擇一種合適的數(shù)據(jù)結構也很重要,比如你在一堆隨機存
放的數(shù)中使用了大量的插入和刪除指令,那使用鏈表要快得多。
數(shù)組與指針語句具有十分密碼的關系,一般來說,指針比較靈活簡潔,而數(shù)組則比
較直觀,容易理解。對于大部分的編譯器,使用指針比使用數(shù)組生成的代碼更短,
執(zhí)行效率更高。但是在Keil中則相反,使用數(shù)組比使用的指針生成的代碼更短。。


2
、使用盡量小的數(shù)據(jù)類型
能夠使用字符型(char)定義的變量,就不要使用整型(int)變量來定義;能夠使用
整型變量定義的變量就不要用長整型(long int),能不使用浮點型(float)變量就
不要使用浮點型變量。當然,在定義變量后不要超過變量的作用范圍,如果超過變
量的范圍賦值,C編譯器并不報錯,但程序運行結果卻錯了,而且這樣的錯誤很難
發(fā)現(xiàn)。
ICCAVR中,可以在Options中設定使用printf參數(shù),盡量使用基本型參數(shù)(%c、
%d
、%x、%X、%u%s格式說明符),少用長整型參數(shù)(%ld%lu、%lx%lX格式說明
),至于浮點型的參數(shù)(%f)則盡量不要使用,其它C編譯器也一樣。在其它條件不
變的情況下,使用%f參數(shù),會使生成的代碼的數(shù)量增加很多,執(zhí)行速度降低。

3
、使用自加、自減指令
通常使用自加、自減指令和復合賦值表達式(a-=1a+=1)都能夠生成高質量的
程序代碼,編譯器通常都能夠生成incdec之類的指令,而使用a=a+1a=a-1之類
的指令,有很多C編譯器都會生成二到三個字節(jié)的指令。在AVR單片適用的ICCAVR、
GCCAVR
IARC編譯器以上幾種書寫方式生成的代碼是一樣的,也能夠生成高質量
incdec之類的的代碼。

4
、減少運算的強度
可以使用運算量小但功能相同的表達式替換原來復雜的的表達式。如下:
(1)
、求余運算。
    a=a%8;
可以改為:
    a=a&7;
說明:位操作只需一個指令周期即可完成,而大部分的C編譯器的“%”運算均是調
用子程序來完成,代碼長、執(zhí)行速度慢。通常,只要求是求2n方的余數(shù),均可使用
位操作的方法來代替。

(2)
、平方運算
    a=pow(a,2.0);
可以改為:
    a=a*a;
說明:在有內(nèi)置硬件乘法器的單片機中(51系列),乘法運算比求平方運算快得多
,因為浮點數(shù)的求平方是通過調用子程序來實現(xiàn)的,在自帶硬件乘法器的AVR單片
機中,如ATMega163中,乘法運算只需2個時鐘周期就可以完成。既使是在沒有內(nèi)置
硬件乘法器的AVR單片機中,乘法運算的子程序比平方運算的子程序代碼短,執(zhí)行
速度快。
如果是求3次方,如:
    a=pow(a,3.0);
更改為:
    a=a*a*a
;
則效率的改善更明顯。

(3)
、用移位實現(xiàn)乘除法運算
    a=a*4;
    b=b/4;
可以改為:
    a=a<<2;
    b=b>>2;
說明:通常如果需要乘以或除以2n,都可以用移位的方法代替。在ICCAVR中,如果
乘以2n,都可以生成左移的代碼,而乘以其它的整數(shù)或除以任何數(shù),均調用乘除法
子程序。用移位的方法得到代碼比調用乘除法子程序生成的代碼效率高。實際上,
只要是乘以或除以一個整數(shù),均可以用移位的方法得到結果,如:
    a=a*9
可以改為:
    a=(a<<3)+a

5
、循環(huán)
(1)
、循環(huán)語
對于一些不需要循環(huán)變量參加運算的任務可以把它們放到循環(huán)外面,這里的任務包
括表達式、函數(shù)的調用、指針運算、數(shù)組訪問等,應該將沒有必要執(zhí)行多次的操作
全部集合在一起,放到一個init的初始化程序中進行。

(2)
、延時函數(shù):
通常使用的延時函數(shù)均采用自加的形式:
    void delay (void)
    {
unsigned int i;
    for (i=0;i<1000;i++)
    ;
    }
將其改為自減延時函數(shù):
    void delay (void)
    {
unsigned int i;
        for (i=1000;i>0;i--)
    ;
    }
兩個函數(shù)的延時效果相似,但幾乎所有的C編譯對后一種函數(shù)生成的代碼均比前一
種代碼少1~3個字節(jié),因為幾乎所有的MCU均有為0轉移的指令,采用后一種方式能
夠生成這類指令。
在使用while循環(huán)時也一樣,使用自減指令控制循環(huán)會比使用自加指令控制循環(huán)生
成的代碼更少1~3個字母。
但是在循環(huán)中有通過循環(huán)變量“i”讀寫數(shù)組的指令時,使用預減循環(huán)時有可能使
數(shù)組超界,要引起注意。

(3)while
循環(huán)和dowhile循環(huán)
while循環(huán)時有以下兩種循環(huán)形式:
unsigned int i;
    i=0;
    while (i<1000)
    {
        i++;
   //
用戶程序
    }
或:
unsigned int i;
    i=1000;
    do
    i--;
    //
用戶程序
    while (i>0);
在這兩種循環(huán)中,使用dowhile循環(huán)編譯后生成的代碼的長度短于while循環(huán)。

6
、查表
在程序中一般不進行非常復雜的運算,如浮點數(shù)的乘除及開方等,以及一些復雜的
數(shù)學模型的插補運算,對這些即消耗時間又消費資源的運算,應盡量使用查表的方
式,并且將數(shù)據(jù)表置于程序存儲區(qū)。如果直接生成所需的表比較困難,也盡量在啟
了,減少了程序執(zhí)行過程中重復計算的工作量。

7
、其它
比如使用在線匯編及將字符串和一些常量保存在程序存儲器中,均有利于優(yōu)化

*************************************

 

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    国产免费黄片一区二区| 日本高清视频在线观看不卡| 国产对白老熟女正在播放| 91日韩欧美国产视频| 国产在线日韩精品欧美| 欧美日韩国产福利在线观看| 韩日黄片在线免费观看| 亚洲精品中文字幕一二三| 日韩性生活片免费观看| 很黄很污在线免费观看| 亚洲熟女少妇精品一区二区三区| 国产精品内射视频免费| 小草少妇视频免费看视频| 久久精品伊人一区二区| 伊人天堂午夜精品草草网| 国产日韩中文视频一区| 冬爱琴音一区二区中文字幕| 天堂网中文字幕在线视频| 日韩一区二区三区嘿嘿| 国产又爽又猛又粗又色对黄| 91在线国内在线中文字幕| 国产日韩欧美国产欧美日韩| 天堂热东京热男人天堂| 国产成人精品综合久久久看| 久久福利视频视频一区二区| 欧美日本亚欧在线观看| 色婷婷人妻av毛片一区二区三区| 国产一区二区精品丝袜| 亚洲国产婷婷六月丁香| 亚洲人妻av中文字幕| 国产高清一区二区白浆| 国产免费观看一区二区| 国产内射一级二级三级| 在线欧美精品二区三区| 中文字幕五月婷婷免费| 中文字幕高清不卡一区| 亚洲精品福利视频你懂的| 亚洲av首页免费在线观看| 日本熟女中文字幕一区| 欧美日韩黑人免费观看| 欧美乱视频一区二区三区 |