还剩14页未读,继续阅读
本资源只提供10页预览,全部文档请下载后查看!喜欢就下载吧,查找使用更方便
文本内容:
单片机C语言及程序设计
4.1C51概述“C51”概念为了与ANSIC区别,把“单片机C语言”称为“C51”,也称为“KeilC”用汇编语言编写单片机程序时,必须要考虑其存储器的结构,尤其要考虑其片内数据存储器、特殊功能寄存器是否正确合理的使用,以及按照实际地址端口数据的处理C语言与ANSI的区别用C51编写程序,虽然不像汇编语言那样需要具体地组织、分配存储器资源,但是C51对数据类型和变量的定义,必须要与单片机的存储结构相关联,否则编译器不能正确地映射定位用C51编写单片机程序,与用ANSIC编写程序的不同之处是,需要根据单片机存储器结构及内部资源,定义相应的数据类型和变量其它的语法规定、程序结构及程序设计方法,都与ANSIC相同所以本章主要介绍C51各种变量的定义、指针定义、函数定义和混合编程C51扩展的关键字由于单片机在结构及编程上的特殊要求,C51有自己的特殊关键字,称之为C51扩展的关键字,下面给出常用的C51扩展的关键字_at_bdatabitcodedataidatainterruptpdatareentrant__itsfrsfr16usingvolatilexdata
4.2C51数据类型及存储表4-1C51数据类型、长度和数值范围数据类型表示方法长度数值范围无符号字符型unsignedchar1字节0~255有符号字符型signedchar1字节-128~127无符号整型unsignedint2字节0~65535有符号整型signedint2字节-32768~32767无符号长整型unsignedlong4字节0~4294967295有符号长整型signedlong4字节-2147483648~2147483647浮点型float4字节±
1.1755E-38~±
3.40E+38特殊功能寄存器型Sfrsfr161字节2字节0~2550~65535位类型bit、__it1位0或1数据类型转换1)自动转换转换规则是向高精度数据类型转换、向有符号数据类型转换如字符型变量与整型变量相加时,则位变量先转换字符型或整型数据,然后相加2)强制转换像ANSIC一样,通过强制类型转换的方式进行转换如unsignedintb;floatc;b=intc;C51数据的存储MCS-51单片机只有bit和unsignedchar两种数据类型支持机器指令,而其它类型的数据都需要转换成bit或unsignedchar型进行存储为了减少单片机的存储空间和提高运行速度,要尽可能地使用unsignedchar型数据
一、位变量的存储bit和__it型位变量,直接存于RAM的位寻址空间,包括低128位和特殊功能寄存器位
二、字符变量的存储字符变量(char)无论是unsignedchar数据还是signedchar数据,均为1个字节,能够被直接存储在RAM中,可以存储在0~0x7f区域,也可以存储在0x80~0xff区域,与变量的定义有关unsignedchar数可直接被MSC-51接受signedchar数据用补码表示需要额外的操作来测试、处理符号位,使用的是两种库函数,代码量大,运算速度降低
三、整型变量的存储整型变量(int)不管是unsignedint数据还是signedint数据,均为2个字节,其存储方法是高位字节保存在低地址(在前面),低位字节保存在高地址(在后面)例如,整型变量的值为0x1234,在内存中的存放如右图所示signedint数据用补码表示
四、长整型变量的存储长整型变量(long)为4个字节,其存储方法与整型数据一样,是最高位字节保存的地址最低(在最前面),最低位字节保存的地址最高(在最后面)如长整型变量的值为0x12345678,在内存中的存放方法如右图所示不管是unsignedlong数据还是signedlong数据
五、浮点型变量的存储浮点型变量(fload)占4个字节,用指数方式表示,其具体格式与编译器有关对于KeilC,采用的是IEEE-754标准,具有24位精度,尾数的最高位始终为1,因而不保存具体分布为1位符号位,8位阶码位,23位尾数,如下图所示字节地址0123浮点数内容SEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMM 符号和阶码尾数高位 尾数低位符号位S1表示负数,0表示正数阶码用移码表示如,实际阶码-126用1表示,实际阶码0用127表示,即实际阶码数加上127得到阶码的表达数阶码数值范围-126~+128例如浮点数-
12.5符号位为1,
12.5的二进制数为
1100.1=
1.1001E+0011,阶码数值为3+127=130=_____010B,尾数为1001因此,其十六进制数为0xC1480000,则存储结构如右图所示
4.3C51变量的定义及数据存储区域C51变量的定义C51变量定义的一般格式为[存储类型]数据类型[存储区]变量名1[=初值][变量名2[=初值]][…]或[存储类型][存储区]数据类型变量名1[=初值][变量名2[=初值]][…]可见变量(非位变量)的定义由4部分组成,即在变量定义时,指定变量4种属性数据类型在前面的
4.2中已经叙述过,对于变量名也无须多说,下面主要解释“存储类型”和“存储区”等概念C51变量的存储类型存储类型这个属性我们仍沿用ANSIC的说法,尽量不改变原来的含义按照ANSIC,C语言的变量有4种存储类型动态存储(auto)静态存储(static)全局存储(extern)寄存器存储(register)
一、动态存储动态(存储)变量用auto定义的为动态变量,也叫自动变量作用范围在定义它的函数内或复合语句内部当定义它的函数或复合语句执行时,C51才为变量分配存储空间,结束时所占用的存储空间释放定义变量时,auto可以省略,或者说如果省略了存储类型项,则认为是动态变量动态变量一般分配使用寄存器或堆栈
二、静态存储静态(存储)变量用static定义的为静态变量分为内部静态和外部静态变量内部静态变量在函数体__义的为内部静态变量在函数内可以任意使用和修改,函数运行结束后会一直存在,但在函数外不可见,即在函数体外得到保护外部静态变量在函数体外部定义的为外部静态变量在定义的文件内可以任意使用和修改,外部静态变量会一直存在,但在文件外不可见,即在文件外得到保护
三、外部存储外部(存储)变量用extern声明的变量为外部变量,是在其它文件定义过的全局变量用extern声明后,便可以在所声明的文件中使用需要注意的是在定义变量时,即便是全局变量,也不能使用extern定义
四、寄存器存储寄存器(存储)变量用register定义的变量为寄存器变量寄存器变量存放在CPU的寄存器中,这种变量处理速度快,但数目少C51中的寄存器变量C51的编译器在编译时,能够自动识别程序中使用频率高的变量,并将其安排为寄存器变量,用户不用专门声明C51变量的存储区域变量的存储区属性是单片机扩展的概念,非常重要,它涉及到7个新的关键字MCS-51单片机有四个存储空间,分成三类,它们是片内数据存储空间、片外数据存储空间和程序存储空间MCS-51单片机有更多的存储区域由于片内数据存储器和片外数据存储器又分成不同的区域,所以单片机的变量有更多的存储区域在定义变量时,必须明确指出是存放在哪个区域表4-2C51存储区与存储空间的对应关系关键字对应的存储空间及范围codeROM空间,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位C51变量定义举例1)定义存储在data区域的动态unsignedchar变量unsignedchardatasec=0min=0hou=0;2)定义存储在data区域的静态unsignedchar变量staticunsignedchardatascan_code=0xfe;3)定义存储在data区域的静态unsignedint变量staticunsignedintdatadd;4)定义存储在bdata区域的动态unsignedchar变量unsignedcharbdataoperateoperate1;//定义指示操作的可位寻址的变量5)定义存储在idata区域的动态unsignedchar数组unsignedcharidatatemp
[20];6)定义在pdata区域的动态有符号int数组intpdatasend_data
[30];//定义存放发送数据的数组7)定义存储在xdata区域的动态unsignedint数组unsignedintxdatare__iv_buf
[50];//定义存放接受数据的数组8)定义存储在code区域的unsignedchar数组unsignedcharcodedis_code
[10]={0x3f0x060x5b0x4f0x660x6d0x7d0x070x7f0x6f};//定义共阴极数码管段码数组C51变量的存储模式存储模式如果在定义变量时缺省了存储区属性,则编译器会自动选择默认的存储区域,也就是存储模式变量的存储模式也就是程序(或函数)的编译模式编译模式分为三种小模式(__all)、紧凑模式(compact)和大模式(large)编译模式由编译控制命令决定存储模式(编译模式)决定了变量的默认存储区域和参数的传递方法
一、__all模式在__all模式下,变量的默认存储区域是“data”、“idata”,即未指出存储区域的变量保存到片内数据存储器中,并且堆栈也安排在该区域中__all模式的特点存储容量小,但速度快在__all模式下参数的传递通过寄存器、堆栈或片内数据存储区完成的
二、compact模式在compact模式下,变量的默认存储区域是“pdata”,即未指出存储区域的变量保存到片外数据存储器的一页中,最大变量数为256字节,并且堆栈也安排在该区域中compact模式的其特点是存储容量较__all模式大,速度较__all模式稍慢,但比large模式要快在compact模式下参数的传递通过片外数据区的一个固定页完成的
三、large模式在large模式下,变量的默认存储区域是“xdata”,即未指出存储区域的变量保存到片外数据存储器,最大变量数可达64KB,并且堆栈也安排在该区域中large模式的特点存储容量大,速度慢large模式下参数的传递方式参数的传递也是通过片外数据存储器完成的C51支持混合模式即可以对函数设置编译模式,所以在large模式下,可以对某些函数设置为compact模式或__all模式,从而提高运行速度默认编译模式如果文件或函数未指明编译模式,则编译器按__all模式处理编译模式控制命令“#prag____all或compact、large”应放在文件的开始C51变量的绝对定位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位变量的定义bit型位变量的定义常说的位变量指的就是bit型位变量C51的bit型位变量定义的一般格式为[存储类型]bit位变量名1[=初值][,位变量名2[=初值]][,…]bit位变量被保存在RAM中的位寻址区域(字节地址为0x20~0x2f,16字节)例如bitflag_run,re__iv_bit=0;staticbitsend_bit;两点说明1)bit型位变量与其它变量一样,可以作为函数的形参,也可以作为函数的返回值,即函数的类型可以是位型的;2)位变量不能定义指针,不能定义数组__it型位变量的定义对于能够按位寻址的特殊功能寄存器、定义在位寻址区域的变量(字节型、整型、长整型),可以对其各位用__it定义位变量为了方便起见,分开讨论按位寻址的特殊功能寄存器中位变量的定义、按位寻址的变量中位变量的定义
一、特殊功能寄存器中位变量定义能够按位寻址的特殊功能寄存器中位变量定义的一般格式为__it位变量名=位地址表达式这里的位地址表达式有三种形式直接位地址特殊功能寄存器名带位号字节地址带位号
1、用直接位地址定义位变量这种情况下位变量的定义格式为__it位变量名=位地址常数这里的位地址常数范围为0x80~0xff,实际是定义特殊功能寄存器的位例如__itP0_0=0x80;__itP1_1=0x91;__itRS0=0xd3;//定义PSW的第3位__itET0=0xa9;//定义IE的第1位
2、特殊功能寄存器名带位号定义这时位变量的定义格式为__it位变量名=特殊功能寄存器名^号常数这里的位号常数为0~7例如__itP0_3=P0^3;__itP1_4=P1^4;__itOV=PSW^2;//定义PSW的第2位__itES=IE^4;//定义IE的第4位
3、寄存器地址带位号定义位变量在这种情况下位变量的定义格式为__it位变量名=特殊功能寄存器地址^位号常数这里的位号常数同上,为0~7例如__itP0_6=0x80^6;__itP1_7=0x90^7;__itAC=0xd0^6;//定义PSW的第6位__itEA=0xa8^7;//定义IE的第7位
4、几点说明1)用__it定义的位变量,必须能够按位操作,而不能够对无位操作功能的位定义位变量2)用__it定义位变量,必须放在函数外面作为全局位变量,而不能在函数内部定义3)用__it每次只能定义一个位变量4)对其它模块定义的位变量(bit型或__it型)的引用声明,都使用bit5)用__it定义的是一种绝对定位的位变量(因为名字是与确定位地址对应的),具有特定的意义,在应用时不能像bit型位变量那样随便使用
二、位寻址区变量的位定义对bdata型变量(字节型、整型、长整型),被保存在RAM中的位寻址区,因此可以对bdata型变量各位作位变量定义这样,既可以对bdata型变量作字节(或整型、长整型)操作,也可以作位操作bdata型变量的位定义格式__it位变量名=bdata型变量名^位号常数bdata型变量为在此之前应该是定义过的,位号常数可以是0~7(8位字节变量),或0~15(16位整型变量),或0~31(32位字长整型变量)例如unsignedcharbdataoperate;对operate的低4位作位变量定义__itflag_key=operate^0;//键盘标志位__itflag_dis=operate^1;//显示标志位__itflag_mus=operate^2;//音乐标志位__itflag_run=operate^3;//运行标志位
4.5C51特殊功能寄存器的定义位特殊功能寄存器的定义定义的一般格式为sfr特殊功能寄存器名=地址常数地址常数范围0x80~0xff特殊功能寄存器定义例子(见reg
51.h、reg
52.h等文件)sfrP0=0x80;//定义P0寄存器sfrP1=0x90;//定义P1口寄存器sfrPSW=0xd0;//定义PSWsfrIE=0xa8;//定义IE位特殊功能寄存器的定义定义的一般格式为sfr16特殊功能寄存器名=地址常数地址常数范围0x80~0xff例如(见reg
51.h、reg
52.h等文件)sfr16DPTR=0x82;sfr16T2=0xcc;//含TL2和TH2sfr16RCAP2=0xca;//含RCAP2L//和RCAP2H0xca为RCAP2L的地址几点说明1)定义特殊功能寄存器中的地址必须在0x80~0xff范围内2)定义特殊功能寄存器,必须放在函数外面作为全局变量3)用sfr或sfr16每次只能定义一个特殊功能寄存器4)像__it一样,用sfr或sfr16定义的是绝对定位的变量(因为名字是与确定地址对应的),具有特定的意义,在应用时不能像一般变量那样随便使用
4.6C51指针的定义
4.6C51指针的定义由于MCS-51单片机有三种不同类型的存储空间,并且还有不同的存储区域,因此C51指针的内容更丰富指针除了具有像变量的四种属性(存储类型、数据类型、存储区、变量名)外,按存储区,将指针分为通用指针和不同存储区域的专用指针通用指针所谓通用指针,就是通过该类指针可以访问所有的存储空间在C51库函数中通常使用这种指针来访问通用指针用3个字节来表示第一个字节表示指针所指向的存储空间第二个字节为指针地址的高字节第三个字节为指针地址的低字节通用指针的定义与一般C语言指针的定义相同,其格式为[存储类型]数据类型*指针名1[,*指针名2][,…]例如unsignedchar*cpt;int*dpt;long*lpt;staticchar*ccpt;存储器专用指针所谓存储器专用指针,就是通过该类指针,只能够访问规定的存储空间区域指针本身占用1个字节(data*,idata*,bdata*,pdata*)或2个字节(xdata*,code*)存储器专用指针的一般定义格式为[存储类型]数据类型指向存储区*[指针存储区]指针名1[*[指针存储区]指针名2…]指向存储区是指针变量所指向的数据存储空间区域不能够缺省指针存储区是指针变量本身所存储的空间区域缺省时认为指针存储区在默认的存储区域,其默认存储区域决定于所设定的编译模式指向和指针存储区,两者可以是同一个区域,但多数情况下不会是同一个区域存储器专用指针例子unsignedchardata*cpt1*cpt2;signedintidata*dpt1*dpt2;unsignedcharpdata*ppt;signedlongxdata*lpt1*lpt2;unsignedcharcode*ccpt;上面所定义的指针虽然所指向的空间不同,但指针变量本身都存储在默认的存储区域又如1unsignedchardata*idatacpt1*idatacpt2;2signedintidata*datadpt1*datadpt2;3unsignedcharpdata*xdatappt;4signedlongxdata*lpt1*xdatalpt2;5unsignedcharcode*dataccpt;前关键字为指针所指向的存储区后关键字为指针本身所存储的区域注意
(1)要区分指针变量指向的空间区域和指针变量本身所存储的区域;
(2)定义时,前者不能缺省,而后者可以缺省;
(3)指针变量的长度指向不同的区域,占用的字节数不同说明指针变量本身所存储的区域,在定义指针时一般都省略了,指针变量本身保存在缺省存储的区域中定义时,缺省指针存储的区域,显得简单,并且对初学者更容易理解指针变换
一、通用指针格式由前面的讨论知,通用指针由3个字节组成,第一个字节为数据的存储区域,后两个字节为指针地址,第一个字节的存储区域编码如表4-6所示表4-6通用指针存储区域编码存储区idataxdatapdatadatacode编码12345
一、指针转换指针转换有两种途径,一种是显式的编程转换,另一种是隐式的自动转换指针的编程转换
(1)通用指针的第一字节,与专用指针的指向数据区属性,二者相互转换;
(2)通用指针后两个字节的地址,与专用指针值的转换指针的隐式自动转换由编译器在进行编译时自动完成C51指针应用指针在PC机上的C语言中应用很广泛在单片机中,由于不使用操作系统,指针的应用可以__于变量,__地指向所需要访问的存储空间位置本节通过例子来学习和认识C51指针的这种__应用性下面介绍两种利用指针访问存储区的方法也可以访问函数
二、通过指针定义的宏访问存储器
1、访问存储器宏的定义用指针定义的、访问存储器宏的格式#define宏名数据类型volatile存储区*0格式中的数据类型主要为无符号的字符型数、整型;格式中的存储区域主要使用data、idata、pdata、xdata和code类型,不使用bdata存储区类型格式中的关键字“volatile”“volatile”是单片机中定义的,其含义为这种变量在程序执行中可被隐含地改变而编译器无法检测到,告知编译器不要做优化处理,使应用者能够得到正确的变量值volatile的应用volatile常用于定义寄存器,特别是状态寄存器,因为状态寄存器的值不是程序员设置,而是单片机在运行中CPU设置的特别说明“volatile”的含义与教材上表述不太一致,此处表述直观更容易理解
2、库函数中访问存储器宏的原型C51编译器提供了两组用指针定义的绝对存储器访问的宏,其原型如下1)按字节访问存储器的宏#defineCBYTEunsignedcharvolatilecode*0#defineDBYTEunsignedcharvolatiledata*0#definePBYTEunsignedcharvolatilepdata*0#defineXBYTEunsignedcharvolatilexdata*02)按整型双字节访问存储器的宏#define__ORDunsignedintvolatilecode*0#defineDWORDunsignedintvolatiledata*0#definePWORDunsignedintvolatilepdata*0#defineXWORDunsignedintvolatilexdata*0无idata型,不能访问片内RAM高128字节区域(0x80~0xff),需要时可以自己定义这些宏定义原型放在absacc.__件中,使用时需要用预处理命令把该头文件包含到文件中,形式为#includeabsacc.h
3、绝对访问存储器宏的应用使用宏定义访问存储器的形式类似于数组1)按字节访问存储器宏的形式宏名[地址]即数组中的下标就是存储器的地址,因此使用起来非常方便例如DBYTE[0x30]=48;//给片内RAM送数据XBYTE[0x0002]=0x36;//给片外RAM送数据dis_buf
[0]=CBYTE[TABLE+5];//从CODE区读取数据2)按整型数访问存储器宏的形式宏名[下标]由于整型数占两个字节,所以下标与地址的关系为地址=下标×2由于数组中的下标与存储器的地址是倍数关系,使用时要注意例如DWORD[0x20]=0x1234;//给0x
40、0x41送数XWORD[0x0002]=0x5678;//给
4、5单元送数通过指针定义的宏访问存储器这种方法,特别适用于访问I/O口
一、通过专用指针直接访问存储器使用指针直接访问存储器对PC机是禁止的,但对于单片机来说使用时注意是可以的使用指针直接访问存储器方法是先定义所需要的指针,给指针赋地址值,然后使用指针访问存储器例如unsignedcharxdata*xcpt;xcpt=0x2000;*xcpt=123;//给0x2000送数xcpt++;*xcpt=234;//给0x2001送数例4-1编写程序,将单片机片外数据存储器中地址从0x1000开始20个字节数据,传送到片内数据存储器地址从0x30开始的区域程序段如下unsignedchardatai*dcpt;unsignedcharxdata*xcpt;dcpt=0x30;//给指针赋地址xcpt=0x1000;fori=0;i20;i++*dcpt+i=*xcpt+i;例4-2在数字滤波中有一种叫做“中值滤波”技术,就是对采集的数据按照从大到小或者从小到大进行排序,然后取中间位置的数作为采样值试编写一函数,对存放在片内数据存储器中,从0x50开始的21个单元的采样数据,用冒泡法排序进行中值滤波,并把得到的中值数据返回中值滤波函数如下unsignedcharmedian_filter(){unsignedchardata*point,i,j,n,d;fori=0;i20;i++//外层循环20次{point=0x50;//point指向0x50处n=20﹣i;//n为内层循环次数forj=0;jn;j++//内层循环{if*point*point+1//从大到小排{d=*point;*point=*point+1;*point+1=d;}point++;//指针指向下一个数}}point=0x50+10;//指向位于中间的数return*point;//返回得到的中值}
4.7C51的输入/输出C51的输入和输出函数的形式虽然与ANSIC的一样,但实际意义和使用方法都大不一样,因此,有必要专门介绍一下C51的输入/输出函数在C51的I/O函数库中定义的I/O函数,都是以_getkey和putchar函数为基础这些I/O函数包括字符输入/输出函数getchar和putchar,字符串输入/输出函数gets和puts,格式输入/输出函数printf和scanf等C51的输入/输出函数,都是通过单片机的串行接口实现的在使用这些I/O函数之前,必须先对单片机的串行口、定时器/计数器T1进行初始化假设单片机的晶振为
11.0592MHz,波特率为9600bps,则初始化程序段为SCON=0x52;//设置串口方式1收、发TMOD=0x20;//设置T1以模式2工作TL1=0xfd;//设置T1低8位初值TH1=0xfd;//设置T1自动重装初值TR1=1;//开T1基本输入/输出函数
1、基本输入函数getkeygetkey函数是基本的字符输入函数,原型为char_getkeyvoid函数功能从单片机串行口读入一个字符,如果没有字符输入则等待,返回值为读入的字符,不显示可重入函数字符输入函数getchar功能与getkey基本相同,唯一的区别还要从串行口返回字符2.基本输出函数putcharputchar函数是基本的字符输出函数,其原型为charputcharchar函数功能是从单片机的串行口输出一个字符,返回值为输出的字符putchar为可重入函数格式输出函数printf函数功能通过单片机的串行口输出若干任意类型的数据格式如下printf(格式控制,输出参数表)格式控制是用双引号括起来的字符串,也称为转换控制字符串,它包括三种信息格式说明符普通字符转义字符1)格式说明符由百分号“%”和格式字符组成,其作用是指明输出数据的格式,如%d、%c、%s等,详细情况见表4-32)普通字符这些字符按原样输出,主要用来输出一些提示信息3)转义字符由“\”和字母或字符组成,它的作用是输出特定的控制符,如转义字符\n的含义是输出换行,详细情况见表4-4表4-3printf函数的格式字符 表4-4常用的转义字符格式字符数据类型输出格式 转义字符含义ASCII码dint有符号十进制数 \0空字符0x00uint无符号十进制数 \n换行符0x0aoint无符号八进制数 \r回车符0x0dxXint无符号十六进制数 \t水平制表0x09ffloat十进制浮点数 \b退格符0x08eEfloat科学计数法的十进制浮点数 \f换页符0x0cgGfloat自动选择e或f格式 \’单引号0x27cchar单个字符 \”双引号0x22s指针带结束符的字符串 \\反斜杠0x5c用printf函数输出例子(假设y已定义过,也赋值过)printf“x=%d”,36;//从串行口输出x=36printf“y=%d”,y;//从串行口输出y=y的值printf“c1=%c,c2=%c”,‘A’,‘B’;//从串行口输出c1=A,c2=Bprintf“%s\n”,“OK,Senddatabegin!”;//从串行口输出OKSenddatabegin!和\n格式输入函数scanfscanf函数的功能通过单片机串行口实现各种数据输入函数格式如下scanf(格式控制,地址列表)格式控制格式控制与printf函数的类似,也是用双引号括起来的一些字符,包括三种信息格式说明符、普通字符和空白字符1)格式说明符由百分号“%”和格式字符组成,其作用是指明输入数据的格式,见表4-52)普通字符在输入时,要求这些字符按原样输入3)空白字符包括空格、制表符和换行符等,这些字符在输入时被忽略地址列表是由若干个地址组成,它可以是指针变量、变量地址(取地址运算符“”加变量)、数组地址(数组名)或字符串地址(字符串名)等用scanf函数输入例子(假设x、y、z、c
1、c2是定义过的变量,str1是定义过的指针)scanf(“%d”,x);scanf(“%d%d”,y,z);scanf(“%c%c”,c1,c2);scanf(“%s”,str1);在实际的串行通信中,传输的数据多数是字符型和字符串,以字符串居多,往往把数字型数据转换成字符串传输例4-3有一单片机时钟系统,为了演示输出函数putchar和输入函数getkey的应用,编写程序,用串行口方式1自发自收,每一秒钟从串行口发送一次时间数据的时、分、秒,从串行口接收到数据后,送给6位数码管显示设晶振频率为
11.0592MHz,波特率为9600bps不用编写时钟计时函数和数码管显示函数#includereg
52.h//包含头文件#includestdio.h//包含I/O函数库unsignedchardatat1
[3];//存放原始的时分秒unsignedchardatadis_buf
[6];//数码管显示void__invoid{unsignedchardatat2
[3];//放接收的时间unsignedchardatasec0=61;//秒备份unsignedchardatai;SCON=0x52;//串行口初始化TMOD=0x20;//设置定时器工作模式TH1=0xfd;//设置T1重装的初值TR1=1;//开T1运行while1{ifsec0!=t1
[2]//判断秒是否已经改变{putchart1[i];t2[i++]=_getkey;ifi2{dis_buf
[0]=t2
[0]/10;dis_buf
[1]=t2
[0]%10;dis_buf
[2]=t2
[1]/10;dis_buf
[3]=t2
[1]%10;dis_buf
[4]=t2
[2]/10;dis_buf
[5]=t2
[2]%10;i=0;sec0=t1
[2];//更新秒备份}}display;//调用数码管扫描显示函数}}
4.8C51函数的定义C51函数的定义在C51中,函数的定义与ANSIC中是相同的唯一不同的就是在函数的后面需要带上若干个C51的专用关键字C51函数定义的一般格式如下返回类型函数名(形参表)[函数模式][reentrant][interruptm][usingn]{局部变量定义执行语句}各属性含义如下函数模式也就是编译模式、存储模式,可以为__all、compact和large缺省时则使用文件的编译模式reentrant表示重入函数所谓可重入函数,就是允许被递归调用的函数是C51定义的关键字在编译时会为重入函数生成一个堆栈,通过这个堆栈来完成参数的传递和存放局部变量重入函数不能使用bit型参数;函数返回值也不能是bit型interruptm中断关键字和中断号interrupt是C51定义的C51支持32个中断源中断入口地址与中断号m的关系中断入口地址=3+8×m表4-7单片机中断源与中断号的关系中断源外中断0T0中断外中断1T1中断串行中断T2中断中断号012345中断入口地址0x00030x000b0x00130x001b0x00230x002busingn选择工作寄存器组和组号,n可以为0~3,对应第0组到第3组关键字using是C51定义的如果函数有返回值,不能使用该属性,因为返回值是存于寄存器中,函数返回时要恢复原来的寄存器组,导致返回值错误C51中断函数的定义C51函数的定义实际上已经包含了中断服务函数,但为了明确起见,下面专门给出中断处理函数的具体定义形式void函数名(void)[函数模式]interruptm[usingn]{局部变量定义执行语句}中断服务函数需要注意以下几点1)中断服务函数不传递参数;2)中断服务函数没有返回值;3)中断服务函数必须有interruptm属性;4)进入中断服务函数,ACC、B、PSW会进栈,根据需要,DPL、DPH也可能进栈,如果没有usingn属性,R0~R7也可能进栈,否则不进栈;5)在中断服务函数中调用其它函数,被调函数最好设置为可重入的,因为中断是随机的,有可能中断服务函数所调用的函数出现嵌套调用;6)不能够直接调用中断服务函数例4-4编写程序,使用定时器/计数器0定时并产生中断,实现从P
1.7产生方波的功能程序如下#includereg
52.h#defineTIMER0L0x18//设振荡频率为12MHz#defineTIMER0H0xfc//定时1ms(1000微秒)voidtimer0_intvoidinterrupt1{TL0=TIMER0L;TH0=TIMER0H;P1_7=~P1_7;//产生的方波频率为500Hz}void__in(void){TMOD=0x01;//设置T1模式1定时TL0=TIMER0L;//设置T0低8位初值TH0=TIMER0H;//设置T0高8位初值IE=0x82;//开T0中断和总中断TR0=1;//开T0运行while
(1);//等待中断,产生方波}
4.9C51与汇编语言混合编程混合编程有两种方式一种是在C语言函数中嵌入汇编语言程序,程序中没有__的汇编语言函数,只有个别C语言函数中嵌入有汇编程序;另一种是C语言文件与汇编语言文件混合编程,程序中有__的汇编程序函数和汇编语言文件无论是哪种混合编程方式,采用C51后,程序的大部分是C语言,只有少部分是汇编语言在C51程序中嵌入汇编程序其方法是用编译控制指令“#prag__src”、“#prag__a__”和“#prag__enda__”实现“#prag__src”是控制编译器将C源文件编译成汇编文件,“#prag__src”要放在文件的开始;“#prag__a__”和“#prag__enda__”指示汇编语言程序的开始和结束,分别放在汇编程序段的前面和后面例4-5编写一从单片机P1口做循环右移输出的流水灯子程序#prag__src//指示将C文件编译成汇编文件……voidround_lampvoid{staticunsignedcharlamp=0x55;P1=lamp;#prag__a__//指示汇编语言程序开始MOVAlamp//对变量lamp做循环右移RRAMOVlampA#prag__enda__//指示汇编语言程序结束}C51程序与汇编程序混合编程在这种情况下,C语言与汇编语言程序都是__的文件,它们的函数要相互调用,这就涉及到了汇编语言程序的参数传递和函数命名两个问题下面先讨论汇编语言函数的命名和参数传递问题,然后讨论混合编程
一、C51函数的命名规则从表4-8中可以看出,C51函数的命名规则主要有函数名字符串//不传递参数的函数_函数名字符串//通过寄存器传递参数_函数名字符串//通过堆栈传递参数的可重入函数C51函数名还有其它的格式,如通过存储器传递参数的函数等,在混合编程中基本不用,所以不再介绍表4-8C51中函数名的转换规则C51函数声明汇编函数名说明typefunc1voidFUNC1调用时不传递参数,但有返回值,函数名不变typefunc2args_FUNC2通过寄存器传递参数,函数名加前缀“_”typefunc3argsreentrant_FUNC3重入函数,通过堆栈传递参数,函数名加前缀“_”
二、C51函数段与数据段的格式C51编译后对每个函数都分配一个__的CODE段,并且汇编函数名字还要带上模块名,所以C51汇编语言函数段的格式为PR函数名字符串模块名PR_函数名字符串模块名PR_函数名字符串模块名如果函数中定义有局部变量,编译时也给局部变量分配数据段,数据段的格式为数据段前缀函数名数据类型表4-9C51段类型前缀与存储段前缀存储区类型说明PRcode可执行程序段COcode程序存储器中的常数数据段BIbit内部RAM的位类型数据段BAbdata内部RAM的可位寻址的数据段DTdata内部RAM的数据段IDidata内部RAM的间接寻址的数据段PDpdata外部RAM的分页数据段XDxdata外部RAM的一般数据段
三、C51函数的参数传递规则分为调用时的参数传递和返回时参数的传递
1、调用时参数的传递分三种情况少于等于3个参数时通过寄存器传递(寄存器不够用时通过存储区传递);多于3个时有一部分通过存储区传递;对于重入函数参数通过堆栈传递通过寄存器传递速度最快表4-10给出了第一种情况通过寄存器传递参数的规则表4-10C51利用寄存器传递参数规则参数号charintlongfloat一般指针1R7R6R7低字节R4~R7R1R2R3R3为存储区,R2为高地址R1为低地址2R5R4R5低字节R4~R7或存储区R1R2R3或存储区3R3R2R3低字节存储区R1R2R3或存储区
2、函数返回值的传递当函数有返回值时,通过寄存器传递表4-11C51函数返回值传递规则返回类型使用的寄存器说明bitC进位标志由进位标志位返回char或1字节指针R7由R7返回int或2字节指针R6,R7高字节在R6,低字节在R7longR4~R7高字节在R4,低字节在R7floatR4~R732位IEEE格式一般指针R1~R3R3为存储区,R1为低地址
四、汇编语言文件及函数编写方法汇编语言文件的构成主要有定义模块名、函数声明、公共函数声明、引用函数声明、引用变量声明、函数定义等部分
1、定义模块对汇编语言文件定义模块名,一般一个文件为一个模块,也可以多个文件为同一个模块名模块定义格式如下NAME模块名定义模块要放在文件的开始例如NAMEEXAMP
2、函数声明即对本模块定义的函数作声明,其格式为PR函数名模块名SEGMENTCODE格式中的函数名规则如上面一所述例如PRDISPLAYEXAMPSEGMENTCODEPR_RIGHTEXAMPSEGMENTCODEPR_MUSICEXAMPSEGMENTCODE说明函数的声明放在文件的前面,一般在模块定义之后,并且紧接着模块定义
3、公共函数声明如果函数在其它文件(模块)中调用,必须作公共函数声明声明格式为PUBLIC函数名例如PUBLICDISPLAYPUBLIC_RIGHT_SHIFTPUBLIC_MUSIC声明公共函数应放在函数声明之后
4、引用函数声明如果在汇编程序中引用了其它文件中的函数,必须作引用声明声明格式为EXTRNCODE(函数名)例如EXTRNCODE(KEY)EXTRNCODE(_COUNT)函数引用声明中的“KEY”函数不传递参数;“_COUNT”函数通过寄存器传递参数
5、引用变量声明如果在汇编程序中引用了其它文件中的变量,必须作引用声明声明格式为EXTRN存储区(变量名)其存储区域类型如表4-2所示的7种类型例如EXTRNDATA(TIMER_SEC)EXTRNIDATA(DIS_BUF)ENTRNXDATA(SEND_BUF)
6、函数编写格式汇编语言函数的格式如下RSEGPR函数名模块名函数名…………RET或RETI
五、汇编语言文件编写举例例4-6编写一个完整的汇编语言程序文件,文件包含三个函数,分别是定时器/计数器T1产生方波__的中断函数、循环右移多位函数和循环左移多位函数;参数传递T1的计数初值通过全局变量T1_H、T1_L传递左移、右移函数都有两个入口参数(被移位的数、移位的位数)和返回值(被移位后的数),要求通过寄存器传递所有参数都是无符号字符型数据程序如下NAMEEXAMP;定义模块名PRT1_INTEXAMPSEGMENTCODEPR_RIGHTEXAMPSEGMENTCODEPR_LEFTEXAMPSEGMENTCODEPUBLIC_RIGHT;公共函数声明PUBLIC_LEFTEXTRNDATAT1_H;引用外部变量声明EXTRNDATAT1_LCSEGAT001BH;设置T1中断入口LJMPT1_INTRSEGPRT1_INTEXAMP;定义T1中断处理函数T1_INT:MOVTL1T1_LMOVTH1T1_HCPLP
1.7RETIRSEGPR_RIGHTEXAMP;右移函数_RIGHT:;R7中为第1个参数,MOVAR7;为将被移位的数RIGHT_LP:;R5为第2个参数移位的位数RRA;右移1位DJNZR5RIGHT_LPMOVR7A;保存返回值于R7中RET;为被移位后的数RSEGPR_LEFTEXAMP;左移函数_LEFT:;R7为第1个参数,MOVAR7;为被移位的数LEFT_LP:;R5为第2个参数移位的位数RLA;左移1位DJNZR5LEFT_LPMOVR7A;保存返回值于R7中RET;为被移位后的数END阅读本例注意以下几个方面1)函数的声明方法和定义方法3)引用其它文件中的变量的声明方法和使用方法4)声明公共函数的方法2)对(中断处理)函数的定位方法5)函数中入口参数的传递方法,返回参数的传递方法6)外部函数的声明方法和使用方法
六、在C51中调用汇编函数的方法在C语言文件中调用汇编语言中的函数,必须先声明再调用,其声明方法与声明C语言函数完全一样,即extern返回值类型函数名参数表;例如externunsignedcharright_shiftcharchar;externunsignedcharleft_shiftcharchar;对于汇编语言函数的调用方法,与调用C语言中的函数完全一样地址低高::3412::地址低高::78563412::地址低高::000048C1::通用指针的特点定义简单访问所有空间访问速度慢。