#define 指令
例如:#define PI 3.1415926 凡是出现“PI”的地方均以“3.1415926”替换。宏的出现有助于提高程序的可读性及书写方便性,也有助于程序的调试。但是,出现在引号中的字符串是不能替换的。如printf(“PI
”);该语句运行后输出的是:PI而不是3.1415926。
define注意事项:
1)在宏定义语名后没有“;”;
2)在C51程序中习惯上用大写字符作为宏替换名,常放在程序开头;
3)宏定义还有一个特点,就是宏替换名可以带有形式参数,在程序中用到时,实际参数会代替这些形式参数。
例如: #define MAX(x, y) (x>y)?x:y
上例宏定义语句的含义是用宏替换名MAX(x, y)代替x, y中较大者,同样也可定义:
#define MIN(x, y) (x<y)?x:y
表示用宏替换名MIN(x, y)代替x, y中较小者。
4)#define命令在程序之外,其有效范围为定义命令之后到源文件结束,但是可以用#undef命令终止宏定义的作用域。如:
PI的范围从#define 开始到#undef PI 结束。
#if、#else、#endif指令
#if、#els和#endif指令为条件编择指令, 它的一般形式为:
上述结构的含义是: 若#if指令后的常数表达式为真, 则编译#if到#else 之间的程序段; 否则编译#else到#endif之间的程序段。
例如:
#undef指令
#undef指令用来删除事先定义的宏定义, 其一般形式为:
#undef 宏替换名
例如:
#undef主要用来使宏替换名只限定在需要使用它们的程序段中。
#include
文件包含是指用#include通常用来将一些常用的宏定义或变量定义所在的源文件包含到“#include”所在的文件中来。
通常格式如下:
#include“filename” 或 #include<filename>
在编译预处理时,将#include命令进行文件包含处理,即将filename中的全部内容复制插入到该指令处,减少重复劳动。通常引号包含的文件与现文件在同一个位置,否则用“<>”括起来。
在源文件开始处一般都要用一些#include指令,如
如用户自定义了一个字库文件”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”预编译指令。
方法二、把asm作为关键字后续汇编用大括号括起来即可。
方法三、在C模块内通过语句“#pragma”嵌入汇编代码。
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所列。
SRC是一个十分有用的编译控制命令,它可令C51编译器将一个C源文件编译成一个相应的汇编源文件,而不是目标文件,在这个汇编文件中,我们可清楚地看到每一个参数的传递方法。
示例剖析
汇编结果
函数名func前有一个前缀字符“_”,这表明该函数采用寄存器传递参数,寄存器R7和R5用来传递参数,计算结果经R7返回。
3.4 应用实例
AD7705的写操作子程序,并给出C51调用格式。
在调用WRITE函数之前先声明该函数为为extern函数,格式如下:extern void WRITE(unisigned char);
C51调用该子程序的格式为 void WRITE(unsigned char command)
四、Keil C51环境下的分页技术
4.1 Keil C51的Bankswitch原理
Keil的BL51使用页面寻址的方式来增加代码空间。EPROM被分页,每页的大小以及在页间进行跳转的方式取决于具体的应用,可以由用户自行设置。
1、Keil C51的分页方式
Keil C51支持的分页方式有三种:
1)利用单片机I/O口线。通常默认是P1口,采用1条P1口线时, 分组数为2,采用5条P1口线时,最多可分为32个代码组,剩余的P1口线也可用于其他用途。
2)利用片外数据存储器(XDATA)空间实现分页。指定一个XDATA端口字节实现分页操作,该字节中的剩余位,不能再用于其他目的。
3)用户自定义方式实现分页。
根据PSD系列器件的特性,本文介绍的是其中的第二种。采用的分页方式、页数和地址在L51_BANK.A51中进行配置。
2、公共空间
公共空间:分页的所有页面均要留有一个相同的存储区域,这个区域包括中断向量、中断功能函数、调用其它EPROM页面的函数、C51库函数、在页面间跳转的代码和被多个页面代码使用的常量及变量.
通常在每页面的底部留作公共空间,保存公共代码。公共空间是处理器在任何时候都能够寻址的。
公共空间是占总存储空间的,若每个页面总存储容量为64KB,公共空间需要10KB存储空间,则该页面给用户实际使用的空间为54KB。若用户使用的页面数为8页,则用户最大使用的代码空间为8×(64-10)+10=442KB。通常页切换需要大约50个机器周期和2字节的堆栈空间。
3、BL51的配置
需要对L51_BANK.A51文件预先进行相关参数的设置,该文件是BL51连接定位器的一部分。
主要有以下几项配置内容:
1)?B_NBANKS EQU X 。定义最大页数,BL51 最大可定义为32页,X值可取为2、4、8、16和32。
2)?B_MODE EQU Y。设置KeilC51的分页方式。Y取值为0、1、4。含义如下:0—通过8051单片机的I/O口进行分组切换;1—通过XDATA存储器单元进行分组切换;4—用户自定义切换方式。
3)?B_RTX EQU Z。设置系统是否使用RTX-51 FULL实时操作系统。Z取值为0、1,分别代表不使用RTX-51及使用RTX-51实时系统。
其它如?B_VAR_BANKING的设置通常使用系统的默认值。
4.2 基于PSD813F2的分页硬件设计
PSD813F2内置128KB闪存,分8个页面,每页16KB,内置可选的32KB启动存储器、2KB的SRAM、27个I/O端口、电源管理单元、40个可编程逻辑宏单元CPLD,通过JTAG串行接口允许在系统编程整个器件。适用于8031、MC68HC11、Dallas、Z80等20余种单片机。
二、Keil C51项目设置
1)建一个KeilC51的新项目,命名为如KC_PSD,在菜单Project选择中选择Option for Target “Target 1”选择项,按图12-4-4所示进行分页及地址配置。
3)新建主程序和各bank文件。
示例
示例的项目主要是为演示BankSwitch,演示结果为单片机的P1.0引脚的发光二极管闪烁。
由主文件psd1.c调用psd2.c中的延时子程序Delay(),psd1.c 存放在第一页(64KB存储空间)中,psd2.c存放在第二页(64KB存储空间)中。
主程序psd1.c 源程序
psd2.c 源程序