Keil C51编译及连接技术
扫描二维码
随时随地手机看文章
主要介绍Keil C51的预处理方法如宏定义、常用的预处理指令及文件包含指令,C51编译库的选择及代码优化原理,C51与汇编混合编程的方法与实现以及超过64KB空间的地址分页方法的C51实现。
教学目标
1.了解Keil C51的预处理方法,主要有宏定义#define、常用的预处理指令#define、#error、#if 、#else、#elif、#endif、#ifdef、#ifndef 、#undef 、#line 、#pragma 及文件包含指令#include。
2.了解small、compact、large三种编译模式信代码优化方法。
3. 掌握C51模块内部调用汇编程序的三种方法,理解C51模块与汇编模块之间的接口规则,主要有C51函数名与汇编程序名的转换规则、C51函数及其相关段的命名规则、C51函数的参数传递规则。
4.理解Keil C51的Bankswitch原理,了解Keil C51的三种分页方式、公共空间的概念,掌握BL51有关分页的配置,以PSD813F2为硬件基础,掌握Keil C51分页的实现过程,能进行分页的程序设计。
一、预处理器
1.1宏定义
宏定义格式如下:#define 名称 替换文字
宏是一种简单的替换,在程序中凡是出现“名称”之处均被“替换文字”替代,替换文字可以是数字,也可以是字符串。
1.2预处理指令
由ANSI C的标准规定,预处理指令主要包括: #define、#error、#if 、#else、#elif、#endif、#ifdef、#ifndef 、#undef 、#line 、#pragma。
#define 指令
例如:#define PI 3.1415926凡是出现“PI”的地方均以“3.1415926”替换。宏的出现有助于提高程序的可读性及书写方便性,也有助于程序的调试。但是,出现在引号中的字符串是不能替换的。如printf(“PIn”);该语句运行后输出的是:PI而不是3.1415926。
define注意事项:
1)在宏定义语名后没有“;”;
2)在C51程序中习惯上用大写字符作为宏替换名,常放在程序开头;
3)宏定义还有一个特点,就是宏替换名可以带有形式参数,在程序中用到时,实际参数会代替这些形式参数。
例如: #define MAX(x, y) (x>y)?x:y
main(){inti=10,j=15;printf("TheMaxmumis%d",MAX(i,j);}
上例宏定义语句的含义是用宏替换名MAX(x, y)代替x, y中较大者,同样也可定义:
#define MIN(x, y) (x
4)#define命令在程序之外,其有效范围为定义命令之后到源文件结束,但是可以用#undef命令终止宏定义的作用域。如:
#definePI3.1415926voidmain(void){……}#undefPI
PI的范围从#define 开始到#undef PI 结束。
#if、#else、#endif指令
#if、#els和#endif指令为条件编择指令, 它的一般形式为:
#if常数表达式语句段;#else语句段;#endif
上述结构的含义是: 若#if指令后的常数表达式为真, 则编译#if到#else 之间的程序段; 否则编译#else到#endif之间的程序段。
例如:
#defineMAX200main(){#ifMAX>999printf("compiledforbiggern");#elseprintf("compiledforsmalln");#endif}
#undef指令
#undef指令用来删除事先定义的宏定义, 其一般形式为:
#undef 宏替换名
例如:
#defineTRUE1...#undefTURE
#undef主要用来使宏替换名只限定在需要使用它们的程序段中。
#include
文件包含是指用#include通常用来将一些常用的宏定义或变量定义所在的源文件包含到“#include”所在的文件中来。
通常格式如下:
#include“filename”或#include
在编译预处理时,将#include命令进行文件包含处理,即将filename中的全部内容复制插入到该指令处,减少重复劳动。通常引号包含的文件与现文件在同一个位置,否则用“<>”括起来。
在源文件开始处一般都要用一些#include指令,如
#include“stdio.h”#include“reg51.h”
如用户自定义了一个字库文件”kzk.h”,在源文件开始用#include “hzk.h”,之后,则可以在源程序中使用字库。
二、C51编译库及代码优化技术
2.1 C51编译库
C51编译器包含6个不同的编译库,如表12-2-1可根据不同需要进行优化 , C51 编译器包含的库模块,都有源代码,对它们可作与硬件相关的修改。用户改变对于现有硬件输入和输出结构的两个模块,就可修改所有库函数,同样也可以重新很快地构造如“printf”和“puts”函数系统默认串口为输出设备,但用户可修改为LCD 显示。
2.2 代码优化
C51可将即使有经验的程序员编制的代码进行优化,用户可选6个优化级,C51的所有优化方法如下:
1)一般优化:
常数折迭:发生在一个表达式或地址计算中的几个常数值组合为一个常数;
跳转优化:跳转转到最终的目标地址,以提高程序效率;
死码消除:不可执行代码(死码)可从程序中去掉;
寄存器变量:只要有可能,自动变量和参量放入寄存器中;
通过寄存器传递参数:寄存器中可传递最多三个参数;
全局公共子式消除:相同的子表达式或地址计算(多次发生在同一函数中)将被识别出来,并且只要有可能,将只计算一次。
2)基于8051 的优化
窥孔(PEEPHOLE)优化:只要能节省存贮空间或执行时间,复杂的运算都将化简。
访问优化:常数和变量直接包含在操作中。
数据覆盖:函数的数据和位移被标记为OVERLAYABLE,被L51 用其它数据和位覆盖。
CASE/SWITCH 优化:SWITCH/CASE 语句优化为一个跳转或一串跳转。
3)代码生成选项:
OPTMIZE(SIZE):共同的“C”操作被子程序代替:程序码长被压缩。
NOAREGS:不使用绝对寄存器访问,程序代码在这种方式下独立于寄存器组。
NOREGPARMS:参数传递总是在本数据段完成,程序代码与早期C51 版本兼容。
三、C51与ASM混合编程技术
3.1 混合编程意义
通常用C51来编写主程序。然而,在一些时序要求严格的采用汇编程序设计具有更高的效率,因此要求在C程序中调用一些用汇编语言编写的子程序。
另一方面,在以汇编语言为主体的程序开发过程中,如果涉及到复杂的数学运算,往往需要借助C51提供的运算库函数和强大的数据处理能力,这就要求在汇编中调用C51函数。
3.2 C51模块内的汇编接口
模块内接口通常是指在C函数内部插入汇编代码,也称内嵌汇编语句 。
在线汇编提供了能直接读写硬件的能力,如读写中断控制允许寄存器等,但编译器并不检查和分析在线汇编语言,插入在线汇编语言改变汇编环境或可能改变C变量的值可能导致严重的错误。
方法一、直接在函数体内的每个汇编语句前加?“asm”预编译指令。
voidreset_data(void){asmMOVR1,#0AHasmLOOP:INCAasmDJNZR0,LOOPreturn;}
方法二、把asm作为关键字后续汇编用大括号括起来即可。
voidreset_data(void){asm{MOVR1,#0AHLOOP:INCADJNZR0,LOOP}return;}
方法三、在C模块内通过语句“#pragma”嵌入汇编代码。
voidreset(void){#pragmaasmMOVR1,#0AHLOOP:INCADJNZR0,LOOP#pragmaendasmreturn;}
Keil C51编译环境相关设置
将嵌有汇编语句源文件加入要编译的项目文件,将光标指向此文件,选择右键菜单“option?for?file?'asm.c'”,将属性单“properties”中的“Generate?Assembler?SRC?File”与“Assemble?SRC?File”两项设置成黑体的“√”,将“Link?Public?Only”的“√”去掉,再编译即可。用此方法可以在C51源代码的任意位置用#pragma?asm和#pragma?endasm嵌入汇编语句。如果编译时未用SRC指令,则C51中的汇编代码会被编译器忽略。
3.3 C51与汇编的模块间接口
1、C51函数名与汇编程序名的转换规则
C51程序模块编译成目标文件后,其中的函数名依据其定义的性质不同会转换为不同的函数名,因此在C51和汇编程序的相互调用时要求汇编程序必须服从这种函数名的转换规则。C51中函数名转换规则如表12-3-1所列。
2、C51函数及其相关段的命名规则
一个C51源程序模块被编译后,其中的每一个函数以“?PR?函数名?模块名”为名的命名规则被分配到一个独立的CODE段。
如:模块“FUNC51”内包含一个名为“func”的函数,则其CODE段的名字是“?PR?FUNC?FUNC51”。
如果一个函数包含有data和bit对象的局部变量,编译器将按“?函数名?BYTE和?函数名?BIT”命令规则建立一个data和bit段,它们代表所要传递参数的起始位置,其偏移值为零。这些段是公开的,因而它们的地址可被其它模块访问。另外,这些段被编译器赋予“OVERLAYABLE”标志,故可被L51连接/定位器作覆盖分析。
依赖于所使用的存储器模式,这些段的段名按表12-3-2所列规则命名,在相互调用时,汇编语言必须服从C51有关段名的命名规则。
各种存储器模式下函数相关段名的命名规则