KEIL中C51英文的翻译.docx
《KEIL中C51英文的翻译.docx》由会员分享,可在线阅读,更多相关《KEIL中C51英文的翻译.docx(35页珍藏版)》请在冰豆网上搜索。
KEIL中C51英文的翻译
第六章高级编程技术
本章介绍的信息,有经验的高级编程软件工程师会发现非常宝贵的。
这些课题大多知识对于成功地使用Cx51编译器创建一个嵌入式的8051目标程序是没有必要的。
但是,以下的章节提供了非标准程序可以完成的洞察力(例如,连接到PL/M-51)。
本章将讨论以下议题:
⏹你可以改变自定义启动程序的文件
⏹你可以改变自定义运行时库例程执行的文件
⏹Cx51编译器使用的名称代码和数据段的公约
⏹如何实现Cx51功能组装和PL/M-51例程
⏹对于不同的Cx51数据类型的数据存储格式
⏹Cx51优化编译器的不同优化功能
自定义文件
该Cx51编译器提供一系列的源文件,你可以对一个特定的硬件平台修改以适应你的目标计划。
这些文件包括:
⏹在启动时(STARTUP.A51)执行的代码
⏹用来初始化静态变量(INIT.A51)的代码
⏹用来执行低电平流I/O的代码
⏹内存分配代码
在这些文件中包含的代码已经编译或组装和在C库中。
当你连接你的程序,来自库的代码自动包含在内。
你可以定制这些文件以适应你的要求。
如果您在μVision2IDE中工作,我们建议你复制您的项目文件夹中的自定义文件进行修改。
该文件修改后的版本,也可以添加相同类型的其他源文件到你的项目中。
当你使用命令行工具时,你必须在连接器命令行中包含修改自定义文件的目标文件。
下面的示例将演示如何包含STARTUP.A51和PUTCHAR.C自定义替换文件:
Lx51MYMODUL1.OBJ,MYMODUL2.OBJ,STARTUP.OBJ,PUTCHAR.OBJ
XBANKING.A51文件允许你改变目前扩展内存访问rountines配置。
STARTUP.A51
在STARTUP.A51文件包含了Cx51目标程序的启动代码。
该源文件位于lib目录下。
在每一个8051项目中包含一个该文件的副本需要自定义启动代码。
启动代码后立即执行目标系统的复位和选择,以便执行下列操作:
⏹清除内部数据存储器
⏹清除外部数据存储器
⏹清除分页外部数据存储器
⏹初始化小模型再入堆栈和指针
⏹初始化大模型再入堆栈和指针
⏹初始化紧凑型再入堆栈和指针
⏹初始化8051硬件堆栈指针
⏹将控制转移到主要的C函数
在STARTUP.A51文件提供了集合常数,你可以改变它来控制在启动时所采取的行动。
这些定义如下表。
常数名称
描述
IDATALEN
指示idata的字节数初始化为0。
默认是80是因为大多数8051衍生产品包含至少128字节的内部数据存储器。
用于8052和其他衍生产品价值的100h具有256字节的内部数据存储器。
XDATASTART
指定了xdata地址开始初始化为0。
XDATALEN
指示xdata字节数初始化为0。
默认为0。
PDATASTART
指定pdata地址开始初始化为0。
PDATALEN
指示pdata字节数初始化为0。
默认为0。
IBPSTACK
指示是否初始化小模式再入堆栈指针(?
C_IBP)。
为1就将此指针值进行初始化,为0就阻止这个指针的初始化。
默认为0。
IBPSTACKTOP
指定小模型再入.堆栈区的起始地址。
默认是0xFF在idata存储器中。
Cx51编译器不检查是否可用堆栈区满足了应用的要求,执行这样的测试是你的责任。
XBPSTACK
指示是否初始化大模式再入堆栈指针(?
C_XBP)。
为1就将此指针值进行初始化,为0就阻止这个指针的初始化。
默认为0。
XBPSTACKTOP
指定大模式再入堆栈区的起始地址。
默认是0xFFFF在xdata存储器。
Cx51编译器不检查是否可用堆栈区满足了应用的要求,执行这样的测试是你的责任。
PBPSTACK
指示是否初始化大模式再入堆栈指针(?
C_PBP)。
为1就将此指针值进行初始化,为0就阻止这个指针的初始化。
默认为0。
PBPSTACKTOP
指定紧凑型再入堆栈区的起始地址。
默认是0xFF在pdata存储器。
Cx51编译器不检查是否可用堆栈区满足了应用的要求,执行这样的测试是你的责任。
PPAGEENABLE
启用(1值)或禁用(0值)8051芯片端口2的初始化。
默认为0。
端口2的寻址允许256个字节的变量内存在任意xdata页中的映射。
PPAGE
指定在pdata内存访问中写入8051的端口的值。
这个值代表了XDATA内存页面使用的pdata。
这是用于pdata的绝对地址范围的高八位。
例如,如果pdata去开始于在xdata存储器中的地址1000h(页10h),PPAGEENABLE应将置1,PPAGE应设置为10h。
在BL51连接器/定位器中必须在PDATA区指令中包含一个在1000h和10FFh之间的值。
例如:
BL51PDATA(1050H)
无论是BL51还是Cx51都不会检查PDATA指令和PPAGE集合常数是否正确指定。
你必须确保这些参数包含适当的值。
在8051的家族中有许多种芯片需要特殊的启动代码。
以下列表提供了各种启动版本的概览:
启动文件
描述
STARTUP.A51
典型8051芯片的标准启动代码。
START_AD.A51
模拟装置微型转换器B2系列变种的启动代码。
STARTLPC.A51
飞利浦LPC变种的启动代码。
START390.A51
达拉斯80C390,80C400,5240contigious模式。
START_MX.A51
飞利浦80C51MX架构的启动代码。
START751.A51
飞利浦80C75x变种的启动代码。
INIT.A51
INIT.A51文件包含了明确初始化变量的初始化例程。
如果你的系统中安装了看门狗定时器,你可以使用看门狗宏把看门狗集成到初始化代码中。
这个宏只有在初始化时间超过看门狗周期较长时需要定义。
例如,如果您使用的是英飞凌C515,宏可以定义如下:
WATCHDOGMACRO
SETBWDT
SETBSWDT
ENDM
INIT_TNY.A51文件是INIT.A51的简化版本,用于项目时可能不包含XDATA存储器。
当你写代码入如飞利浦LPC系列这种在数据空间包含变量初始化的单芯片时,你应该使用该文件。
XBANKING.A51
此文件提供远(HDATA)和常远(HCONST)内存类型支持例程。
扩展LX51连接器/定位器管理扩展地址空间HDATA和HCONST中针对远和常量远的处理。
该Cx51编译器使用一个3字节通用指针来访问这些内存区域。
与远东内存类型定义的变量都放在内存类HDATA中。
变量定义的常量远获取内存类HCONST。
要对传统的8051芯片使用C51编译器的远内存你必须使用如第84页所述的“VARBANKING”指令。
内存类型远和常量远提供新型8051芯片的大量代码/xdata空间。
如果您正在使用的CPU提供一个扩展的24位DPTR寄存器,你可以匹配文件XBANKING.A51的默认版本,并确定在以下表中列出的符号。
常量名
描述
?
C?
XPAGE1SFR
DPTR页面寄存器的SFR地址包含了DPTR位16-23。
?
C?
XPAGE1RST
重置?
C?
XPAGE1SFR的值在X:
0区域的地址。
当你使用的是VARBANKING
(1)指令时该设置由C51编译器来使用。
VARBANKING
(1)C51编译器在中断函数的开始保存?
C?
XPAGE1SFR并设置这个寄存器为?
C?
XPAGE1RST的值。
远内存类型允许你访问注入EEPROM空间或者字符串代码ROM的特殊内存区域。
您的应用程序访问这些内存区域,好像他们是标准8051内存空间的一部分。
文件夹C51\实例\FARMEMORY中的示例程序展示如何在经典8051芯片中使用C51远内存类型。
如果一个例子没有满足你的要求,你可以调整访问程序如以下列表。
访问例程
描述
?
C?
CLDXPTR,?
C?
CSTXPTR
在扩展内存中加载/存储一个字节(char)。
?
C?
ILDXPTR,?
C?
ISTXPTR
在扩展内存中加载/存储一个字(int)。
?
C?
PLDXPTR,?
C?
PSTXPTR
在扩展内存在加载/存储一个三位指针。
?
C?
LLDXPTR,?
C?
LSTXPTR
在扩展内存中加载/存储一个双字(long)。
每个访问例程在CPU寄存器R1/R2/R3的一个3位指针参数中作为一个内存地址参数得到。
寄存器R3保存内存类型的值。
对于经典8051芯片,Cx51编译器使用以下内存类型的值:
R3值
存储类型
存储类
地址范围
0x00
data/idata
DATA/IDATA
I:
0x00-I:
0xFF
0x01
xdata
XDATA
X:
0x0000-X:
0xFFFF
0x02-0x7F
far
HDATA
X:
0x010000-X:
0x7E0000
0x80-0xFD
Farconst
HCONST
C:
0x800000-C:
0xFD0000(farconstismappedintothebankedmemoryareas)
0xFE
pdata
XDATA
one256-bytepageinXDATAmemory
0xFF
code
CODE/CONST
C:
0x0000-C:
0xFFFF
R3的值0x00,0x01,0xFE的和0xFF在运行时的库中已处理。
只有值0x02-0xFE是通过上述的XPTR访问例程传递。
在AX51宏汇编提供了MBYTE运行器计算R3的值,需要传递给XPTR访问函数。
下面是一个AX51汇编使用XPTR访问函数的例子:
MOVR1,#LOW(variable);givesLSBaddressbyteofvariable
MOVR1,#HIGH(variable);givesMSBaddressbyteofvariable
MOVR1,#MBYTE(variable);givesmemorytypebyteofvariable
CALL?
C?
CLDXPTR;loadBYTEvariableintoA
基本I/O函数
下面的文件包含了低电平流I/O例程的源代码。
当你使用μVision2IDE,你可以简单地添加修改版本到项目。
C源文件
描述
PUTCHAR.C
使用所有的流程序输出字符。
你可以调整这个例程适应你的个人硬件(例如,LCD或LED显示器)。
默认版本通过串行接口输出字符。
一个XON/XOFF协议是用于流量控制。
换行字符('\n')被转换成回车/换行符序列(‘\r\n’))。
GETKEY.C
所有的流程序使用该输入字符。
你可以调整这个例程适应您的个人硬件(矩阵键盘,例如)。
默认版本通过串行接口读取一个字符。
没有数据执行转换。
内存分配函数
下面的文件包含内存分配例程的源代码。
C源文件
描述
CALLOC.C
分配内存给内存池阵列
FREE.C
返回先前分配的内存块给内存池
INIT_MEM.C
指定内存池的内存可能被分配使用malloc,释放calloc和realloc函数的位置和大小。
MALLOC.C
从内存池分配内存。
REALLOC.C
重新调整先前分配的内存块
优化器
Cx51编译器是一个优化编译器。
这意味着,编译器需要一定的步骤,以确保可能生成的代码和输出到目标文件是最有效(最小和/或最快)代码。
编译器分析生成的代码产生更有效的指令序列。
这将确保您Cx51编译程序尽可能快的运行。
该Cx51编译器提供了几种不同的优化级别。
请参阅63页“优化”的详细信息。
一般优化
优化
描述
常量折叠
几个常数值在一个表达式或地址计算相结合为一个常数。
跳转优化
当程序的效率增加时跳跃倒转或扩展到最终目标地址。
死代码清除
寄存器变量
从程序中移除不能到达的代码(死代码)。
如可能自动变量和函数参数都位于寄存器时。
预留给这些变量的数据内存被省略。
通过寄存器传递参数
三个功能参数的最大的可以在寄存器中传递。
全局公共子表达式消除
可能的话相同的子表达式或者地址计算在一个函数中会发生多次确认和一次计算。
常见的入口代码重用
当有多个请求调用一个函数,有些安装程序代码可以重复使用,从而减少程序大小。
公共块子程序
检测重复的指令序列,并将其转换进入子程序。
编译器甚至重新排列代码获得较大的重复序列。
8051专用优化
优化
描述
窥视孔优化
当内存空间或执行时间可以保存为一个结果是复杂的操作可以被简化操作所取代。
扩展访问优化
常量和变量直接包含在操作。
数据叠加
函数的数据和位段被认定为OVERLAYABLE,并被BL51链接器/定位器的其他数据和位段覆盖。
Case/Switch优化
使用跳转表或字符串跳跃优化开关Case/Switch语句。
代码生成选项
优化
描述
OPTIMIZE(SIZE)
常见的C操作被替换为子程序,从而减少了程序代码。
NOAREGS
Cx51编译器不再使用绝对寄存器访问。
程序代码是独立的寄存器组。
NOREGPARMS
参数传递总是在本地数据段中执行。
程序代码兼容Cx51的早期版本。
段命名约定
由Cx51编译器(程序代码,程序的数据,和常数)生成的对象都存储在段的代码或数据存储单元。
一个段可以重定位或者绝对化。
每个重定位的段都有一个类型和名称。
本节描述了Cx51comiler用来命名这些段的公约。
段名包括一个module_name,这个名字是在已声明的对象中的源文件的名字。
为了容纳现有的种类繁多的软件和硬件工具,所有的段名转换和存储为大写。
每一个段名有一个正确表示该段的存储类型的前缀。
前缀是包含在问号(?
)内。
下面是一个标准的段名前缀列表:
段前缀
存储类型
描述
?
PR?
program
可执行程序代码
?
CO?
code
程序存储器中的常量数据
?
BI?
bit
内部数据存储器的数据位
?
BA?
bdata
内部数据存储器的位寻址数据
?
DT?
data
内部数据存储器
?
FD?
far
远存储器(RAM空间)
?
FC?
constfar
远存储器(恒定ROM空间)
?
ID?
idata
间接寻址内部数据存储器
?
PD?
pdata
外部数据存储器中的数据分页
?
XD?
xdata
Xdata存储器(RAM空间)
?
XC?
constxdata
Xdata存储器(恒定ROM空间)
数据对象
数据对象是你在你的C程序中声明的变量和常量。
Cx51编译器为每一个内存类型声明一个变量生成一个独立的段。
下表列出了部分不同的变量数据对象生成的名称。
段名
描述
?
BA?
module_name
位寻址的数据对象
?
BI?
module_name
位对象
?
CO?
module_name
常数(字符串和初始化的变量)
?
DT?
module_name
在数据声明的对象
?
FC?
module_name
在常量远声明的对象(需要OMF2指令)
?
FD?
module_name
在远声明的对象(需要OMF2指令)
?
ID?
module_name
在idata声明的对象
?
PD?
module_name
在pdata声明的对象
?
XC?
module_name
在常量xdata中声明的对象(需要OMF2指令)
?
XD?
module_name
在xdata声明的对象
程序对象
程序对象包括Cx51编译器中C程序函数所产生的代码。
一个源代码模块的每个函数都分配了一个使用?
PR?
function_name?
module_name命名约定的独立代码段。
例如,文件SAMPLE.C中的函数error_check会产生一个段名?
PR?
ERROR_CHECK?
SAMPLE。
段还会因一个函数体内局部变量的声明而产生。
这些段名称遵循上述公约,并由内存空间中的局部变量的存储决定有一个不同的前缀。
函数的参数历史上是使用固定的内存位置传递。
这仍然是在PL/M-51写程序实现。
然而,Cx51可以在寄存器中传递多达3个函数参数。
其它参数传递使用传统的固定内存区域。
内存空间保留给所有的函数参数,无论这些参数中是否有些可能是在寄存器中传递。
参数区域必须公开已知的任何调用的模块。
因此,他们都公开使用下列定义段名:
?
function_name?
BYTE
?
function_name?
BIT
例如,如果func1是一个函数,它接受两个位参数以及其他数据类型的参数,位参数传递开始于?
FUNC1?
BIT,和所有其他参数传递开始于?
FUNC1?
BYTE。
参考163页“C程序接口汇编程序”函数的参数段的例子。
函数有参数,本地变量,或者位变量包含这些变量的所有其他领域。
这些段可以由BL51链接器/定位器覆盖。
它们为使用的内存类型为基础被创建。
小模型段命名约定
信息
段类型
段名
程序代码
code
?
PR?
function_name?
module_name
局部变量
data
?
DT?
function_name?
module_name
局部位变量
bit
?
BI?
function_name?
module_name
紧凑型段命名约定
信息
段类型
段名
程序代码
code
?
PR?
function_name?
module_name
局部变量
pdata
?
PD?
function_name?
module_name
局部位变量
bit
?
BI?
function_name?
module_name
大模型段命名约定
信息
段类型
段名
程序代码
code
?
PR?
function_name?
module_name
局部变量
xdata
?
XD?
function_name?
module_name
局部位变量
bit
?
BI?
function_name?
module_name
对寄存器参数和再入属性的函数名字稍作修改以避免运行时错误。
下表列出了从标准段名偏差。
声明
符号
描述
voidfunc(void)…
FUNC
函数名称没有任何理由不在寄存器中传递,其传递的参数转移到目标文件没有任何变化。
函数名转换为大写。
voidfunc1(char)…
_FUNC1
对于在寄存器中传递参数的函数,下划线字符('_')是函数名的前缀。
这是区分在CPU寄存器中传递参数的其他的函数。
voidfunc2(void)reentrant…
_?
FUNC2
对于可再入的函数,字符串“_?
”为函数的前缀。
这是用来识别再入函数。
接口C程序汇编
在8051汇编中你可以轻松使用汇编连接你的程序。
A51汇编器是一个在OMF-51格式中发出对象模块的8051宏汇编。
通过观察一些编程规则,你可以在C中调用汇编程序,反之亦然。
公共变量在集合模块中声明对你的C程序可用。
从你的C程序来调用汇编程序有几个原因。
⏹你可能已经给我们写好汇编代码。
⏹你可能需要提高一个特定功能的速度。
⏹你可能想直接从汇编中操作寄存器或者存储器映射I/O设备。
本节介绍如何编写汇编程序便可直接连接到C程序例程。
对于从C中调用一个汇编程序,它必须要知道在C函数中使用的参数传递和返回值。
对于实际问题,它必须有一个C函数。
功能参数
默认情况下,C函数在寄存器中可传递三个以上的参数。
其他参数则通过固定的存储器位置传递。
你可以使用NOREGPARMS指令来禁止参数在寄存器中传递。
如果在寄存器中传递的参数被禁用或者有太多的参数,参数在固定的内存位置传递。
在寄存器中传递参数的函数由下一个划线字符('_')前缀Cx51编译器标志代码生成时间函数名称。
传递函数只能在固定的内存位置参数不是以下划线前缀。
请参阅第166页“使用SRC指令”的一个例子。
在寄存器中传递参数
C函数可以在寄存器和固定的内存位置中传递参数。
三个参数中的一个最大值可以在寄存器中传递。
所有其他参数在固定的内存位置传递。
下表定义寄存器传递参数。
Arg号码
Char,1位指针
Int,2位指针
Long,float
通用指针
1
R7
R6&R7
(MSBinR6,
LSBinR7)
R4-R7
R1—R3
(MemtypeinR3,
MSBinR2,
LSBinR1)
2
R5
R4&R5
(MSBinR4,
LSBinR5)
R4-R7
R1—R3
(MemtypeinR3,
MSBinR2,
LSBinR1)
3
R3
R2&R3
(MSBinR2,
LSBinR3)
R1—R3
(MemtypeinR3,
MSBinR2,
LSBinR1)
下面的例子说明寄存器如何选择参数传递。
声明
描述
func1(
inta)
第一和唯一的参数a,是在寄存器R6和R7中传递。
func2(
intb,
intc,
int*d)
第一个参数b,在寄存器R6和R7中传递。
第二个参数c,在寄存器R4和R5中传递。
第三个参数d,在寄存器R1,R2和R3中传递。
func3(
longe,
longf)
第一个参数e,在寄存器R4,R5,R6和R7中传递。
第二个参数f,不能位于寄存器,因为这些对于一个有长型的第二个参数是已利用现有的第一个参数。
此参数是在固定的内存位置传递。
func4(
floatg,
charh)
第一个参数g,在寄存器R4,R5,R6和R7传递。
第二个参数h,不能再寄存器中传递,在固定的内存位置传递。
在固定内存位置传递参数
参数在固定内存位置传递到汇编程序使用段名?
function_name?
BYTE和?
function_name?
BIT来保存参数值传递给函数的函数名称。
位参数是在复制到?
function_name?
BIT段前调用该函数。
所有参数在这些段中分配空间,即使他们是使用寄存器传递。
参数按顺序存储在他们各自声明的各段中。
用于参数传递的固定内存位置是在外部内部存储器还是外部存储器取决于所用的存储模式。
SMALL内存模式是最有效的,它使用内部数据存储器传递参数段。
COMPACT和LARGE模型使用外部数据存储器传递参数段。
函数返回值
函数返回值总是使用CPU寄存器传递。
下表列出了可能的返回类型和每个使用的寄存器。
返回类型
寄存器
描述
Bit
进位标志
单字节在进位标志中返回
char/unsignedchar,
1-bytepointer
R7
单字节类型在R7返回
int/unsignedint,
2-byteptr
R6&R7
MSBinR6,LSBinR7
long/unsignedlong
R4-R7
MSBinR4,LSBinR7
Float
R4-R7
32位IEEE格式
通用指针
R1-R3
存储类型inR3,