第 8章 Cx51的数据类型及基本运算用汇编语言对单片机进行编程其优点是目标程序长度短、速度快,可直接对硬件进行操作,因而在不太复杂的工业控制中得到广泛应用。但使用汇编的麻烦在于它的可读性和可维护性,特别是当程序没有很好注释的时候;汇编程序的移植性也较差;并且,对复杂的控制,
汇编语言难以胜任。使用 C语言可以很好的解决这些问题。
本章主要介绍国内比较流行的 Keil 公司的编译器 Cx51 所支持的数据类型、运算规则。
8.1 C语言程序的基本结构下面用实例说明 C语言程序的基本结构。
/ *
*filename,simple.c
*date,04-Aug.-2006
*/
#include<stdio.h>
#include<reg51.h>
main()
{
unsigned char a,b,c,sum;
a = 5;
b = 3;
SCON = 0x52;
TMOD = 0x20;
TCON = 0x69;
TH1 = 0x0F3; scanf("% d",&c); /* input a decimal interger */
sum = a + b + c;
printf( "sum= %d\n",sum) ; /* output sum */

这个程序的功能是已知两个数 a和 b,输入第三个数,然后求和并输出结果。
在 C语言中,通过 /* …… */ 作为程序的注释部分,当不能满足注解要求时要另起一行,一般要求以 *开头。注释可出现在程序的任何部分。
为了编写程序和阅读程序方便,本书中的 C语言程序都有注释部分,一般在程序前后各有一部分,
程序前面的注释一般表示程序的文件名、以及建立的日期,这样便于从外存储器中找到文件;程序后面的注解一般表示程序的运行情况,输入情况,输出情况,标准的 Cx51 编译器的输入输出为单片机串行通信口。
在本例程序的开始处使用了预处理命令 #include,它告诉编译器在编译时将头文件 stdio.h和 reg51.h读入后一起编译。在头文件 stdio,h中包括了对标准输入输出函数的说明,在头文件 reg51,h中包括了对 8051单片机特殊功能寄存器的说明。所有的头文件说明均位于
\keil\C51\INC\。
本程序的主体部分为
main()
{


其中 main是一个函数,而且是一个特殊的函数,所有
C语言程序都包含 main函数。它实质上是 C语言程序的首部。一般函数名后面为参数表,参数表在一对圆括号()
之中。 main函数可以有参数。本程序中的 main函数没有参数,即参数表为空,但一对圆括号()必须有,不能省去。
{ }是一对花括号,将构成函数的语句包括起来。 C语言中的语句大致分为两类;一类为说明语句,用来描述数据,决定内存的分配; 另一类为执行语句,用来描述对数据进行的动作,决定内存的内容。
本程序共有 10个语句:
unsigned char a,b,c,sum;是说明语句。它说明 a,b,c,sum
四个变量都是 unsigned char(长度为 8位 ) 类型的变量;
a=5; b=3;是两个赋值语句。等号 = 作为赋值运算符,它们分别将 5和 3 赋给变量 a 和 b;
SCON= 0x52; TMOD= 0x20; TCON = 0x69; TH1 =
0x0F3;四句是对 8051的串行口进行初始化,因为 Keil Cx51 提供的标准的输入输出函数都是通过 8051的串口来进行的,因此必须首先对串口进行初始化。
scanf( "%d",& c),是一个输入语句。它按十进制(由 d指明)
给变量 c 输入一个长度为 8位的整数,在程序执行时等待用户输入。
本程序中输入 5。
sum = a + b + c;是一个赋值语句。它将三个变量的值求和,再赋值给 sum。
printf ("sum= %d\n",sum ); 是一个输出语句。它首先输出字符串 sum =,然后按十进制(由 d指明)输出变量 sum 的值。在本程序中输出 sum = 13。
综上所述,C语言程序的基本结构为:
预处理命令
main()
{
语句;
}
其中语句必须以分号结尾,因为分号是语句的终止符,它属于语句的一个组成部分。
标识符标识符是用来标识 C语言源程序中函数、变量、常量、
数组、数据类型、存储方式、语句等对象。
关键字关键字是一类具有固定名称和特定含义的特殊标识符,
有时又称为保留字。在编写 C语言源程序时一般不允许将关键字另作别用,换句话说就是对于标识符的命名不要与关键字相同。与其它计算机语言相比,C语言的关键字是比较少的,ANSI C标准一共规定了 32个关键字。
8.2 Keil Cx51标识符与关键字
数据是具有一定格式的数字或数值,数据是计算机操作的对象。不管使用任何语言、
何种算法进行程序设计,最终在计算机中运行的只有数据流。
数据的不同格式叫做数据类型。
数据按一定的数据类型进行的排列、组合、
架构称为数据结构。
Cx51提供的数据结构是以数据类型的形式出现的。
8.3数据与数据类型
Cx51编译器具体支持的数据类型有:位型
( bit)、无符号字符( unsigned char)、有符号字符 (signed char)、无符号整型 (unsigned
int)、有符号整型 (signed int)、无符号长整型
( unsigned long)、有符号长型 (signed long)、
浮点 (float)和指针类型等。由于 8051是 8位机,
因而不存在字节对准问题。这意味着数据结构成员是顺序放置的。
数据类型的转换:当计算结果隐含着另外一种数据类型时,数据类型可以自动进行转换。例如,
将一个位变量赋给一个整型变量时,位型值自动转换为整值,有符号的变量也能自动进行处理。
这些转换也可以用 C语言的标准指令进行人工转换。
8.4常量与变量
常量,在程序运行的过程中,其值不能改变的量称为常量。常量可以有不同的数据类型。如 0,1,
2,-3为整型常量; 4,6,-1.23等为实型常量;
‘ a’,‘ b’为字符型常量。可以用一个标识符号代表一个常量。
变量,在程序运行中,其值可以改变的量称为变量。一个变量主要由两部分构成:一个是变量名,
一个是变量值。每个变量都有一个变量名,在内存中占据一定的存储单元(地址),并在该内存单元中存放该变量的值。
8.5 Cx51数据的存储类型
KEIL Cx51编译器完全支持 8051单片机的硬件结构,可完全访问 8051
硬件系统的所有部分。该编译器通过将变量、常量定义成不同的存储类型
(data,bdata,idata,pdata,xdata,code)的方法,将它们定位在、不同的存储区中。
存储类型 与存储空间对应关系
data 直接寻址片内数据存储区,访问速度快( 128字节)
bdata 可位寻址片内数据存储器,允许位与字节混合访问( 16字节)
idata 间接寻址片内数据存储区,可访问片内全部 RAM地址空间( 256字节)
pdata 分页寻址片外数据存储区( 256字节),由 MOVX @R0访问
xdata 片外数据存储区( 64KB),由 MOVX @DPTR访问
code 代码存储区( 64KB),由 MOVC @DPTR访问存储类型与 8051单片机实际存储空间的对应关系表
8.6 Cx51编译器对特殊功能寄存 (SFR)的定义定义的方法是引入关键字,sfr”,语法如下:
sfr sfr_name= int constant;
例 sfr P1 = 0x90; /* P1口地址 90H*/
sfr TMOD = 0x89; /*定时器 /计数器方式控制寄存器地址 89H */
注意,sfr后面必须跟一个特殊寄存器名,,=”后面的地址必须是常数,不允许带有运算符的表达式,这个常数值的范围必须在特殊功能寄存器地址范围内,位于 0x80- 0xFF之间。
16位 SFR定义的语法与 8位 SFR相同,16位
SFR的低端地址必须作为,sfr16”的定义地址。

sfr16 T2 = 0xCC; /* 定时器 2,T2低 8位地址 =
0CCH
T2高 8位地址 = 0CDH
*/
定义中名字后面不是赋值语句,而是一个
SFR地址,高字节必须位于低字节之后。这种定义适用于所有新的 SFR,但不能用于定时器 /计数器 0和 1。
8.7 Cx51对 8051并行接口的定义
对于 8051片内 I/O口用关键字 sfr来定义。

sfr P0 =0x80; /*定义 P0口,地址 80H */
sfr P1 = 0x90; /* 定义 P1口,地址 90H */
对于片外扩展 I/O口,则根据其硬件译码地址,将其视为片外数据存储器的一个单元,使用# define语句进行定义:

# include< absacc,h >
# define PORTA XBYTE[ 0x0FFC0 ] /* 将 PORTA定义为外部 I/O口,地址为 0x0FFC0,长度为 8位 */
PORTA = 0x01; /* 向外部 I/O口输出数据 */
8.8位变量( BIT)及其 Cx51定义
位变量的 Cx51定义的语法及语义:
bit driverP1; /* 将 driverP1定义为位变量 */
bit led- pointer; /* 将 led-pointer定义为位变量 */
bit QQ-number; /* 将 QQ-number定义为位变量 */
函数可包含类型为 bit的参数,也可以将其作为返回值。

bit func( bit b0,bit b1)
{ /*… */
return( b1);
}
对位变量定义的限制:位变量不能定义成一个指针,
如不能定义
bit *led-pointer;
也不存在位数组,如不能定义
bit b_array[ ];
可位寻址对象指可以字节或位寻址的对象。
例 先定义变量的数据类型和存储类型:
bdata int ibase; /* ibase定义为 bdata整型变量 */
bdata char bary[4]; /* bary[ 4]定义为 bdata字符型数组 */
然后可使用,sbit”定义可独立寻址访问的对象位,即
sbit mybit0 = ibase^0; /* mybit0定义为 ibase的第 0位 * /
sbit Ary07 = bary[0]^7; /* Ary07定义为 bary[0]的第 7位 */
对象,ibase”,,bary”也可以字节寻址。
例 Ary37 = 0; /* bary[3]的第 7位赋值为 0 */
bary[3] ='a'; /*字节寻址,bary[3]赋值为 'a' */
8.9 Cx51运算符、表达式及其规则
Cx51算术运算符及其表达式
Cx51提供最基本的五种算术运算符,+、-,*、/、
% 模(求余,例如 9% 5结果是 9除以 5所得的余数 4)。
用算术运算符和括号将运算对象连接起来的式子称为算术表达式。其中的运算对象包括常量、变量、函数、数组和结构等。
算术运算符的优先级规定为:先乘除模,后加减,括号最优先。即在算术运算符中,乘、除、模运算符的优先级相同,并高于加减运算符。在表达式中若出现括号,则括号中的内容优先级最高。
Cx51关系运算符、表达式及优先级
Cx51有六种关系运算符:<小于、>大于、< =小于或等于、> =大于或等于,= = 测试等于,!= 测试不等于。
前四种关系运算符(<,>,< =,> =优先级相同,后两种也相同;前四种优先级高于后两种。
关系运算符的优先级低于算术运算符,但高于赋值运算符。

c>a+b等效于 c>(a+b)
a> b != c等效于( a>b)!= c
a = b<c等效于 a = (b<c)
a = b>c等效于 a =(b>c)
Cx51逻辑运算符、表达式及优先级
Cx51有三种逻辑运算符,&& (逻辑,与,AND),| |
(逻辑,或,OR),! (逻辑,非,NOT)。
,&&”和,| |”是双目运算符,要求有两个运算对象;而,!,
是单目运算符,只要求有一个运算对象。
Cx51逻辑运算符与算术运算符、关系运算符和赋值运算符之间优先级的次序如下图所示。其中,!,(非)运算符优先级最高,算术运算符次之,关系运算符再次之,,&&,
和,| |”再再次之,最低为赋值运算符。
! ( 非 )
算 术 运 算 符关 系 运 算 符
& & 和 ||
赋 值 运 算 符优 先 级
( 低 )
( 高 )
Cx51位操作及其表达式
C x51提供了 6种位操作运算符,&(按位与),| (按位或),^(按位异或)、~( 按位取反)、<<
(位左移)、>> (位右移)。
除了按位取反运算符,~,以外,以上位操作运算符都是两目运算符,即要求运算符两侧各有一个运算对象。
位运算符只能是整型或字符型数,不能为实型数据。
移位与数学运算:对于二进制数来说,左移一位相当于该数乘 2,而右移一位相当于该数除 2,利用这一性质可以用移位来做快速乘除法。例如,若要对某数乘 10,则使用这种方法将比直接做乘法更有效率,即先将该数左移两位再与该数本身相加,然后再左移一位。
自增减运算符,复合运算符及其表达式
自增减运算符自增减运算符的作用是使变量值自动加 1或减 1。
如:
++(--) i,在使用 i之前,先使 i值加(减) 1。
i++,i--,在使用 i之后,再使 i值加(减) 1。
复合运算符及其表达式凡是二目运算符,都可以与赋值运算符,=”一起组成复合赋值运算符。 Cx51共提供了 10种复合赋值运算符,
即,+=,- =,*=,/ =,% =,<< =,>> =,&=,
^=,|=。