单片机C语言及程序设计.docx
《单片机C语言及程序设计.docx》由会员分享,可在线阅读,更多相关《单片机C语言及程序设计.docx(38页珍藏版)》请在冰豆网上搜索。
单片机C语言及程序设计
单片机C语言及程序设计
4.1C51概述
“C51”概念:
为了与ANSIC区别,把“单片机C语言”称为“C51”,也称为“KeilC”。
用汇编语言编写单片机程序时,必须要考虑其存储器的结构,尤其要考虑其片内数据存储器、特殊功能寄存器是否正确合理的使用,以及按照实际地址端口数据的处理。
Ø4.1.2C语言与ANSI的区别
用C51编写程序,虽然不像汇编语言那样需要具体地组织、分配存储器资源,但是C51对数据类型和变量的定义,必须要与单片机的存储结构相关联,否则编译器不能正确地映射定位。
用C51编写单片机程序,与用ANSIC编写程序的不同之处是,需要根据单片机存储器结构及内部资源,定义相应的数据类型和变量。
其它的语法规定、程序结构及程序设计方法,都与ANSIC相同。
所以本章主要介绍C51各种变量的定义、指针定义、函数定义和混合编程。
Ø4.1.3C51扩展的关键字
由于单片机在结构及编程上的特殊要求,C51有自己的特殊关键字,称之为C51扩展的关键字,下面给出常用的C51扩展的关键字。
_at_bdatabitcode
dataidatainterruptpdata
reentrantsbitsfr
sfr16usingvolatilexdata
Ø4.2C51数据类型及存储
表4-1C51数据类型、长度和数值范围
数据类型
表示方法
长度
数值范围
无符号字符型
unsignedchar
1字节
0~255
有符号字符型
signedchar
1字节
-128~127
无符号整型
unsignedint
2字节
0~65535
有符号整型
signedint
2字节
-32768~32767
无符号长整型
unsignedlong
4字节
0~
有符号长整型
signedlong
4字节
-~
浮点型
float
4字节
±1.1755E-38~±3.40E+38
特殊功能寄存器型
Sfr
sfr16
1字节
2字节
0~255
0~65535
位类型
bit、sbit
1位
0或1
数据类型转换
1)自动转换
转换规则是向高精度数据类型转换、向有符号数据类型转换。
如字符型变量与整型变量相加时,则位变量先转换字符型或整型数据,然后相加。
2)强制转换
像ANSIC一样,通过强制类型转换的方式进行转换。
如:
unsignedintb;
floatc;b=(int)c;
Ø4.2.2C51数据的存储
MCS-51单片机只有bit和unsignedchar两种数据类型支持机器指令,而其它类型的数据都需要转换成bit或unsignedchar型进行存储。
为了减少单片机的存储空间和提高运行速度,要尽可能地使用unsignedchar型数据。
一、位变量的存储
bit和sbit型位变量,直接存于RAM的位寻址空间,包括低128位和特殊功能寄存器位。
二、字符变量的存储
字符变量(char):
无论是unsignedchar数据还是signedchar数据,均为1个字节,能够被直接存储在RAM中,可以存储在0~0x7f区域,也可以存储在0x80~0xff区域,与变量的定义有关。
unsignedchar数:
可直接被MSC-51接受
地址
低
高
:
:
34
12
:
:
signedchar数据:
用补码表示。
需要额外的操作来测试、处理符号位,使用的是两种库函数,代码量大,运算速度降低。
三、整型变量的存储
整型变量(int):
不管是unsignedint数据还是signedint数据,
均为2个字节,其存储方法是高位字节保存在低地址(在前面),
低位字节保存在高地址(在后面)。
例如,整型变量的值为0x1234,在内存中的存放如右图所示。
signedint数据用补码表示。
四、长整型变量的存储
长整型变量(long)为4个字节,其存储方法与整型数据一样,
是最高位字节保存的地址最低(在最前面),最低位字节保存的地址最高(在最后面)。
Ø如长整型变量的值为0x,在内存中的存放方法如右图所示。
地址
低
高
:
:
78
56
34
12
:
:
不管是unsignedlong数据还是signedlong数据。
五、浮点型变量的存储
浮点型变量(fload)占4个字节,用指数方式表示,其具体格式与编译器有关。
对于KeilC,采用的是IEEE-754标准,具有24位精度,尾数的最高位始终为1,
因而不保存。
具体分布为:
1位符号位,8位阶码位,23位尾数,如下图所示。
字节地址
0
1
2
3
浮点数
内容
SEEE
EEEE
EMMM
MMMM
MMMM
MMMM
MMMM
MMMM
符号和阶码
尾数高位
尾数低位
符号位S:
1表示负数,0表示正数。
地址
低
高
:
:
00
00
48
C1
:
:
阶码:
用移码表示。
如,实际阶码-126用1表示,实际阶码0用127表示,即实际阶码数加上127得到阶码的表达数。
阶码数值范围:
-126~+128。
例如浮点数-12.5
符号位为1,
12.5的二进制数为1100.1=1.1001E+0011,
阶码数值为3+127=130=B,
尾数为1001。
因此,其十六进制数为0xC,则存储结构如右图所示。
Ø4.3C51变量的定义及数据存储区域
Ø4.3.1C51变量的定义
C51变量定义的一般格式为:
[存储类型]数据类型[存储区]
变量名1[=初值][,变量名2[=初值]][,…]
或[存储类型][存储区]数据类型
变量名1[=初值][,变量名2[=初值]][,…]
可见变量(非位变量)的定义由4部分组成,即在变量定义时,指定变量4种属性。
数据类型:
在前面的4.2中已经叙述过,对于变量名也无须多说,下面主要解释“存储类型”和“存储区”等概念。
Ø4.3.2C51变量的存储类型
存储类型这个属性我们仍沿用ANSIC的说法,尽量不改变原来的含义。
按照ANSIC,C语言的变量有4种存储类型:
动态存储(auto)
静态存储(static)
全局存储(extern)
寄存器存储(register)
一、动态存储
动态(存储)变量:
用auto定义的为动态变量,也叫自动变量。
作用范围:
在定义它的函数内或复合语句内部。
当定义它的函数或复合语句执行时,C51才为变量分配存储空间,结束时所占用的存储空间释放。
定义变量时,auto可以省略,或者说如果省略了存储类型项,则认为是动态变量。
动态变量一般分配使用寄存器或堆栈。
二、静态存储
静态(存储)变量:
用static定义的为静态变量。
分为内部静态和外部静态变量。
内部静态变量:
在函数体内定义的为内部静态变量。
在函数内可以任意使用和修改,函数运行结束后会一直存在,但在函数外不可见,即在函数体外得到保护。
外部静态变量:
在函数体外部定义的为外部静态变量。
在定义的文件内可以任意使用和修改,外部静态变量会一直存在,但在文件外不可见,即在文件外得到保护。
三、外部存储
外部(存储)变量:
用extern声明的变量为外部变量,是在其它文件定义过的全局变量。
用extern声明后,便可以在所声明的文件中使用。
需要注意的是:
在定义变量时,即便是全局变量,也不能使用extern定义。
四、寄存器存储
寄存器(存储)变量:
用register定义的变量为寄存器变量。
寄存器变量存放在CPU的寄存器中,这种变量处理速度快,但数目少。
C51中的寄存器变量:
C51的编译器在编译时,能够自动识别程序中使用频率高的变量,并将其安排为寄存器变量,用户不用专门声明。
Ø4.3.3C51变量的存储区域
变量的存储区属性是单片机扩展的概念,非常重要,它涉及到7个新的关键字。
MCS-51单片机有四个存储空间,分成三类,它们是片内数据存储空间、片外数据存储空间和程序存储空间。
MCS-51单片机有更多的存储区域:
由于片内数据存储器和片外数据存储器又分成不同的区域,所以单片机的变量有更多的存储区域。
在定义变量时,必须明确指出是存放在哪个区域。
表4-2C51存储区与存储空间的对应关系
关键字
对应的存储空间及范围
code
ROM空间,64KB全空间
data
片内RAM,直接寻址,低128字节
bdata
片内RAM,位寻址区0x20~0x2f,可字节访问
idata
片内RAM,间接寻址,256字节,与@Ri对应
pdata
片外RAM,分页寻址的256字节(P2不变),P2
改变可寻址64KB全空间,与MOVX@Ri对应
xdata
片外RAM,64KB全空间
bit
片内RAM位寻找区,位地址0x00~0x7f,128位
Ø4.3.4C51变量定义举例
1)定义存储在data区域的动态unsignedchar变量:
unsignedchardatasec=0,min=0,hou=0;
2)定义存储在data区域的静态unsignedchar变量:
staticunsignedchardatascan_code=0xfe;
3)定义存储在data区域的静态unsignedint变量:
staticunsignedintdatadd;
4)定义存储在bdata区域的动态unsignedchar变量:
unsignedcharbdataoperate,operate1;//定义指示操作的可位寻址的变量
5)定义存储在idata区域的动态unsignedchar数组:
unsignedcharidatatemp[20];
6)定义在pdata区域的动态有符号int数组:
intpdatasend_data[30];//定义存放发送数据的数组
7)定义存储在xdata区域的动态unsignedint数组:
unsignedintxdatareceiv_buf[50];//定义存放接受数据的数组
8)定义存储在code区域的unsignedchar数组:
unsignedcharcodedis_code[10]=
{0x3f,0x06,0x5b,0x4f,0x66,
0x6d,0x7d,0x07,0x7f,0x6f};//定义共阴极数码管段码数组
Ø4.3.5C51变量的存储模式
存储模式:
如果在定义变量时缺省了存储区属性,则编译器会自动选择默认的存储区域,也就是存储模式。
变量的存储模式也就是程序(或函数)的编译模式。
编译模式分为三种:
小模式(small)、紧凑模式(compact)和大模式(large)。
编译模式由编译控制命令决定。
存储模式(编译模式)决定了变量的默认存储区域和参数的传递方法。
一、small模式
在small模式下,变量的默认存储区域是“data”、“idata”,即未指出存储区域的变量保存到片内数据存储器中,并且堆栈也安排在该区域中。
small模式的特点:
存储容量小,但速度快。
在small模式下参数的传递:
通过寄存器、堆栈或片内数据存储区完成的。
二、compact模式
在compact模式下,变量的默认存储区域是“pdata”,即未指出存储区域的变量保存到片外数据存储器的一页中,最大变量数为256字节,并且堆栈也安排在该区域中。
compact模式的其特点:
是存储容量较small模式大,速度较small模式稍慢,但比large模式要快。
在compact模式下参数的传递:
通过片外数据区的一个固定页完成的。
三、large模式
在large模式下,变量的默认存储区域是“xdata”,即未指出存储区域的变量保存到片外数据存储器,最大变量数可达64KB,并且堆栈也安排在该区域中。
large模式的特点:
存储容量大,速度慢
large模式下参数的传递方式:
参数的传递也是通过片外数据存储器完成的。
C51支持混合模式:
即可以对函数设置编译模式,所以在large模式下,可以对某些函数设置为compact模式或small模式,从而提高运行速度。
默认编译模式:
如果文件或函数未指明编译模式,则编译器按small模式处理。
编译模式控制命令:
“#pragmasmall(或compact、large)”应放在文件的开始。
Ø4.3.6C51变量的绝对定位
C51有三种方式可以对变量(I/O端口)绝对定位:
绝对定位关键字_at_、指针、库函数的绝对定位宏。
对于后两种方式,在后面指针一节介绍。
C51扩展的关键字_at_专门用于对变量作绝对定位,_at_使用在变量的定义中,其格式为:
[存储类型]数据类型[存储区]变量名1_at_地址常数[,变量名2…]
举例说明_at_的使用方法
1)对data区域中的unsignedchar变量aa作绝对定位:
unsignedchardataaa_at_0x30;
2)对pdata区域中的unsignedint数组cc作绝对定位:
unsignedintpdatacc[10]_at_0x34;
3)对xdata区域中的unsignedchar变量printer_port作绝对定位:
unsignedcharxdataprinter_port_at_0x7fff;
对变量绝对定位的几点说明:
1)绝对地址变量在定义时不能初始化,因此不能对code型变量绝对定位;
2)绝对地址变量只能够是全局变量,不能在函数中对变量绝对定位;
3)绝对地址变量多用于I/O端口,一般情况下不对变量作绝对定位;
3)位变量不能使用_at_绝对定位。
Ø4.4C51位变量的定义
Ø4.4.1bit型位变量的定义
常说的位变量指的就是bit型位变量。
C51的bit型位变量定义的一般格式为:
[存储类型]bit位变量名1[=初值]
[,位变量名2[=初值]][,…]
bit位变量被保存在RAM中的位寻址区域(字节地址为0x20~0x2f,16字节)。
例如:
bitflag_run,receiv_bit=0;
staticbitsend_bit;
两点说明:
1)bit型位变量与其它变量一样,可以作为函数的形参,也可以作为函数的返回值,即函数的类型可以是位型的;
2)位变量不能定义指针,不能定义数组。
Ø4.4.2sbit型位变量的定义
对于能够按位寻址的特殊功能寄存器、定义在位寻址区域的变量(字节型、整型、长整型),可以对其各位用sbit定义位变量。
为了方便起见,分开讨论按位寻址的特殊功能寄存器中位变量的定义、按位寻址的变量中位变量的定义。
一、特殊功能寄存器中位变量定义
能够按位寻址的特殊功能寄存器中位变量定义的一般格式为:
sbit位变量名=位地址表达式
这里的位地址表达式有三种形式:
直接位地址
特殊功能寄存器名带位号
字节地址带位号
1、用直接位地址定义位变量
这种情况下位变量的定义格式为:
sbit位变量名=位地址常数
这里的位地址常数范围为0x80~0xff,实际是定义特殊功能寄存器的位。
例如:
sbitP0_0=0x80;
sbitP1_1=0x91;
sbitRS0=0xd3;//定义PSW的第3位
sbitET0=0xa9;//定义IE的第1位
2、特殊功能寄存器名带位号定义
这时位变量的定义格式为:
sbit位变量名=特殊功能寄存器名^号常数
这里的位号常数为0~7。
例如:
sbitP0_3=P0^3;
sbitP1_4=P1^4;
sbitOV=PSW^2;//定义PSW的第2位
sbitES=IE^4;//定义IE的第4位
3、寄存器地址带位号定义位变量
在这种情况下位变量的定义格式为:
sbit位变量名=特殊功能寄存器地址^位号常数
这里的位号常数同上,为0~7。
例如:
sbitP0_6=0x80^6;
sbitP1_7=0x90^7;
sbitAC=0xd0^6;//定义PSW的第6位
sbitEA=0xa8^7;//定义IE的第7位
4、几点说明
1)用sbit定义的位变量,必须能够按位操作,而不能够对无位操作功能的位定义位变量。
2)用sbit定义位变量,必须放在函数外面作为全局位变量,而不能在函数内部定义。
3)用sbit每次只能定义一个位变量。
4)对其它模块定义的位变量(bit型或sbit型)的引用声明,都使用bit。
5)用sbit定义的是一种绝对定位的位变量(因为名字是与确定位地址对应的),具有特定的意义,在应用时不能像bit型位变量那样随便使用。
二、位寻址区变量的位定义
对bdata型变量(字节型、整型、长整型),被保存在RAM中的位寻址区,因此可以对bdata型变量各位作位变量定义。
这样,既可以对bdata型变量作字节(或整型、长整型)操作,也可以作位操作。
bdata型变量的位定义格式:
sbit位变量名=bdata型变量名^位号常数
bdata型变量为在此之前应该是定义过的,位号常数可以是0~7(8位字节变量),或0~15(16位整型变量),或0~31(32位字长整型变量)。
例如:
unsignedcharbdataoperate;
对operate的低4位作位变量定义:
sbitflag_key=operate^0;//键盘标志位
sbitflag_dis=operate^1;//显示标志位
sbitflag_mus=operate^2;//音乐标志位
sbitflag_run=operate^3;//运行标志位
Ø4.5C51特殊功能寄存器的定义
Ø4.5.18位特殊功能寄存器的定义
定义的一般格式为:
sfr特殊功能寄存器名=地址常数
地址常数范围:
0x80~0xff。
特殊功能寄存器定义例子(见reg51.h、reg52.h等文件):
sfrP0=0x80;//定义P0寄存器
sfrP1=0x90;//定义P1口寄存器
sfrPSW=0xd0;//定义PSW
sfrIE=0xa8;//定义IE
Ø4.5.216位特殊功能寄存器的定义
定义的一般格式为:
sfr16特殊功能寄存器名=地址常数
地址常数范围:
0x80~0xff。
例如(见reg51.h、reg52.h等文件):
sfr16DPTR=0x82;
sfr16T2=0xcc;//含TL2和TH2
sfr16RCAP2=0xca;//含RCAP2L//和RCAP2H,0xca为RCAP2L的地址
几点说明:
1)定义特殊功能寄存器中的地址必须在0x80~0xff范围内。
2)定义特殊功能寄存器,必须放在函数外面作为全局变量。
3)用sfr或sfr16每次只能定义一个特殊功能寄存器。
4)像sbit一样,用sfr或sfr16定义的是绝对定位的变量(因为名字是与确定地址对应的),具有特定的意义,在应用时不能像一般变量那样随便使用。
Ø4.6C51指针的定义
Ø4.6C51指针的定义
由于MCS-51单片机有三种不同类型的存储空间,并且还有不同的存储区域,因此C51指针的内容更丰富。
指针除了具有像变量的四种属性(存储类型、数据类型、存储区、变量名)外,按存储区,将指针分为通用指针和不同存储区域的专用指针。
Ø4.6.1通用指针
所谓通用指针,就是通过该类指针可以访问所有的存储空间。
在C51库函数中通常使用这种指针来访问。
通用指针用3个字节来表示:
第一个字节:
表示指针所指向的存储空间
第二个字节:
为指针地址的高字节
第三个字节:
为指针地址的低字节
通用指针的定义与一般C语言指针的定义相同,其格式为:
[存储类型]数据类型*指针名1[,*指针名2][,…]
通用指针的特点:
Ø定义简单
Ø访问所有空间
Ø访问速度慢
例如:
unsignedchar*cpt;
int*dpt;
long*lpt;
staticchar*ccpt;
Ø4.6.2存储器专用指针
所谓存储器专用指针,就是通过该类指针,只能够访问规定的存储空间区域。
指针本身占用1个字节(data*,idata*,bdata*,pdata*)或2个字节(xdata*,code*)。
存储器专用指针的一般定义格式为:
[存储类型]数据类型指向存储区
*[指针存储区]指针名1[,*[指针存储区]指针名2,…]
指向存储区:
是指针变量所指向的数据存储空间区域。
不能够缺省。
指针存储区:
是指针变量本身所存储的空间区域。
缺省时认为指针存储区在默认的存储区域,其默认存储区域决定于所设定的编译模式。
指向和指针存储区,两者可以是同一个区域,但多数情况下不会是同一个区域。
存储器专用指针例子:
unsignedchardata*cpt1,*cpt2;
signedintidata*dpt1,*dpt2;
unsignedcharpdata*ppt;
signedlongxdata*lpt1,*lpt2;
unsignedcharcode*ccpt;
上面所定义的指针虽然所指向的空间不同,但指针变量本身都存储在默认的存储区域。
又如:
1)unsignedchardata*idatacpt1,*idatacpt2;
2)signedintidata*datadpt1,*datadpt2;
3)unsignedcharpdata*xdatappt;
4)signedlongxdata*lpt1,*xdatalpt2;
5)unsignedcharcode*dataccpt;
前关键字为指针所指向的存储区
后关键字为指针本身所存储的区域
注意:
(1)要区分指针变量指向的空间区域和指针变量本身所存储的区域;
(2)定义时,前者不能缺省,而后者可以缺省;(3)指针变量的长度:
指向不同的区域,占用的字节数不同。
说明:
指针