C51中变量和函数的绝对地址定位问题.._第1页
C51中变量和函数的绝对地址定位问题.._第2页
免费预览已结束,剩余28页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、C51中变量和函数的绝对地址定位问题1. 变量绝对地址定位1)在定义变量时使用_at_关键字加上地址就可.e.g.un sig ned char idata myvar _at_ 0 x40;把变量 myvar 定义在 idata 的 0 x40 处,在 M51 文件中可以找到这麽 一行IDATA 0040H0001H ABSOLUTE表示有变量在 idata 的 0 x0040 处绝对地址定位.2)使用 KeilC 编译器定义绝对地址的变量,方法待查.2. 函数绝对地址定位1) 在程序中编写一函数 myTestvoid myTest(void)/ Add your code here2) 使

2、用 KeilC 编译器定位绝对地址的函数,打开 Project - Options for Target 菜单,选中 BL51 Locate 选项卡,在 Code:中输入:?PR?myTest?MAIN(0 x4000)把函数 myTest 定位到程序区的 0 x4000 处, 再次编译就可以了 3)一次定位多个函数的方法同样地,在程序中再编写另外一个函数 myTestlvoid myTestl(void)/ Add your code here在 Options for Target 菜单的 BL51 Locate 选项卡的 Code:中输入:?PR?myTest1?MAIN(0 x3900

3、), ?PR?myTest?MAIN(0 x4000)把函数 myTest1 定位在程序区的 0 x3900 处,把函数 myTest 定义在程 序区的 0 x4000 处,重新编译就可以了 在 M51 文件中可以找到下面的内容: 3.obj TO Reader RAMSIZE (256) CODE (?PR?MYTEST1?MAIN(0X3900), ?PR?MYTEST?MAIN (0X4000)3665H029BH* GAPCODE 3900H0014H UNIT?PR?MYTEST1?MAIN3914H 06ECH* GAPCODE 4000H0014H UNIT?PR?MYTEST?

4、MAIN4)函数的调用:程序中直接调用函数的方式就不说明了,这里重点讲使用函数指针调用 绝对地址处的函数的方法.(1)定义调用的函数原形typedef void (*CALL_MYTEST)(void);这是一个回调函数的原形,参数为空.(2)定义相应的函数指针变量CALL_MYTEST myTestCall = NULL;(3)函数指针变量赋值,指向我们定位的绝对地址的函数myTestCall = 0 x3900;指向函数 myTest1(4)函数指针调用if (myTestCall != NULL)myTestCall();/调用函数指针处的函数 myTest1,置 PC 指针为 0 x3

5、900检查编译生成的 bin文件,到 0 x3900处可以看到 myTest1的内 容,在 0 x4000处可以看到 myTest 的内容,(5)其它说明:如果在 0 x3000 到 0 x3900 的程序空间没有内容 时,把 myTestCall 的地址指针指到 0 x3800(在 0 x3000 到 0 x3900 之间)时,会从 0 x3900 处开始执行.至於在 Load 中调用 AP 中的函数的方法与此类似,但是相应的参数传 递可能要另寻方法*keil C51 绝对地址访问在利用 keil 进行 8051 单片机编程的时,常常需要进行绝对地址进行访问.特别是 对硬件操作,如 DA A

6、D 采样丄 CD 液晶操作,打印操作等等C51 提供了三种访问绝对地址的方法:1. 绝对宏:在程序中,用菲 include即可使用其中定义的宏来访问绝对地址,包括:CBYTE、XBYTE、PWORD、DBYTE、CWORD、XWORD、PBYTE、DWORD 具体使用可看一看 absacc.h 便知例如:rval=CBYTE0 x0002;指向程序存贮器的 0002h 地址rval=XWORD 0 x0002;指向外 RAM 的 0004h 地址2. _at_关键字直接在数据定义后加上_at_ const 即可,但是注意:(1)绝对变量不能被初使化;bit 型函数及变量不能用_at_指定。例如

7、:idata struct link list _at_ 0 x40;指定 list 结构从 40h 开始。xdata char text25b _at_0 xE000;指定 text 数组从 0E000H 开始提示:如果外部绝对变量是 I/O 端口等可自行变化数据,需要使用 volatile 关键 字进行描述,请参考 absacc.h。3. 连接定位控制此法是利用连接控制指令 code xdata pdata data bdata 对 段”地址进行,如要 指定某具体变量地址,则很有局限性,不作详细讨论。附:(c51)/*-ABSACC.HDirect access to 8051, exte

8、 nded 8051 and Philips 80C51MX memory areas.Copyright (c) 1988-2002 Keil Elektronik GmbH and Keil Software, Inc.Allrightsreserved.-*/#ifndef_ABSACC_H#defi ne_ABSACC_H#defi neCBYTE(un sig nedcharvolatilecode*)0)#defi neDBYTE(un sig nedcharvolatiledata*)0)#defi nePBYTE(un sig nedcharvolatilepdata*)0)#

9、defi neXBYTE(un sig nedcharvolatilexdata*)0)#defi neCWORD (un sig nedintvolatilecode*)0)#defi neDWORD (un sig nedintvolatiledata*)0)#defi nePWORD (un sig nedintvolatilepdata*)0)#defi neXWORD (u nsig nedintvolatilexdata*) 0)#ifdef_CX51_#defi neFVAR(object, addr) (*(objectvolatilefar *)(addr)#defi neF

10、ARRAY(object, base) (objectvolatilefar *)(base)#defi neFCVAR(object, addr) (*(objectcon stfar *)(addr)#defi neFCARRAY(object, base) (objectconstfar *)(base)#else#defi ne FVAR(object, addr) (*(object volatilefar *)(addr)+0 x10000L)#defi ne FCVAR(object, addr) (*(object constfar *) (addr)+0 x810000L)#

11、defi ne FARRAY(object, base) (object volatilefar *)(base)+0 x10000L)#defi ne FCARRAY(object, base) (object constfar *)(base)+0 x810000L)#en dif#en dif附:(c166)/*-ABSACC.HDirect access to 166 memory areas for C166/EC+ Version 5.Copyright (c) 1992-2004 Keil Elektronik GmbH and Keil Software, Inc.*/#ifn

12、def#defi ne#if(_MODEL_=0)#defi neMVAR(object, addr)(*(objectvolatile*)(addr)#defi neMARRAY(object, base)(objectvolatile*)(base)#else#defi neMVAR(object, addr) (*(objectvolatilefar*)(addr)#defi neMARRAY(object, base)(objectvolatilefar*)(base)#defi neHVAR(object, addr) (*(objectvolatilehuge*)(addr)#de

13、fi neHARRAY(object, base) (objectvolatilehuge *)(base)#defi neXVAR(object, addr) (*(objectvolatilexhuge *)(addr)#defi neXARRAY(object, base) (objectvolatilexhuge *)(base)#en dif#en difAllrightsreserved.ABSACC_HABSACC_H以下来自转载:使用 KeilC51 软件,可以很方便地将代码或者数据绝对定位到某个地址。1、代码定位:方法 1 :使用伪指令 CSEG。比如要将 MyFunc1 定

14、位到代码区 C:0 x1000 ,则新 建一个A51 文件,添加以下内容:PUBLIC MYFUNC1CSEG AT 1000HMYFUNC1:;其它代码RET在其它源文件中,就可以调用 MyFunc()函数了。需要注意的是,编译器不检测 传递参数的数目,仅检测函数是否有返回值。方法 2 :使用 BL51 Locate 选项。比如在 ma in .c 中定义了一个 MyFu nc2 函数, 并且要将该函数定位到代码区 C:0 x2000,则从菜单中选择 Project-Options forTargetTargets,在弹出的对话框中选择 BL51 Locate 页,在下面的 code 栏中

15、写上?PR?MYFUNC2?MAIN(0 x2000) 即可。如果想定位多个函数,也可以使用*通配符。2、变量定位:只有全局变量可以绝对定位,局部变量无法实现绝对定位。方法 1:使用_at_关键字。声明一个全局变量 unsigned char data MyBuf18 _at_ 0 x20;方法 2 :使用 BL51 Locate 选项。比如将 main.c 中定义的所有 data 型的全局变 量定位到数据区 D:0 x28 开始的空间,则从菜单中选择 Project-Options for Target Target1,在弹出的对话框中选择 BL51 Locate 页,在下面的 data 栏

16、中写上?DT?MAIN(0 x28)即可。如果是 idata,则使用?ID?MAIN(0 x28),如果是 xdata,则使用?XD?MAIN(0 x28), 如果是 pdata,则使用?PD?MAIN(0 x28)3、堆栈定位:在 STARTUP.A51 文件中定义了堆栈区?STACK,其起始地址同样可以在 BL51 Locate页中设置,在 Stack 栏写上?STACK(0 x80)BL51 locate 选项卡中 code range 和 xdata range 如果不填写,编译默认将程序中相应代码和变量从空间前面取起 网上看到有人提到在 keil 中使用_at_进行绝对地址定位问题,

17、我简单介绍一下它 的用法。使用_at_关键字对存储器进行绝对地址定位程序如下# i ncludechar xdata LED_Data50 _at_ 0 x8000;mai n()LED_Data0 = 0 x23;在 keil 中运行以上程序可以在存储器窗口中输入x: 0 x8000 可以看到 0 x8000地址中的值为 0 x23.值得指出的几点是1. 在给变量 LED_Data50定位绝对地址空间时,不能对其赋初值。2. char xdata LED_Data50 _at_ 0 x8000; 这条语句不能主函数中。有些网友提到在按着 keil 说明中用_at_进行绝对地址定位时,编译会出

18、现错误 274 ,就是将 这条语句放在主函数中的原因。3. keil 中地址是自动分配的,所以除非特殊情况否则不提倡使用绝对地址定位。 初学者因帖别注意。不要把 c 当作汇编使用。对需要/RST 复位后要保持变量不变,防止意外改变(比如升级到新程序,变量 地址可能被编译器优化到其他地方),比较有用! !STARTUP.A51 这个文件有什么用,有必要添加到工程吗?如果不添加startup.a51文件,编译器就会自动加入一段初始化内存以及堆栈等 的代码,这时的内存初始化部分你就无法去控制了,当然这在大部分情况下没什么关系。但是如果你想你的程序在复位后,内存里面的信息依然还保存着(所说 的热复位”

19、,那么你就需要添加该启动文件,并且去里面修改内存初始化部分, 不要初始化你需要保留的部分内存。请问如何在 keil 编译器里,编程时指定函数的绝对地址(无内容)不好意思啊,我还从来没有接触过有这样要求情况, 不过从网上其他地方找了一篇你参考一下吧,、函数定位:假如要把 C 源文件 tools.c中的函数int BIN2HEX(i nt xx) 放在 CODE MEMORY0 x1000 处,先编译该工程,然后打开该工程的 M51 文件,在* * *C O D EM E M O R Y* * *行下找出要定位的函数的名称,应该形如:CODExxxxHxxxxHUNIT?PR?_BCD2HEX?T

20、OOLS然后在:Project-Optio ns forTarget .-BL51 Locate: Code中填写如下内容:?PR?_BCD2HEX?TOOLS(0 x1000)再次 Build,在 M51 中会发现该函数已放在 CODE MEMORY3x1000 处了2、赋初值的变量定位:要将某变量定位在一绝对位置且要赋初值, 此时用 _at_ 不能完成,则如下操作:在工程中建立一个新的文件, 如 InitVars.c , 在其中对要处理的变量赋初值 (假 设是 code变量):char code myVer = COPYRIGHT 2001-11;然后将该文件加入工程,编译,打开 M51

21、文件,若定义的是 code 型,贝恠* * *C O D EM E M O R Y* * *F 可找到:CODE xxxxHxxxxHUNIT?CO?INITVARS然后在:Project-Options for Target .-BL51 Locate: Code中填入:?CO?INITVARS(Ox2OO)再次编译即可。相应地,如为 xdata 变量,则 InitVars.c 中写:char xdata myVer = COPYRIGHT 2001-11;然后将该文件加入工程,编译,打开M51 文件,在* * * X D A T A M E M O R Y * * *下可找到:XDATA

22、xxxxHxxxxHUNIT?XD?INITVARS然后在:Project-Options for Target .-BL51 Locate: Xdata中填入:?XD?INITVARS(0 x200)再次编译即可。相应地,若定义的是 data/idata 等变量,则相应处理即可。3、若有多个变量或函数要进行绝对地址定位, 则应按地址从低到高的顺序排列。*PIC 51 混编 C18 指定数据绝对地址51:RSEG 是段选择指令,要想明白它的意思就要了解段的意思段是程序代码或数据对象的存储单位。程序代码放到代码段,数据对象放到数据 段。段分两种,一是绝对段,一是再定位段。绝对段在汇编语言中指定,

23、在用L51 联接的时候,地址不会改变。用于如访问一个固定存储器的 i/o,或提供中断 向量的入口地址。而再定位段的地址是浮动的。它的地址有 L51 对程序模块连 接时决定,C51对源程序编译所产生的段都是再定位段, 它都有段名和存储类型。 绝对段没有段名。说了这么多,大家可能还是不明白段是什么意思。别急,接着往下看 例如,你写用 C 写了一个函数 void test_fun(void) , ,存在 test.c 中,用编译 器编译以后 SRC FILE 中看到:?PR?test_fun?TEST SEGMENT CODE /( 函数放至 M 弋码段中)写这个函数体的时候:RSEG ?PR?te

24、st_fun?TEST /选择已定位的代码段为 当前段test_fu n:,代码所以函数的表达模式是这样:?PR?函数名?文件名而函数名又分:1:无参函数?PR?函数名?文件名2:有参函数?PR?_函数名?文件名3:再入函数?PR?_?函数名?文件名又例女口 你定义了全局变量 unsigned char data temp1,temp2; unsigned char xdatatemp3;在 test.c 文件中,编译器会为每个文件分 0 到多个全局数据段,相 同类型的全局变量被存到同一段中。所以上面会编译成如下:RSEG ? DT? TEST.tempi: DS 1.temp2: DS 1J

25、RSEG ?XD? TEST.temp3: DS 1/下面是各个类型的数据全局段的表示?co?文件名II常数段?XD? FILE_NAME /XDATA 数据段?DT? FILE_NAME /DATA 数据段?ID? FILE_NAME /IDATA ?BI? FILE_NAME / BIT ?BA? FILE_NAME /BDATA .?PD? FILE_NAME /PDATA.看到这里大家应该明白段的意思了吧。 也许你会问,这有什么作用哪?它就是用 在当你需要用汇编语言写一部份程序的时候,把汇编写的函数放在这个问件中, 改名 xxx.a51,按上面的规则写。编译就好。既然知道了段的意思,现

26、在我们回到 SEG 的用法上来。A51 中有两种段选择指令:再定位段选择指令 和绝对段选择指令它们用来选 择当前段是再定位段还是绝对段。使用不同的段选择指令, 将使程序定位在不同 的地址空间之内。1:再定位段的选择指令是:RSEG 段名它用来选择一个在前面已经定义过的再定位段作为当前段。用法就像我们上面的例子,先申明了一个函数段,后面写这个函数段。2:绝对段选择指令CSEG AT 绝对地址表达式/绝对代码段DSEG AT 绝对地址表达式/内部绝对数据段XSEG AT 绝对地址表达式/外部绝对数据段ISEG AT 绝对地址表达式内部间接寻址绝对数据段BSEG AT 绝对地址表达式/绝对位寻址段它

27、们的用法我举一个例子:例如我们写串口中断程序,起始地址是 0 x23.就这样写CSEG AT 0X23LJMP seriallSRRSEG ?PR?seriallSR?TEST.serialISR:PIC:汇编函数使用同一个工程 C 文件中的变量,例如 ICFLAG 在 C 文件中定义,则汇 编文件中定义方式为;定义外部变量EXTERN ICFLAG定义函数例如CARDATR:*8RETGLOBAL CARDATR在同一个工程文件下调用汇编中的函数 CARDATR则应该定义函数 extern void CARDATR(void);C18 指定数据绝对地址例如:#pragma udata ove

28、rlay RECBUFS =0 x190 200UINT8 NUMBER;UINT8 REC_BUF31;#pragma udata*标签:KEIL C51 编程KEIL C51 高级编程KEIL C51 高级编程第一节绝对地址访问C51 提供了三种访问绝对地址的方法:1. 绝对宏:在程序中,用“# in elude ”即可使用其中定义的宏来访问绝对地址,包括:CBYTE、XBYTE、PWORD、DBYTE、CWORD、XWORD、PBYTE、DWOR D具体使用可看一看 absacc.h 便知例如:rval=CBYTE0 x0002;指向程序存贮器的 0002h 地址rval=XWORD 0

29、 x0002;指向外 RAM 的 0004h 地址2. _at_关键字直接在数据定义后加上_at_ con st 即可,但是注意:(1)绝对变量不能被初使化;bit 型函数及变量不能用_at_指定。例如:idata struct link list _at_ 0 x40;指定 list 结构从 40h 开始。xdata char text25b _at_0 xE000 ;指定 text 数组从 0E000H 开始提示:如果外部绝对变量是 I/O 端口等可自行变化数据,需要使用 volatile 关键 字进行描述,请参考 absacc.h。3. 连接定位控制此法是利用连接控制指令 code xd

30、ata pdata data bdata 对“段”地址进行,如要指定某具体变量地址,则很有局限性,不作详细讨论。第二节 Keil C51 与汇编的接口1. 模块内接口方法是用# pragma 语句具体结构是:#pragma asm汇编行#pragma endasm这种方法实质是通过 asm 与 ndasm 告诉 C51 编译器中间行不用编译为汇编行, 因而在编译控制指令中有 SRC 以控制将这些不用编译的行存入其中。2. 模块间接口C 模块与汇编模块的接口较简单,分别用 C51 与 A51 对源文件进行编译,然后 用 L51将 obj 文件连接即可,关键问题在于 C 函数与汇编函数之间的参数传

31、递 问题,C51 中有两种参数传递方法。(1)通过寄存器传递函数参数最多只能有 3 个参数通过寄存器传递,规律如下表:参数数目charintIon g,float一般指针123R7R5R3R6& R7R4& R5R2& R3R4 R7R4 R7R1 R3R1 R3R1 R3通过固定存储区传递(fixed memory)这种方法将 bit 型参数传给一个存储段中:? function_name?BIT将其它类型参数均传给下面的段:? function_name?BYTE,且按照预选顺序存放。至于这个固定存储区本身在何处,则由存储模式默认。(3)函数的返回值函数返回值一律放

32、于寄存器中,有如下规律:return typeRegistev说明bit标志位由具体标志位返回char/unsigned char 1_byte 指针R7单字节由 R7 返回int/unsigned int 2_byte 指针R6 & R7双字节由 R6 和 R7 返回,MSB 在 R6long&un sig ned longR4 R7MSB 在 R4, LSB 在 R7floatR4 R732Bit IEEE 格式一般指针R1 R3存储类型在 R3 高位 R2 低 R1SRC 控制该控制指令将 C 文件编译生成汇编文件(.SRC),该汇编文件可改名后,生成汇 编.ASM文件,

33、再用 A51 进行编译。第三节 Keil C51 软件包中的通用文件在 C51LiB 目录下有几个 C 源文件,这几个 C 源文件有非常重要的作用,对它 们稍事修改,就可以用在自己的专用系统中。1. 动态内存分配init_mem.C :此文件是初始化动态内存区的程序源代码。 它可以指定动态内存的 位置及大小,只有使用了 init_mem()才可以调回其它函数,诸如 malloc calloc, realloc 等。calloc.c :此文件是给数组分配内存的源代码,它可以指定单位数据类型及该单 元数目。malloc.c :此文件是 malloc 的源代码,分配一段固定大小的内存。realloc

34、.c :此文件是 realloc.c 源代码,其功能是调整当前分配动态内存的大小。2. C51 启动文件 STARTUP.A51启动文件 STARTUP.A51 中包含目标板启动代码,可在每个 project 中加入这个 文件,只要复位,则该文件立即执行,其功能包括:l 定义内部 RAM 大小、外部 RAM 大小、可重入堆栈位置l 清除内部、外部或者以此页为单元的外部存储器l 按存储模式初使化重入堆栈及堆栈指针l 初始化 8051 硬件堆栈指针l 向 main()函数交权开发人员可修改以下数据从而对系统初始化常数名意义IDATALEN 待清内部 RAM 长度XDATA START 指定待清外部

35、 RAM 起始地址XDATALEN 待清外部 RAM 长度IBPSTACK 是否小模式重入堆栈指针需初始化标志,1 为需要。缺省为 0IBPSTACKTOP 指定小模式重入堆栈顶部地址XBPSTACK 是否大模式重入堆栈指针需初始化标志,缺省为 0XBPSTACKTOP 指定大模式重入堆栈顶部地址PBPSTACK 是否 Compact 重入堆栈指针,需初始化标志,缺省为 0PBPSTACKTOP 指定 Compact 模式重入堆栈顶部地址PPAGEENABLE P2 初始化允许开关PPAGE 指定 P2 值PDATASTART 待清外部 RAM 页首址PDATALEN 待清外部 RAM 页长度

36、提示:如果要初始化 P2 作为紧凑模式高端地址,必须:PPAGEENAGLE = 1, PPAGE为 P2 值,例如指定某页 1000H 10FFH,贝 U PPAGE = 10H,而且连 接时必须如下:L51PDATA(1080H),其中 1080H 是 1000H 10FFH 中的任一个值。以下是 STARTUP.A51 代码片断,红色是经常可能需要修改的地方:;This file is part of the C51 Compiler package;Copyright KEIL ELEKTRONIK GmbH 1990STARTUP.A51: This code is executed

37、 after processor reset.;To translate this file use A51 with the following invocation:J;A51 STARTUP.A51J;To link the modified STARTUP.OBJ file to your application use the foll owi ng;L51 inv ocatio n:J;L51 , STARTUP.OBJ;User-defined Power-On Initialization of MemoryJ;With the following EQU statements

38、 the initialization of memory;at processor reset can be defined:J;the absolute start-address of IDATA memory is always 0IDATALEN EQU 80H ; the length of IDATA memory in bytes.JXDATASTART EQU 0H ; the absolute start-address of XDATA memoryXDATALEN EQU 0H ; the length of XDATA memory in bytes.JPDATAST

39、ART EQU 0H ; the absolute start-address of PDATA memoryPDATALEN EQU OH ; the length of PDATA memory in bytes.J;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 I

40、nitilizationJ;The following EQU statements define the stack pointer for reentrant;functions and initialized it:J;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.J;Stack Space f

41、or 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.J;Stack Space for reentrant functions in the COMPACT model.PBPSTACK EQU 0 ; set to 1 if compact reentrant is used.PBPSTACKTOP EQU 0FFFFH+1;

42、set top of stack to highest location+1.;Page Definition for Using the Compact Model with 64 KByte xdata RA M;The following EQU statements define the xdata page used for pdata;variables. The EQU PPAGE must conform with the PPAGE control use d;in the linker invocation.PPAGEENABLE EQU 0 ; set to 1 if p

43、data object are used.PPAGE EQU 0 ; define PPAGE number.3.标准输入输出文件putchar.cputchar.c 是一个低级字符输出子程,开发人员可修改后应用到自己的硬件系统 上,例如向 CLD 或 LEN 输出字符。缺省:putchar.c 是向串口输出一个字符 XON|XOFF 是流控标志,换行符“ *n” 自动转化为回车/换行“ rn”。getkey.cgetkey 函数是一个低级字符输入子程,该程序可用到自己硬件系统,如矩阵键 盘输入中,缺省时通过串口输入字符。4.其它文件还包括对 Watch-Dog 有独特功能的 INIT.A51

44、 函数以及对 8xC751 适用的函数, 可参考源代码。第四节段名协定与程序优化1.段名协定(Segment Naming Conventions)C51 编译器生成的目标文件存放于许多段中,这些段是代码空间或数据空间的一 些单元,一个段可以是可重定位的,也可以是绝对段,每一个可重定位的段都有 一个类型和名字,C51 段名有以下规定:每个段名包括前缀与模块名两部分, 前缀表示存储类型,模块名则是被编译的模 块的名字,例如:? CO ? main1 :表示 main1 模块中的代码段中的常数部分? PR ? function1 ? module 表 module 模块中函数 function1

45、的可执行段,具体 规定参阅手册。2.程序优化C51 编译器是一个具有优化功能的编译器,它共提供六级优化功能。确保生成目 标代码的最高效率(代码最少,运行速度最快)。具体六级优化的内容可参考帮助。在 C51 中提供以下编译控制指令控制代码优化:OPTIMIZE(SJXE):尽量采用子程序,使程序代码减少。NOAREGS :不使用绝对寄存器访问,程序代码与寄存器段独立。NOREGPARMS :参数传递总是在局部数据段实现,程序代码与低版本C51 兼容。OPTIMIZE(SIZE)AK OPTIMIZE(speed)提供 6 级优化功能,缺省为:OPTIMIZE(6,SPEED)。第五节 Keil

46、C51 的代码效率一、存储模式的影响存储模式决定了缺省变量的存储空间,而访问各空间变量的汇编代码的繁简程度 决定了代码率的高低。例如:一个整形变量 i,如放于内存 18H、19H 空间,则+i 的操作编译成四条 语句:INC 0 x19MOV A,0 x19JNZ 0 x272DINC 0 x180 x272D : 而如果放于外存空间 0000H、0001H 则+i 的操作编译成九条语句:MOV DPTR,0001MOVX A, DPTRINC AMOVX DPTR,AJNz #5MOV OPTR,#0000MOVX A,DPTRINC AMOVX DPTR,A就汇编之后的语句而言,对外部存储

47、器的操作较内部存储器操作代码率要低得 多,生成的语句为内存的两倍以上,而程序中有大量的这种操作,可见存储模式 对代码率的响了。因此程序设计的原则是1、存储模式从 small-Compact-large 依次选择,实在是变量太多,才选 large 模 式。2、即使选择了 large 模式,对一些常用的局部的或者可放于内存中的变量,最 好放于内存中,以尽量提高程序的代码率。二、程序结构的影响程序的结构单元包括模块、函数等等。同样的功能,如果结构越复杂,其所涉及 的操作、变量、功能模块函数等就越多,较之结构性好,代码简单的程序其代码 率自然就低得多。此外程序的运行控制语句,也是影响代码率的关键因素,

48、例如:switch -case 语 句,许多编译器都把它们译得非常复杂, Keil C51 也不例外, 相对较为简易的 S witch-case语句,编译成跳转指令形式,代码率较高,但对较为复杂的 SwitchCase ,则要调用一个系统库函数?C?ICASE 进行处理,非常复杂。再如 if( ),while(),等语句也是代码相对较低的语句,但编译以后比switch-case 要高得多。因此建议设计者尽量少用 switch-case 之类语句来控制程序结构,以提高代码率。除以上两点外,其它因素也会对代码率产生影响,例如:是否用寄存器传递参数即 NOAREGS 选项是否有是否包括调试信息:即

49、DEBUG 选项是否包括扩展的调试信息:即 BJECTEXTEND第六节如何优化 C 语言代码(程序员必读)1、选择合适的算法和数据结构应该熟悉算法语言,知道各种算法的优缺点,具体资料请参见相应的参考资料, 有很多计算机书籍上都有介绍。将比较慢的顺序查找法用较快的二分查找或乱序查 找法代替,插入排序或冒泡排序法用快速排序、 合并排序或根排序代替,都可以大 大提高程序执行的效率。选择一种合适的数据结构也很重要,比如你在一堆随机 存放的数中使用了大量的插入和删除指令,那使用链表要快得多。数组与指针语句具有十分密码的关系, 一般来说,指针比较灵活简洁,而数组则 比较直观,容易理解。对于大部分的编译器,使用指针比使用数组生成的代码更短,执行效率更高。但是在 Keil 中则相反,使用数组比使用的指针生成的代码更短。2、使用尽量小的数据类型能够使用字符型(char) 定义的变量,就不要使用整型(int) 变量来定义;能够使用 整型变量定义的变量就不要用长整型(long int),能不使用浮点型(float)变量就 不要使用浮点型变量。当然,在定义变量后不要超过变量的作用范围,如果超过变量的范围赋值,C 编译器并不报错,但程序运行结果却错了,而且这样的错误很 难发现。在 ICCAVR 中,可以在 Options

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论