版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第五章单片机的C语言程序设计及仿真调试disable任课教师:刘忠国山东大学课程中心网站:
http:///G2S/stcmcu.cc宏晶官方网站:http://STC单片机编译(汇编)/编程(烧录)/仿真工具说明书;stc15系列单片机器件手册等keilμvision软件下载及指导手册(Help→μvisionHelp)/KeilSoftware–Cx51编译器用户手册:Cx51编译器--对传统和扩展的8051微处理器的优化的C编译器和库参考2第五章单片机的C语言程序设计及仿真调试本章学习目标掌握单片机C语言程序中的常用功能掌握KeilC的程序设计掌握IAP15W4K58S4单片机C语言程序调试过程3第五章单片机的C语言程序设计及仿真调试§5.1C51程序的基本语法5.1.1关键字5.1.2C51程序的一般结构5.1.3数据类型5.1.4运算符和表达式§5.2KeilC51程序的语句5.2.1表达式语句5.2.2条件语句5.2.3开关语句5.2.4循环语句5.2.5goto、break、continue和return语句§5.3函数5.3.1函数的定义与调用5.3.2KeilC51函数§5.4KeilC51库函数§5.5预处理命令§5.6单片机C语言程序框架4汇编语言和C语言的选择问题设计规模较小的嵌入式应用系统时,可以使用汇编语言。因为代码一般不长,且较简单。当程序比较复杂,且没有很好的注释时,使用汇编语言编写的程序,可读性和可维护性会很差,代码的可重用性也比较低。使用C语言编程,编写简单、直观易读、便于维护、通用性好。在控制任务比较复杂或者具有大量运算的系统中,C语言优势明显。由于模块化,用C语言编写的程序具有很好的可移植性。5§5.1C51程序的基本语法1.标准C语言(ANSIC)的关键字(1)数据类型关键字1)基本数据类型void、char、int、float、double2)类型修饰关键字short、long、signed、unsigned3)复杂类型关键字struct、union、enum、typedef、sizeof4)存储级别关键字auto、static、register、extern、const、volatile§5.1.1关键字6§5.1.1关键字(2)流程控制关键字1)跳转结构return、continue、break、goto2)分支结构if、else、switch、
case、default3)循环结构for、do、
while2.KeilC51编译器支持的关键字bit、sbit、sfr、sfr16、data、bdata、idata、pdata、
xdata、
code、
interrupt、
reentrant、using7预处理命令
//以#开头的命令,用于包含头文件、定义常数等全局变量声明
//全局变量虽然方便传递参数,但不宜多函数1的声明......函数n的声明voidmain(void) //主函数{
局部变量声明
//局部变量只能在所定义的函数内部引用
可执行语句
函数调用
无限循环}§5.1.2C51程序的一般结构8//一般函数的定义函数1(形式参数声明){
局部变量声明
可执行语句}......函数n(形式参数声明){
局部变量声明
可执行语句}§5.1.2C51程序的一般结构9//中断函数的实现voidISRname(void)interruptn//n为中断号{
局部变量声明
可执行语句}注意如下几点:(1)所有函数以花括号“{”开始,以花括号“}”结束,包含在“{}”内的部分称为函数体。花括号必须成对出现,如果一个函数内有多对花括号,则最外层的花括号为函数体的范围。为了增加程序的可读性,应采用缩进方式书写。§5.1.2C51程序的一般结构10注意如下几点:(2)建议一行写一条语句,每条语句最后必须以一个分号“;”结尾。(3)每个变量必须先定义后引用。在函数内部定义的变量为局部变量,又称为内部变量,只有定义它的那个函数才能使用。在函数外部定义的变量为全局变量,又称为外部变量,在定义它的那个程序文件中的函数都可以使用。(4)程序语句的注释放在双斜杠“//”之后,或者放在“/*......*/”之内。§5.1.2C51程序的一般结构111.常量和变量常量包括整形常量、浮点型常量、字符型常量(如‘a’:单引号字符
)
及字符串常量(双引号单个或多个字符,如"a","Happy")等。变量是一种在程序执行过程中其值不断变化的量。使用一个变量之前,必须先进行定义。§5.1.3数据类型
1.常量和变量2.数据类型(1)基本数据类型基本数据类型是不可以再分解为其他类型的数据类型。如char(字符型)、int(整型)、long(长整型)、float(浮点型)等。122.数据类型(2)构造数据类型构造数据类型是根据已定义的一个或多个数据类型用构造的方法来定义的。§5.1.3
数据类型
2.数据类型在C语言中,构造类型有以下几种:数组类型结构类型联合类型13(3)指针类型指针是一种特殊的,具有重要作用的数据类型。用来表示某个量在内存中的地址。§5.1.3
数据类型2.数据类型(4)空类型函数调用后并不需要向调用者返回函数值,这种函数可以定义为“空类型”。其类型说明符为void。14C51编译器除了支持上述数据类型外,还支持以下几种扩充数据类型:(1)bit:位类型。bit型变量的位地址由编译器分配。(2)sfr:特殊功能寄存器,用来控制中断、定时器、计数器、串口、I/O及其他部件。(3)sfr16:16位特殊功能寄存器。(4)sbit:位寻址。§5.1.3
数据类型2.数据类型bit与sbit的用法区别:定义的bit型变量的位地址在00H~7FH之间,具体地址值不定,由编译器随机分配。sbit位寻址:该数据类型用一个指定的变量作为基地址然后用其位的位置来得到一个实际的位地址。15(1)bit:位类型。可以定义一个位变量,但不能定义位指针,也不能定义位数组。
C51程序中,函数参数和返回值也可以是位变量。例:bit
finish_flag=0;bittestfunc(bitvar1,bitvar2){
...
return(0);};§5.1.3
数据类型2.数据类型所有bit型变量的都被定位在8051片内RAM的可位寻址区20H~2FH,共16个字节,所以最多只能声明128个bit型位变量。16(2)sfr:字节寻址。语法如下:
sfrsfr_name=int_constant;如
sfrP0=0x80;
0x80为P0口的地址,“=”后为常数,且这个常数必须在特殊功能寄存器的地址范围内,即0x80到0xFF之间。§5.1.3
数据类型2.数据类型(3)sfr16:字寻址如sfr16DPTR=0x82;
指定DPTR的地址DPL=0x82,DPH=0x83。(4)sbit:位寻址。用于定义可位寻址变量。可定义8051片内RAM的可寻址位,或特殊功能寄存器的可寻址位。17§5.1.3
数据类型2.数据类型(4)
sbit:位寻址。定义可位寻址变量。定义片内RAM的可寻址位,
或特殊功能寄存器的可寻址位。C51编译器提供了存储类型bdata,bdata存储类型变量位于片内RAM的可位寻址区,可字节寻址,或位寻址,因此bdata型变量可用sbit指定其中任意位为可位寻址变量。需注意:使用bdata和sbit定义的变量必须是全局变量,并且用sbit定义为位寻址变量时要求基址对象的存储器类型必须为bdata
(特殊功能寄存器除外)。sbit声明方法:
sbitbitname=bdata型变量或sfr_name^bit_number;其中,sfr_name
是已定义SFR的名字,bit_number是位号,其数值取决于基址对象的数据类型,对char和SFR而言是(0~7)
,对int型而言是(0~15),对long型而言是(0~31)。18§5.1.3
数据类型2.数据类型sbit声明方法:
sbitbitname=bdata型变量或sfr_name^bit_number;其中,sfr_name
是已定义SFR的名字,bit_number是位号,其数值取决于基址对象的数据类型,对char和SFR而言是(0~7),对int型而言是(0~15),对long型而言是(0~31)。如:unsignedcharbdataflag;//flag为bdata型无符号字符变量int
bdata
ibase
;//定义ibase为bdata整型变量使用sbit可位寻址变量如下:
sbitflag0=flag^0
;//定义flag0为flag的第0位
sbitmybit15=ibase^15;//mybit15为ibase的第15位
sbitCY=PSW^7;//定义CY为PSW的第7位sbitP00=P0^0;//定义P0.0口线的名称是P0019sbit可以有下面声明方法:方法1:sbitbitname=sfr_name^bit_number;其中,sfr_name必须是已定义的SFR的名字,bit_number是位号(0~7)。如:sbitCY=PSW^7;//定义CY为PSW的第7位。方法2:sbitbitname=sfr_address^bit_number;
其中,sfr_address是SFR所在的地址(0x80~0xff),bit_number是位号(0~7)。如:sbitOV=0xD0^2;//定义PSW中的OV位方法3:sbitbitname=bit_address;其中,bit_address是位地址。如:sbitEA=0xAF;//第0xAF位为EA§5.1.3
数据类型2.数据类型20KeilC51编译器支持的数据类型如表5-1所示。数据类型位数(bit)字节数(byte)取值范围bit11/80或1signedchar81-128~+127unsignedchar810~255enum8/161or2-128~+127or-32768~+32767signedshort162-32768~+32767unsignedshort1620~65535signedint162-32768~+32767unsignedint1620~65535signedlong324-2147483648~+2147483647unsignedlong3240~4294967295float324+1.175494E38~+3.402823E+38sbit11/80或1sfr810~255sfr161620~65535表5-1KeilC51编译器支持的数据类型§5.1.3
数据类型2.数据类型213.存储器类型存储器类型取值范围data默认存储器类型,低128字节内部RAM,DATA区(00H~7FH地址空间),访问速度最快。bdata可位寻址内部RAM,BDATA区(20H~2FH地址空间),允许位和字节混合访问。idata256字节内部RAM,IDATA区(00H~FFH地址空间),允许访问全部内部单元。pdata分页寻址外部RAM,PDATA区(0000H~FFFFH地址空间),用MOVX@Ri指令访问。xdata外部RAM,XDATA区(0000H~FFFFH地址空间),用MOVX@DPTR指令访问。code程序存储区,CODE区(0000H~FFFFH地址空间),用MOVC@A+DPTR指令访问。表5-2KeilC51编译器支持的存储器类型对变量定义格式:数据类型
[存储器类型]变量名表;
§5.1.3
数据类型3.存储器类型
例如:unsignedchardatabuffer;22存储类型的指定:变量或参数的存储类型可由存储模式指定缺省类型,也可由关键字code、data、idata、xdata、pdata直接声明指定。例如:unsigned
char
databuffer;
databuffer;//没有指定数据类型,默认为int型unsigned
char
code
numtab[16]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xFB,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E};
//定义LED显示字模(参见137页)unsigned
char
xdata
arr[10][4][4];§5.1.3
数据类型3.存储器类型对变量定义格式:数据类型
[存储器类型]变量名表;
23§5.1.3
数据类型3.存储器类型数据存储类型的指定:变量或函数参数存储类型可由存储模式
(Small,large,Compact)(OptionsforTarget‘Target1’...选项)指定缺省存储类型;在small模式下,函数参数和局部变量位于由data定义的单片机片内数据RAM(00~7FH)中;在compact模式下,函数参数和局部变量位于pdata定义的扩展数据RAM中(访问用MOVX@Ri)。在large模式下,函数参数和局部变量位于xdata定义的扩展数据RAM中(访问用MOVX@DPTR)数据存储模式(Small,large,Compact)24在Xtal(MHz)右侧框输入6,其余按默认设置。4-3汇编语言程序调试Project窗口,选中Target1,并单击右键,出现浮动菜单。浮动菜单中选中OptionsforTarget‘Target1’...选项。出现OptionsforTarget‘Target1’对话框界面。在该界面中,点击Target标签。在该标签界面中,按下面设置参数:在此设置使用晶振的频率
针对目标硬件设置工具选项MemoryModel右侧下拉选择:Small,Compact,large的存储模式。254.关于指针数据类型指针变量的值是一个地址,这个地址不仅可以是变量的地址,也可以是其他数据结构的地址。KeilC51编译器支持两种指针类型:一般指针(GenericPointer)和存储器指针(MemorySpecificPointer,
指定存储区指针)。一般指针的声明和使用均与标准C相同,同时还可以说明指针的存储类型(见下页)。char*s
;/*stringptr*/int*numptr;/*intptr*/§5.1.3
数据类型4.关于指针数据类型26一般指针的声明和使用均与标准C相同,同时还可以说明指针的存储类型。char*s
;/*stringptr*/int*numptr;/*intptr*/可用存储类型标识符指定一个通用指针的存储区如:
char*xdatastrptr;/*genericptrstoredinxdata*/int*datanumptr;/*genericptrstoredindata*/long*idatavarptr;/*genericptrstoredinidata*/这些例子指向可能保存在任何存储区中的变量但是指针分别保存在xdata、data和idata中。§5.1.3
数据类型4.关于指针数据类型27一般指针用3个字节存放:存储器类型,存储器地址高8位偏移量和低8位偏移量。
char
data*str;
//str指向data区中char型数据
int
xdata*pow;
//pow指向外部RAM的int型整数这种指针存放时,只需1(或2)个字节就够了,因只需存放偏移量。
存储器指针(指定存储区指针),说明时即指定了存储类型,例如:
4.关于指针数据类型存储类型Idata/data/bdatapdataxdataCode编码值0x000xFE0x010xFF存放数据的存储器类型存储器类型是在编译时需要的,既已指出,所以指针只需存偏移量即可,参考:KeilHelp:Cx51CompilerUser'sGuide→LanguageExtensions→Pointers28§5.1.3
数据类型4.关于指针数据类型象一般指针一样可指定一个存储器指针的保存存储区,即在指针声明前加存储类型标识符,例如:unsignedcharxdata
*pt;
//pt本身依存储模式存放unsignedcharxdata*datapt;//pt被保存在内部RAM中unsignedcharxdata
*xdatapt;
//pt被保存在外部RAM中上面的语句都声明pt为指向保存在外部RAM中unsignedchar数据的指针,但pt本身的保存位置却不同。存储器指针29运算符是告诉编译程序执行特定算术或逻辑操作的符号,表达式则是由运算符及运算对象所组成的具有特定含义的一个式子。在任意一个表达式的后面加一个分号“;”就构成了一个表达式语句。C51程序就是由多个表达式语句构成的语句集合。运算符可以分为赋值运算符、算术运算符、关系运算符、逻辑运算符、位运算符、复合赋值运算符、逗号运算符、条件运算符、指针和地址运算符、强制类型转换运算符等。§5.1.4
运算符和表达式301.赋值运算符符号“=”为赋值运算符,它的作用是将一个数据的值或表达式的值赋给一个变量。利用赋值运算符将一个变量与一个表达式连接起来的式子成为赋值表达式,在赋值表达式的后面加一个分号“;”便构成了赋值语句。§5.1.4
运算符和表达式1.赋值运算符312.算术运算符算术运算符用于各类数值运算。包括加(+)、减或取负值(-)、乘(*)、除(/)、取余(或称模运算,%)、自增(++)、自减(--)共七种。在除法运算中,如果是两个整数相除,其结果为整数,舍去小数部分。用算术运算符将运算对象连接起来的式子就是算术表达式。§5.1.4
运算符和表达式
2.算术运算符32计算一个算术表达式的值时,要按照运算符的优先级高低顺序进行。算术运算符中,取负值(-)的优先级最高,其次是乘法(*)、除法(/)和取余(%)运算符,加法(+)和减法(-)运算符的优先级最低。需要时,可在算术表达式中必要的地方采用圆括号来改变优先级,括号的优先级最高。在使用自增(++)运算符和自减(--)运算符时,要注意运算符的位置。例如,++i和i++的意义完全不同,前者为在使用i之前先使i加1,而后者则是在使用i之后再使i加1。在实际应用中,尽可能使用后者的方式,即i++的形式。§5.1.4
运算符和表达式
2.算术运算符333.关系运算符关系运算符用于比较运算。包括大于(>)、小于(<)、大于等于(>=)、小于等于(<=)、等于(==)和不等于(!=)六种。前四种关系运算符具有相同的优先级,后两种关系运算符也具有相同的优先级;但前四种的优先级高于后两种。用关系运算符将两个表达式连接起来即构成关系表达式。§5.1.4
运算符和表达式3.关系运算符344.逻辑运算符逻辑运算符包括与(&&)、或(||)、非(!)3种,用于对包含关系运算符的表达式(称为条件)进行合并或取非运算。关系运算符和逻辑运算符通常用来判别某个或某些条件是否满足,条件满足时结果为1,条件不满足时结果为0。§5.1.4
运算符和表达式4.逻辑运算符35与运算符(&&)表示2个条件同时满足时(即2个条件都为真时),返回结果才为真。或运算符(||)用于检查2个条件中是否有1个为真的运算符,只要有1个条件为真,运算结果就为真。逻辑非运算符(!)表示对表达式的值取反。上述几种运算符的优先级依次为(由高到低):逻辑非→算术运算符→关系运算符→逻辑与→逻辑或。§5.1.4运算符和表达式4.逻辑运算符365.位运算符C语言提供了6种位运算符:按位与(&)、按位或(|)、按位异或(^)、取反(~)、左移(<<)和右移(>>)。位运算符的优先级从高到低依次为:按位取反(~)→左移(<<)和右移()→按位与(&)→按位异或(^)→按位或(|)。§5.1.4
运算符和表达式5.位运算符其功能是参与运算的两数各对应的二进位进行如下运算:37(1)按位与运算按位与运算符“&”是双目运算符。其功能是参与运算的两数各对应的二进位相与。只有对应的2个二进位均为1时,结果位才为1,否则为0。(2)按位或运算按位或运算符“|”是双目运算符。其功能是参与运算的两数各对应的二进位相或。只要对应的2个二进位有1个为1时,结果位就为1。§5.1.4
运算符和表达式5.位运算符38(3)按位异或运算按位异或运算符“^”是双目运算符。其功能是参与运算的两数各对应的二进位相异或。当2个对应的二进位相异时,结果为1。(4)求反运算求反运算符“~”为单目运算符,具有右结合性。其功能是对参与运算的数的各二进位按位求反。§5.1.4运算符和表达式5.位运算符39(5)左移运算左移运算符“<<”是双目运算符。其功能把“<<”左边的运算数的各二进位全部左移若干位,由“<<”右边的数指定移动的位数,高位丢弃,低位补0。(6)右移运算右移运算符“>>”是双目运算符。其功能是把“>>”左边的运算数的各二进位全部右移若干位,“>>”右边的数指定移动的位数。对于有符号数,右移时,符号位将随同移动。当为正数时,最高位补0;为负数时,符号位为1,最高位补1。§5.1.4
运算符和表达式5.位运算符406.复合赋值运算符在赋值运算符“=”之前加上其他二目运算符可构成复合赋值运算符。构成复合赋值表达式的一般形式为:
变量双目运算符=
表达式它等效于:变量=变量运算符
表达式复合赋值运算符有:+=,-=,*=,/=,%=,<<=,>>=,&=,^=,~=,|=。§5.1.4
运算符和表达式6.复合赋值运算符417.逗号运算符逗号运算符用于把若干表达式组合成一个表达式(称为逗号表达式)。程序运行时,对于逗号表达式的处理,是从左至右依次计算出各个表达式的值,而整个逗号表达式的值是最右边表达式的值。例如:inta1,a2,b=2,c=7,d=5;//第1行a1=(++b,c--,d+3);//第2行,a1的值为8a2=++b,c--,d+3;//第3行,
a2的值为4§5.1.4
运算符和表达式
7.逗号运算符428.条件运算符条件运算符(?:)是一个三目运算符,用于条件求值。它要求有三个运算对象,使用它可以将三个表达式连接构成一个条件表达式。条件表达式的一般形式为:逻辑表达式?表达式1:表达式2其功能是,首先计算逻辑表达式的值,当逻辑表达式的值为真(非0值)时,将表达式1的值作为整个条件表达式的值;当逻辑表达式的值为假(0值)时,将表达式2的值作为整个条件表达式的值。§5.1.4
运算符和表达式8.条件运算符439.指针和地址运算符变量的指针就是该变量的地址,而存放变量地址的变量称为指针变量。为表示指针变量和它所指向的变量地址之间的关系,C语言有运算符:取内容(*)和取地址(&),其运算的一般形式分别为:变量=*指针变量指针变量=&目标变量取内容(*)运算的含义是将指针变量所指向的目标变量的值赋给等号(=)左边的变量;取地址(&)运算的含义是将目标变量的地址赋给等号(=)左边的指针变量。§5.1.4
运算符和表达式
9.指针和地址运算符44unsignedchar*txp;
unsigned
char
txbuffer[50];txp=txbuffer;
例使用指针可以进行外部扩展I/O口的访问。在C51中有两种方法访问外部I/O端口。方法1:使用自定义指针。由于片外I/O端口与片外存储器统一编址,所以可以定义xdata类型的指针访问外部I/O端口。例如,某单片机应用系统中,使用8255(见第1版8.3节)扩展I/O端口,采用线选法对8255进行地址译码,单片机的P2.7(A15)接8255的片选引脚,因8255的命令口地址为7FF3H,PA口地址为7FF0H,PB口地址为7FF1H,PC口地址为7FFF2H。访问8255的C程序如下:§5.1.4
运算符和表达式9.指针和地址运算符458255因目前停产较少用,本版教材不再介绍(用法见第1版8.3节)方法1:使用自定义指针访问外部扩展I/O口写端口程序:charxdata
*com8255;
//定义指向外部存储区(片外I/O端口)的指针
com8255=0x7ff3;
//使指针指向8255的控制口(命令口)地址7FF3H*com8255=0x81;//输出命令字81H到命令口寄存器//
81H:PA,PB口都是模式0,直接输出,PC口直接输入以上C程序相当于下面的汇编语言程序:
MOVDPTR,#7FF3H
MOVA,#81H
MOVX@DPTR,A具体命令介绍见见第1版8.3节:并行接口扩展方法,309页图8-5246方法1:使用自定义指针访问外部扩展I/O口读端口程序:
charxdata*com8255;//定义指针
chari;com8255=0x7FF0;
//使指针指向8255的PA口地址7FF0Hi=*com8255;//读PA端口内容到变量i47§5.1.4
运算符和表达式9.指针和地址运算符为了方便访问外部存储器及I/O端口,在C51的absacc.h头文件做了如下定义,利用这些定义可以方便地访问外部I/O端口。#defineCBYTE((unsignedcharvolatile
code*)0)#defineDBYTE((unsignedcharvolatile
data*)0)#definePBYTE((unsignedcharvolatile
pdata*)0)#defineXBYTE((unsignedcharvolatile
xdata*)0)volatile修饰了的变量随程序的执行其值会被改变,“易变”方法2:使用C51预定义指针访问外部扩展I/O口48#defineXBYTE((unsignedcharvolatilexdata*)0)以XBYTE为例介绍上述宏定义使用方法,XBYTE使用格式:
XBYTE[地址]方法2:使用C51预定义指针访问外部扩展I/O口例如:#include<absacc.h>#define
PORTA
XBYTE[0x7ff0]
//其中,PORTA为程序定义的I/O端口名称,[]内的内容//7ff0H为PORTA的地址voidmain(void){chara;
PORTA=0x81;//*输出81H到端口7ff0Ha=PORTA;//读端口7ff0H到变量a}定义XBYTE数组的首地址是0XBYTE[0x7ff0]的中括号内的值0x7ff0,指出了(定义)数组XBYTE首地址的偏移地址4910.强制类型转换运算符强制类型转换运算符的作用是将表达式或变量的类型强制转换成为括号内所指定的类型。强制类型转换运算符的一般使用形式为:变量=(类型)表达式如:pxdata=(charxdata*)0x3000;//pxdata为在xdata中定义的char类型指针变量§5.1.4运算符和表达式10.强制类型转换运算符50表达式语句是最基本的一种语句。在表达式的后面加一个分号“;”就构成了表达式语句。表达式语句也可以仅由一个分号“;”构成,这种语句称为空语句。空语句不执行具体的动作。程序设计时,有时需要用到空语句。例如,使用循环语句延时程序中的循环体内可以使用空语句。§5.2KeilC51程序的语句§5.2.1表达式语句51条件语句又称为分支语句,使用关键字“if”构成。C51提供了三种形式的条件语句。1.if(条件表达式){语句体;}§5.2.2条件语句522.if(条件表达式){
语句体1;}else{
语句体2;}3.if(条件表达式1){
语句体1;}elseif(条件表达式2){
语句体2;}……elseif(条件表达式m){
语句体m;}Else{
语句体n;}§5.2.2条件语句53开关语句也是一种用来实现多条件分支的语句。……case常量表达式n:语句体n;
break;
default:语句体d}§5.2.3开关语句54switch(表达式){case常量表达式1:语句体1;
break;
case常量表达式2:语句体2;
break;1.while语句利用while语句构成循环结构的一般形式如下:while(条件表达式){
语句体;}§5.2.4循环语句552.do-while语句采用do-while语句构成循环结构的一般形式如下:do{
语句体;}while(条件表达式);3.for语句采用for语句构成循环结构的一般形式如下:for([初值设定表达式];[循环条件表达式];[更新表达式]){
语句体;}§5.2.4循环语句56goto语句是一个无条件转向语句,其一般形式为:goto语句标号;break语句也可以用于跳出循环语句,其一般形式为:break;continue是一种中断语句,其功能是中断本次循环,继续下一次循环,一般形式为:continue;return语句用于终止函数的执行,并控制程序返回到调用该函数的位置。一般形式为:return(表达式);return;§5.2.5goto、break、continue和return语句57两种函数:KeilC51定义的标准库函数和用户自定义函数。§5.3.1函数的定义与调用§5.3函数58函数定义的一般形式为:函数返回值类型
函数名(形式参数表){
局部变量定义
函数体语句}函数调用的一般形式为:[变量=]函数名(实际参数表)5.3.2KeilC51函数C51的函数声明对ANSIC作了扩展,具体包括:1、中断函数声明中断函数通过使用interrupt关键字和中断号(0~31)来声明。中断号告诉编译器中断服务程序入口地址。IAP15W4K58S4单片机的中断号及中断服务程序入口地址如表5-3
中断号中断源入口地址中断号中断源入口地址0外部中断INT00003H11外部中断
INT3005BH1T0溢出中断000BH12T2溢出中断0063H2外部中断INT10013H16外部中断
INT40083H3T1溢出中断001BH17串口3中断008BH4串行口UART1中断0023H18串口4中断0093H5ADC中断002BH19T3溢出中断009BH6LVD中断0033H20T4溢出中断3H7PCA中断003BH21比较器中断00ABH8串行口UART2中断0043H22PWM中断00B3H9SPI中断004BH23PWM异常检测中断00BBH10外部中断
INT20053H59中断服务函数的一般形式为:void
函数名(void)interrupt中断号
[usingn]中断函数通过使用interrupt关键字和中断号来声明。中断号告诉编译器中断服务程序的入口地址。也就是说,C51通过中断号来区分各个不同的中断,而与中断函数的名字无关。其中usingn用于选择单片机不同的寄存器组,n为0~3的常整型数,分别选中4个不同寄存器组中的一个。Using是一个可选项,可以不用。不用时,由编译器自动选择一个寄存器组。60/785.3.2KeilC51函数1、中断函数声明5.3.2KeilC51函数1、中断函数声明例如,串行口1的中断函数(4号中断)可以声明如下:
voidUART1_ISR(void)interrupt4[using1]{/*中断服务程序的代码*/}上述代码声明了串行口1中断服务函数。其中,interrupt4指明是串行口1的中断,using1指明采用工作寄存器区1区,using1在中括号中,说明该部分可省略。其他中断函数的定义与此类似。中断函数具体是哪个中断的函数,与中断号有关,而与函数名(如UART1_ISR)无关
,但一般赋予与相应中断相符合的名称。无输入参数无返回参数615.3.2KeilC51函数-——2、指定工作寄存器区当需要指定函数中使用的工作寄存器区时,使用关键字using后跟一个0到3的数,对应着工作寄存器0到3区。例如,在下面的函数中使用了工作寄存器1区(相当于PSW.4=0,PSW.3=1):
unsignedcharGetKey(void)using1{/*用户程序代码*/}
指定工作寄存器区62程序状态标志寄存器PSW(8位)RS1,RS0(PSW.4~PSW.3):工作寄存器组选择控制位,其详细介绍见后续内容。OV(PSW.2):溢出标志位。指示运算过程中是否发生了溢出,在执行指令过程中自动形成。位号D7D6D5D4D3D2D1D0符号CYACF0RS1RS0OVF1PRS1RS0OVP(PSW.0):奇偶标志位累加器ACC中1的个数为偶数,P=0;否则P=1。每个指令周期都由硬件来置“1”或清“0”。在具有奇偶校验的串行数据通信中,可根据P设置奇偶校验位。P63工作寄存器组的选择:PSW寄存器中的RS1和RS0两位组合决定当前使用的工作寄存器组。可通过位操作指令修改RS1和RS0的内容,选择不同的工作寄存器组。RS1(PSW.4)RS0(PSW.3)工作寄存器组工作寄存器地址000R7~R0对应的地址为07H~00H011R7~R0对应的地址为0FH~08H102R7~R0对应的地址为17H~10H113R7~R0对应的地址为1FH~18H表3-1工作寄存器组选择18H~1FH10H~17H08H~0FH00H~07H3区(R7~R0)2区(R7~R0)1区(R7~R0)0区(R7~R0)645.3.2KeilC51函数——2、指定存储模式用户可以使用small,compact及large说明存储模式。例如:
voidfun1(void)small{}提示:small说明的函数内部变量全部使用内部RAM。关键的、经常性的、耗时的地方可以这样声明,以提高运行速度。2、指定存储模式65用compact说明时,函数参数和局部变量位于pdata定义的扩展数据RAM中。用large说明时,函数参数和局部变量位于xdata定义的扩展数据RAM中。5.3.2KeilC51函数——3、函数的重入一个可重入函数,简单来说,就是可以被中断的函数,即可以在这个函数执行的任何时刻中断它,可重入函数可以被递归调用。递归或可重入函数在单片机系统中容易产生问题,因为单片机和PC不同,PC使用(调用)堆栈传递参数,且静态变量以外的内部变量都在堆栈中;而单片机一般使用寄存器传递参数,
内部变量一般在固定的RAM中,函数重入时会破坏(覆盖)上次调用的数据。可在函数前声明函数的不可重入性(不可中断性)。若声明为不可重入(中断)的,则该函数调用过程中将不可被中断。typedefunsignedcharuchar;#pragmadisable/*DisableInterrupts*/uchardfunc(ucharp1,ucharp2){return(p1*p2+p2*p1);}66(#pragma的作用是设定编译器的状态或者指示编译器完成一些特定的动作)5.3.2KeilC51函数——3、函数的重入可用以下两种方法解决函数的重入(及不可中断)问题:第一种方法:
在相应的函数前使用“#pragmadisable”声明,即只允许主程序或中断之一调用该函数(函数调用后不再被中断)。第二种方法:
将该函数说明为可重入的。如下:
voidfunc(param...)reentrant{...};因为单片机内部堆栈空间的限制,C51没有像大系统那样使用调用堆栈:一般在C语言中调用过程时,会把过程的参数和过程中使用的局部变量入栈。KeilC51编译后将生成一个可重入变量堆栈,然后就可以模拟通过调用堆栈传递变量的方法。67这样函数重入时,不用担心变量数据被覆盖的问题。
5.3.2KeilC51函数——3、函数的重入为了提高效率,C51没有提供这种调用堆栈,而是提供一种压缩栈。每个过程被给定同一个空间,用于存放局部变量。过程中的每个变量都存放在这个空间的固定位置。当递归调用这个过程时,会导致变量被覆盖。在某些实时应用中,非重入函数是不可取的。因为,函数调用时可能会被中断程序中断,而在中断程序中可能再次调用这个函数,所以C51允许将函数定义成重入函数。重入函数可被递归调用和多重调用,而不用担心变量被覆盖,因C51可模拟生成一个可重入变量堆栈{随存储模式(small,compact,large)不同,堆栈可在不同的存储器区(idata,pdata,xdata)},每次函数调用时的局部变量都会被单独保存。因为这些堆栈是模拟的,重入函数一般都比较大,运行起来也比较慢。不常使用685.3.2KeilC51函数——3、函数的重入由于一般可重入函数由主程序和中断调用(即递归调用),所以通常中断程序使用与主程序不同的工作寄存器组。另外,对可重入函数,在相应的函数前面加上开关#pragmanoaregs,以禁止编译器使用绝对寄存器寻址,可生成不依赖于寄存器组的代码。所以对不依赖于寄存器组的函数可被其他使用了不同的寄存器组的多个函数调用。externcharfunc();#pragmaNOAREGSnoaregfunc
()
{k=func()+func();}#pragmaAREGSaregfunc()using3
{k=noaregfunc()+func();
}不常使用69本征库函数是指编译时直接将固定的代码插入到当前行,而不是用汇编语言中的ACALL和LCALL指令实现调用,从而大大提高函数的访问效率。非本征库函数则必须由ACALL和LCALL指令实现调用。KeilC51提供了9个本征库函数,如表5-4所示。使用本征库函数时,须在源程序中包含头文件(含有函数原型声明),即用预处理命令#include<INTRINS.H>。§5.4.1本征库函数§5.4KeilC51库函数70函数名及定义功能说明unsignedchar_crol_(unsignedcharval,unsignedcharn)将字符型数据val循环左移n位,相当于RL指令。unsignedint_irol_(unsignedintval,unsignedcharn)将整型数据val循环左移n位,相当于RL指令。unsignedlong_lrol_(unsignedlongval,unsignedcharn)将长整型数据val循环左移n位,相当于RL指令。unsignedchar_cror_(unsignedcharval,unsignedcharn)将字符型数据val循环右移n位,相当于RR指令。unsignedint_iror_(unsignedintval,unsignedcharn)将整型数据val循环右移n位,相当于RR指令。unsignedlong_lror_(unsignedlongval,unsignedcharn)将长整型数据val循环右移n位,相当于RR指令。bit_testbit_(bitx)相当于JBCbit指令unsignedchar_chkfloat_(floatval)测试并返回浮点数状态。void_nop_(void)产生一个NOP指令。表5-4KeilC51编译器提供的本征库函数§5.4.1
本征库函数71_chkfloat_函数返回一个unsignedchar值包含下面的状态信息:返回值意义0
标准浮点数1
浮点数02+INF正溢出3-INF负溢出4NaN(不是一个数)错误状态§5.4KeilC51库函数5.4.2字符判断转换库函数(表5-5)72字符判断转换库函数的原型声明在头文件CTYPE.H中定义。函数名及定义功能说明bit
isalpha(char
c)检查参数字符是否为英文字母,是则返回1,否则返回0bit
isalnum(char
c)检查参数字符是否为英文字母或数字字符,是则返回1,否则返回0bit
iscntrl(char
c)检查参数字符是否为控制字符(值在0x00~0x1f之间或等于0x7f)是则返回1,否则返回0bit
isdigit(char
c)检查参数字符是否为十进制数字0~9,是则返回1,否则返回0bit
isgraph(char
c)检查参数字符是否为可打印字符(不包括空格),值域0x21~0x7e,是则返回1,否则返回0bit
isprint(char
c)检查参数字符是否为可打印字符(包括空格),值域0x20~0x7e,是则返回1,否则返回0bit
ispunct(char
c)检查参数字符是否为标点、空格或格式字符,是则返回1,否则返回0bit
islower(char
c)检查参数字符是否为小写英文字母,是则返回1,否则返回0bit
isupper(char
c)检查参数字符是否为大写英文字母,是则返回1,否则返回0bit
isspace(char
c)检查参数字符是否为空格、制表符、回车、换行、垂直制表符和送纸(值为0x09~0x0d,或为0x20),是则返回1,否则返回0bit
isxdigit(char
c)检查参数字符是否为十六进制数字字符,是则返回1,否则返回0char
toint(char
c)将ASCII字符的0~9、a~f(大小写无关)转换为十六进制数字,返回值为0H~9H,0AH~0FHchar
tolower(char
c)将大写字符转换成小写形式,如果字符参数不在A~Z之间,则该函数不起作用char
_tolower(char
c)将字符参数c与常数0x20逐位相或,从而将大写字符转换成小写字符char
toupper(char
c)将小写字符转换成大写形式,如果字符参数不在a~z之间,则该函数不起作用char
_toupper(char
c)将字符参数c与常数0xdf逐位相与,从而将小写字符转换成大写字符char
toascii(char
c)将任何字符参数值缩小到有效的ASCII范围内,即将c与0x7f相与,去掉第7位以上的位§5.4KeilC51库函数5.4.2字符判断转换库函数(表5-5)73函数名及定义功能说明bit
isupper(char
c)检查参数字符是否为大写英文字母,是则返回1,否则返回0bit
isspace(char
c)检查参数字符是否为空格、制表符、回车、换行、垂直制表符和送纸(值为0x09~0x0d,或为0x20),是则返回1,否则返回0bit
isxdigit(char
c)检查参数字符是否为十六进制数字字符,是则返回1,否则返回0char
toint(char
c)将ASCII字符的0~9、a~f(大小写无关)转换为十六进制数字,返回值为0H~9H,0AH~0FHchar
tolower(char
c)将大写字符转换成小写形式,如果字符参数不在A~Z之间,则该函数不起作用char
_tolower(char
c)将字符参数c与常数0x20逐位相或,从而将大写字符转换成小写字符char
toupper(char
c)将小写字符转换成大写形式,如果字符参数不在a~z之间,则该函数不起作用char
_toupper(char
c)将字符参数c与常数0xdf逐位相与,从而将小写字符转换成大写字符char
toascii(char
c)将任何字符参数值缩小到有效的ASCII范围内,即将c与0x7f相与,去掉第7位以上的位(续表)表5-6输入/输出库函数§5.4KeilC51库函数5.4.3输入/输出库函数(表5-6)74函数名及定义功能说明char
_getkey(void)等待从8051串口读入一个字符并返回读入的字符,这个函数是改变整个输入端口机制时应做修改的唯一一个函数char
getchar(void)使用_getkey从串口读入字符,并将读入的字符马上传给putchar函数输出,其他与_getkey函数相同char
*gets(char
*s,int
n)该函数通过getchar从串口读入一个长度为n的字符串并存入由s指向的数组。输入时一旦检测到换行符就结束字符输入。输入成功时返回传入的参数指针,失败时返回NULLchar
ungetchar(char
c)将输入字符回送到输入缓冲区,因此下次gets或getchar可用该字符。成功时返回char型值,失败时返回EOF,不能处理多个字符char
putchar(char
c)通过8051串行口输出字符,与函数_getkey一样,这是改变整个输出机制所需要修改的唯一一个函数int
printf(const
char
*fmstr[,argument]...)以第一个参数指向字符串制定的格式通过8051串行口输出数值和字符串,返回值为实际输出的字符数int
sprintf(char
*s,const
char
*fmstr[,argument]...)与printf功能相似,但数据是通过一个指针s送入内存缓冲区,并以ASCII码的形式存储int
puts(const
char
*s)利用putchar函数将字符串和换行符写入串行口,错误时返回EOF,否则返回0int
scanf(const
char
*fmstr[,argument]...)在格式控制串的控制下,利用getchar函数从串行口读入数据,每遇到一个符合格式控制串fmstr规定的值,就将它按顺序存入由参数指针argument指向的存储单元。其中每个参数都是指针,函数返回所发现并转换的输入项数,错误则返回EOFint
sscanf(char
*s,const
char
*fmstr[,argument]...)与scanf的输入方式相似,但字符串的输入不是通过串行口,而是通过指针s指向的数据缓冲区void
vprintf(const
char
*s,char
*fmstr,char
*argptr)将格式化字符串和数据值输出到由指针s指向的内存缓冲区内。类似于sprintf,但接受一个指向变量表的指针,而不是变量表。返回值为实际写入到输出字符串中的字符数表5-6输入/输出库函数(续表)§5.4KeilC51库函数5.4.3输入/输出库函数(表5-6)75函数名及定义功能说明int
sprintf(char
*s,const
char
*fmstr[,argument]...)与printf功能相似,但数据是通过一个指针s送入内存缓冲区,并以ASCII码的形式存储int
puts(const
char
*s)利用putchar函数将字符串和换行符写入串行口,错误时返回EOF,否则返回0int
scanf(const
char
*fmstr[,argument]...)在格式控制串的控制下,利用getchar函数从串行口读入数据,每遇到一个符合格式控制串fmstr规定的值,就将它按顺序存入由参数指针argument指向的存储单元。其中每个参数都是指针,函数返回所发现并转换的输入项数,错误则返回EOFint
sscanf(char
*s,const
char
*fmstr[,argument]...)与scanf的输入方式相似,但字符串的输入不是通过串行口,而是通过指针s指向的数据缓冲区void
vprintf(const
char
*s,char
*fmstr,char
*argptr)将格式化字符串和数据值输出到由指针s指向的内存缓冲区内。类似于sprintf,但接受一个指向变量表的指针,而不是变量表。返回值为实际写入到输出字符串中的字符数输入/输出库函数的原型声明在头文件STDIO.H,通过8051内核单片机的串行口工作,若希望支持其他I/O接口,只需改动_getkey()和putchar(),库中所有其他I/O支持函数都依赖于这两个函数模块。在使用8051系列单片机的串行口之前,应先对其进行初始化。例,晶振频率11.0592MHz,若串口1通信的参数为9600,n,8,1(9600波特率,8个数据位,1个停止位,没有奇偶校验位),则初始化语句:voidUartInit(void)//9600bps@11.0592MHz{SCON=0x50;//8位数据,可变波特率AUXR&=0xBF;//定时器1时钟为Fosc/12,即12TAUXR&=0xFE;//串口1选择定时器1为波特率发生器TMOD&=0x0F;//设定定时器1为16位自动重装方式TL1=0xE8;//设定定时初值TH1=0xFF;//设定定时器初值ET1=0;//禁止定时器1中断TR1=1;//启动定时器1TI=1;//直接使用printf必须加入此句才能实现发送
}§5.4KeilC51库函数5.4.3输入/输出库函数(表5-6略)76例如:_getkey()函数的定义(在Keil安装文件夹LIB的C文件中)#include<reg51.h>char_getkey(){charc;while(!RI);
c=SBUF;RI=0;return(c);}【例5-1】输出库函数printf()的应用举例:#include<stdio.h>#include<stc15.h>voidUart1Init(void);//将前面定义的串口1初始化函数进行声明voidmain(void){ chara; intb; longc; unsignedcharx; unsignedinty; unsignedlongz; floatf,g; charbuf[]="TestString"; char*p=buf;
Uart1Init();
//调用串口1初始化函数
a=1;b=12365;c
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 肺气肿管理流程指南
- 核医学科甲状腺功能障碍核素治疗指南
- 军校学员个人情况报告
- 麻醉科全麻术前评估指南
- 急诊科心血管内科创伤性休克急救培训要点
- 婚礼宴会设计策划方案
- 巴厘岛风格景观设计
- 基于Spark的实时日志分析平台前沿技术课程设计
- c 的课程设计的致谢
- 湖北2026年一级建造师考试(民航机场工程管理与实务)模拟题含答案及答案
- TSG08-2026《特种设备使用管理规则》解读
- 2026年等离子体物理考研复试高频面试题包含详细解答
- 门诊消防安全责任制制度
- T-STSI 43-2023 人工智能算力资源池技术规范
- 2025黑龙江省农业投资集团有限公司权属企业市场化选聘10人笔试参考题库附带答案详解(3卷)
- 江苏师范大学本科毕业论文开题报告格式
- 做账实操-高新技术行业会计真账实操 SOP
- GB/T 32684-2025塑料酚醛树脂游离甲醛含量的测定
- 智能化环境风险评估-洞察与解读
- 2025广东省监狱管理局所属事业单位招聘医疗卫生专业技术人才99人考试参考试题及答案解析
- 国企廉洁从业课件教学
评论
0/150
提交评论