第1章C51数据类型与运算
1.1C51数据类型
C51的数据类型如下所示:
C51编译器支持的数据类型、长度和值域如下表1-1所示。
与面向数学运算的计算机相比,51单片机对变量类型或数据类型的选择更具有关键性意义。如果在程序设计中使用大量而不必要的变量类型,这会导致C编译器调用库函数的数量,以处理大量的变量类型和数据类型。所以必须特别慎重地进行变量和数据类型的选择。
1.2C51数据存储类型
C51编译器还可以通过将变量、常量定义成不同的存储类型(data,bdata,idata,pdata,xdata,code)的方法,将它们定义在不同的存储区中。
存储类型与51单片机实际存储空间的对应关系如表1-2所示。
储区中。片内RAM是存放临时性传递变量或使用频率较高变量的理想场所。访问片内数据存储器(data、bdatai、data)比访问片外数据存储器(xdata、pdata)相对快一些,因此可将经常使用的变量置于片内数据存储器,而将规模较大的或不常使用的数据置于片外数据存储器中。
C51存储类型及其大小和值域如表1-3所示。例如:
表1-3 C51存储类型及其大小和值域
char data var1; /*字符变量var1被定义为data存储类型,定位在片内RAM中*/
bit bdata flags; /*位变量flags被定义为data存储类型,定位在片内RAM中的位寻址区*/
/*(20H~2FH)*/
float idata x,y,z; /*浮点变量x,y,z被定义为idata存储类型,定位在片内RAM
中,并只能用间接寻址的方法进行访问*/
unsigned int pdata dimension; /*无符号整型变量dimension被定义为pdata存储类
型,定位在片外RAM中,并用MOVX @Ri访问*/ unsigned char xdata vector[10][4][4]; /*无符号字符三维数组变量vector[10][4][4]
被定义为xdata存储类型,定位在片外RAM
中,占据10×4×4=160个字节空间*/
如果在变量定义时省略存储类型标志符,编译器会自动默认存储类型。默认的存储类型进一步由SMALL、COMPACT和LARGE存储模式指令限制,见表1-4所示。
存储模式决定了变量的默认存储类型、参数传递区和无明确存储类型的说明。例如,char var1在SMALL存储模式下,var1被定位在data存储区;在COMPACT模式下,var1被定位
pdata存储区,在LARGE模式下,var1被定位在xdata存储区中。
1.3C51定义SFR(特殊功能寄存器)
51单片机内有21个特殊功能寄存器(SFR),它们分布在片内RAM的高128B中,对特殊功能寄存器只能用直接寻址方式访问。特殊功能寄存器中还有11个可位寻址的寄存器。
在C51中,特殊功能寄存器及其可位寻址的位是通过关键字sfr和sbit来定义的,这种方法与标准C不兼容,只能用于C51。例如:
sfr PSW=0xD0; /*定义程序状态字PSW的饿地址为D0H*/
sfr TMOD=0x89; /*定义定时器/计数器方式控制寄存器TMOD的地址为89H*/
sfr P1=0x90; /*定义P1口的地址为90H*/
PSW是可位寻址的SFR,其中各位可用sbit定义。例如:
sbit CY=0Xd7; /*定义进位标志CY的地址为D7H*
sbit AC=0xD0^6; /*定义辅助进位标志AC的地址为D6H*/
sbit RS0=0XD0^3; /*定义RS0的地址为D3H*/
注意:sfr和sbit只能在函数外使用,一般放在程序的开头。
实际上大部分特殊功能寄存器及其可位寻址的位的定义在reg51.h、reg52.h等头文件中已经给出,使用时只需在源文件中包含相应的头文件,即可使用SFR及其可位寻址的位;而对于未定义的位,使用前必须先定义。例如:
#include
sbit P10=P1^0;
sbit P12=P1^2;
main()
{
P10=1;
P12=0;
PSW=0x08;
……
}
1.4C51定义并行口
51单片机的基本I/O只有P0、P1、P2和P3四个,除此之外,还可以在片外扩展I/O口和其他功能芯片,它们与外部数据存储器统一编址,即51单片机把它们当做外部数据存储器单元。
P0、P1、P2和P3的定义在头文件reg51.h和reg52.h中,扩展的外部RAM单元和外部I/O需要用户自己定义。例如:
#include
#define PA XBYTE[0xffec]
Main()
{
PA=0x3A; /*将数据3AH写入地址为0xffec的存储单元或I/O端口*/
}
以上程序用C的编译预处理命令#define将PA定义为外部I/O口,地址为0xffec,是单字节量。其中XBYTE是一个指针,指向外部数据存储器的零地址单元,它是在头文件absacc.h 中定义的。
1.5C51定义位变量
51单片机位运算器,C51相应地设置了位数据类型。
1.位变量的定义
位变量用关键字“bit”来定义,它的值是一个二进制位。例如:
bit lock; /*将lock定义位位变量*/
bit direction; /*将direction定义为位变量*/
2.函数参数和返回值的类型
函数可以有bit类型参数,也可以有bit类型的返回值。例如:
bit func(bit b0,bit b1)
{
bit a;
……
Return a;
}
使用禁止中断宏命令#progma disable或指定明确的寄存器切换(using n)的函数不能返
回位值。
3.对位变量定义的限制
不能定义位变量的指针,如:
bit *bit_point;
不能定义位数组,如:
bit bit_array[5];
位变量说明中可以指定存储类型,位变量的存储类型只能是bdata。
在程序设计时,对于可位寻址的对象,既可以字节寻址又可以位寻址的变量,则其存储类型只能是bdata。
使用时,先说明字节变量的数据类型和存储类型。例如:
int bdata a; /*整型变量a定位在片内数据存储区中的可位寻址区*/
char bdata b[4]; /*字符数组b定位在片内数据存储区中的可位寻址区*/
然后,使用sbit关键字定义其中可独立寻址的位变量。例如:
sbit a0=a^0; /*定义a0为a 的第0位*/
sbit a12=a^12; /*定义a12为a的第12位*/
sbit b03=b[0]^3; /*定义b03为b[0]的第3位*/
sbit b36=b[3]^6; /*定义b36为b[3]的第6位*/
sbit定义要求基址对象的存储类型为bdata。
使用sbit类型位变量时,基址变量和其对应的位变量的说明必须在函数外部进行。
1.6C51运算符、表达式及其规则
C51的运算符主要有:算术运算符、关系运算符、逻辑运算符、赋值及复合赋值运算符等等。
1.算术运算符和算术表达式
(1)基本的算术运算符。C51最基本的算术运算符有以下五种:
+ 加法运算符
- 减法运算符
* 乘法运算符
/ 除法运算符
% 模运算或取余运算符
对于除法运算符:若两个整数相除,结果为整数(即取整)。
对于取余运算符:要求%两侧的操作数均为整型数据,所得结果的符号与左侧操作数的符号相同。
(2)自增、自减运算符。++为自增运算符,--为自减运算符。例如:++j、j++、--i、i--。
++和--运算符只能用于变量,不能用于常量和表达式。++j表示先加1,再取值;j++表示先取值,再加1。同理,自减运算也是这个道理。
(3) 算术表达式和运算符的优先级与结合性。用算术运算符和括号将运算对象连接起来的式子称为算术表达式。其中的运算对象包括常量、变量、函数、数组、结构等。例如:a+b*c/d 。
C51规定算术运算符的优先级和结合性为:先乘除模,后加减,括号最优先。
如果一个运算符两侧的数据类型不同,则必须通过数据类型转换将数据转换成同种类型。转换方式有以下两种。
一是自动类型转换,即在程序编译时,由C 编译器自动进行数据类型转换。转换规则如下:
一般来说,当运算对象的数据类型不相同时,先将较低的数据类型转换成较高的数据类型,运算结果为较高的数据类型。
二是强制类型转换,使用强制类型转换运算符,其形式为:(类型名)(表达式)。例如: (double )a 将a 强制转换成double 类型 (int )(x+y )将x+y 强制转换成int 类型
2. 关系运算符和关系表达式
(1) 关系运算符及其优先级。关系运算即比较运算。C51提供了以下六中关系运算符 < 小于 <= 小于等于 > 大于 >= 大于等于 == 等于 != 不等于
优先级关系是:<、<=、>、>=这四个运算符的优先级相同,处于高优先级;==和!=这两个运算符的优先级相同,处于低优先级。关系运算符的优先级低于算术运算符的优先级,而高于赋值运算符的优先级。
(2)关系表达式。用关系运算符将运算对象连接起来的式子称为关系表达式。如:a>b,a+b>=c+d,(a=3)<(b=2)等。
关系表达式的值为逻辑值:真和假。C51中用0表示假,用1表示真。
例如有关系表达式a>=b,若a的值是4,b的值是3,则给定关系满足,关系表达式的值为1,即逻辑真;若a的值是2,则给定关系不成立,关系表达式的值为0,即逻辑假。
3.逻辑运算符和逻辑表达式
(1)逻辑运算符及其优先级。逻辑运算是对逻辑量进行运算。C51提供三种逻辑运算符。如下:
&& 逻辑与
|| 逻辑或
! 逻辑非
它们的优先级关系是:!的优先级最高,而且高于算术运算符;||的优先级最低,它低于关系运算符,却高于赋值运算符。
(2)逻辑表达式。用逻辑运算符将运算对象连接起来的式子称为逻辑表达式。运算对象可以是表达式或逻辑量,而表达式可以是算术表达式、关系表达式或逻辑表达式。逻辑表达式的值也是逻辑量,即真或假。
对于算术表达式,其值若为0,则认为是逻辑假;若不为0,则认为是逻辑真。逻辑表达式的执行规则是:逻辑表达式不一定完全被执行,只有当一定要执行下一个逻辑运算符才能确定表达式的值时,才执行该运算符。例如:a&&b&&c,若a的值为0,则不需要判断b 和c的值就可确定表达式的值为0。
又如:a||b||c,若a 值为0,则还需判断b的值,若b的值为1,则不需判断c的值就可确定表达式的值为1。
4.位运算符及其表达式
位运算的操作对象只能是整型和字符型数据,不能是实型数据。C51提供以下六种位运算:
& 按位与,相当于ANL指令
| 按位或,相当于ORL指令
^ 按位异或,相当于XRL指令
~ 按位取反,相当于CPL指令
<< 左移,相当于RL指令
>> 右移,相当于RR指令
5.赋值运算符和赋值表达式
(1)赋值运算符。赋值运算符就是赋值号“=”,赋值运算符的优先级低,结合性是右结合性。
(2)赋值表达式。将一个变量与表达式用赋值号连接起来就构成赋值表达式。形式如下:
变量名=表达式
赋值表达式中表达式包括变量、算术运算表达式、关系运算表达式、逻辑运算表达式等,甚至可以是另一个赋值表达式。赋值过程是将“=”右边表达式的值赋给“=”左边的一个变量,赋值表达式的值就是赋值变量的值。例如:
a=b=5,该表达式的值为5
a=(b=4)+(c=6),该表达式的值为10
(3)赋值的类型转换规则。在赋值运算中,当“=”两侧的类型不一致时,系统自动将右边表达式的值转换成左边变量的类型,再赋给该变量。转换规则如下:
①实型数据赋给整型变量时,舍弃小数部分。
②整型数据赋给实型变量时,数值不变,但以浮点数形式存储在变量中。
③长字节整型数据赋给短字节整型变量时,实行截断处理。如将long型数据赋给int 型变量时,将long型数据的低两字节数据赋给int型变量,而将long型数据的高两字节的数据丢弃。
④短字节整型数据赋给长字节整型变量时,进行符号扩展。如将int型数据赋给long 型变量时,将int型数据赋给long型变量的低两字节,而将long型变量的高两字节的每一位都设为int型数据的符号值。
6.复合赋值运算符
赋值号前加上其他运算符构成复合运算符。C51提供以下十种复合运算符:+=,-=,*=,/=,%=、&=,|=,^=,<<=,>>=。例如:
a+=b 等价于a=(a+b)
x*=a+b 等价于x=(x*(a+b))
a&=b 等价于a=(a&b)
a<<=4 等价于a=(a<<4)
第2章 C51流程控制语句
C51程序与其他语言程序一样,程序结构也分为顺序结构、选择结构或分支结构、循环结构三种。由于顺序结构比较简单,在此不多讲述,下面就选择语句和循环语句进行叙述。
2.1选择语句
选择语句即条件判断控制语句,它首先判断给定的条件是否满足,然后根据判断的结果决定执行给出的若干种选择之一。C51中选择语句有if语句、switch/case语句。
1.if语句
C51提供三种形式的if语句:
(1)if(表达式){语句;}
例如:
if(p1!=0)
{c=20;}
(2)if(表达式){语句1;}else{语句2;}
例如:
if(p1!=0)
{c=20;}
Else
{c=0;}
(3)if(表达式1){语句1;}
else if(表达式2){语句2;}
else if(表达式3){语句3;}
……
else if(表达式n){语句n;}
else {语句n+1;}
例如:
if(a>=1){c=10;}
else if(a>=2){c=20;}
else if(a>=4){c=40;}
else {c=0;}
(4)if语句的嵌套。在if语句中又含有一个或多个if语句,这种情况称为if语句的嵌套。If语句的嵌套的基本形式如下:
请注意if 与else 的对应关系,else 总是与它前面最近的一个if 语句相对应最好使内层嵌套的if 语句也包含else 部分(不要省略),这样,程序中if 的数目与else 的数目一一对应,不至于出错。另外在编程时最好使用相同深度的缩进排写的形式将同一层次上的if-else 语句在同一列的位置上对齐,这样不仅不易出错,也便于阅读程序。
例1-1 如图1-1所示,单片机P1口的P1.0和P1.1各接一个开关S1、S2,P1.4、P1.5、P1.6和P1.7各接一只发光二极管。由S1和S2的不同状态来确定哪个发光二极管被点亮。如表1-5所示。
图2-1 例1-1附图
程序如下:
#include
{
char a;
a=P1 /*读P1口*/
a=a&0x03; /*屏蔽高6位*/
if(a==0) P1=0x83;
else if a==1 P1=0x43;
else if a==2 P1=0x23;
else P1=0x13;
}
2.switch/case语句
switch/case语句是多分支选择语句,它的饿一般形式如下:
switch (表达式)
{
case 常量表达式1:语句1;break;
case 常量表达式2:语句2;break;
……
case 常量表达式n:语句n;break;
default:语句n+1;
}
(1)当switch括号中的表达式的值与某一case后面的常量表达式的值相同时,就执行它后面的语句,然后因遇到break而退出switch语句。若所有的case中的常量表达式的值都没有与表达式的值相匹配时,就执行default后面的语句。
(2)每一个case的常量表达式必须时互不相同的,否则将出现混乱局面。
(3)各个case和default出现的次序,不影响程序的执行结果。
(4)如果在case语句中遗忘了break,则程序执行了本行之后,不会按规定退出switch 语句,而是执行后续的case语句。
例1-2 将例1-1用switch/case语句改写。
程序如下:
#include “reg51.h”
void main()
{
char a;
a=P1; /*读P1口*/
a=a&0x03; /*屏蔽高6位*/
switch (a)
{
case0:P1=0x83;break;
case1:P1=0x43;break;
case2:P1=0x23;break;
case3:P1=0x13;
}
}
2.2循环语句
C51循环种类有当型循环和直到型循环,有四种循环实行方法。
1.if语句和goto语句
goto语句只能构成简单循环,与if语句一起可以实现当型和直到型循环。
⑴构成当型循环
loop:if(表达式)
{语句
`goto loop;
}
⑵构成直到型循环
loop:{语句
if(表达式)goto loop;
}
例1-3 例1-2的程序只能执行一遍,如果需要在程序执行过程中随时改变开关状态,进而改变二极管的发光状态,就需要不停地执行程序,现用goto语句构成死循环的程序如下:#include “reg51.h”
void main()
{
char a;
loop:
a=P1;
a=a&0x03; /*屏蔽高6位*/
switch (a)
{
case0:P1=0x83;break;
case1:P1=0x43;break;
case2:P1=0x23;break;
case3:P1=0x13;
}
goto loop;
}
2.while语句
while语句用来实现当型循环。其一般格式为:
while(表达式)语句
表达式可以是任何表达式,语句可以是符合语句。
while语句的执行过程:
(1)计算表达式的值;
(2)若其值为非0,则执行内嵌语句(循环),若其值为0,则退出while循环。
例1-4 将例1-3的死循环用while循环实现。
程序如下:]
#include “reg51.h”
void main()
{
char a;
while (1)
{
a=P1;
a=a&0x03; /*屏蔽高6位*/
switch (a)
{
case0:P1=0x83;break;
case1:P1=0x43;break;
case2:P1=0x23;break;
case3:P1=0x13;
}
}
}
3.do-while语句
do-while语句实现直到型循环。其一般格式为:
do 语句while (表达式);
do-while语句的特点是:先执行语句,后判断表达式。执行过程为:
(1)执行内嵌的语句;
(2)计算表达式。当表达式的值为非0时,则循环;当表达式的值为0时,执行do-while 语句下面的语句。
例1-5 将例1-4用do-while语句改写。
程序如下:
#include “reg51.h”
void main()
{
char a;
do {
a=P1;
a=a&0x03; /*屏蔽高6位*/
switch (a)
{
case0:P1=0x83;break;
case1:P1=0x43;break;
case2:P1=0x23;break;
case3:P1=0x13;
}
} while (1);
}
4.for语句
for语句的一般形式为:
for (表达式1;表达式2;表达式3)语句
它的执行过程为:
(1)求解表达式1;
(2)求解表达式2,若其值非0,则执行内嵌语句;若其值为0,则退出循环;
(3)求解表达式3,回到第2步;
for语句最简单的应用形式是:
for(循环变量初值;循环条件;循环变量改变)语句
例1-6 求1~100的累加和。
main()
{
float sum=0;
int n;
for(n=1;n<=100;n++)
{
sum=sum+(float)n;
}
}
for语句中,可以没有表达式1、表达式2或表达式3。若三个表达式都没有,则相当于一个死循环。
例1-7 将例1-5用for语句改写。
程序如下:
#include “reg51.h”
void main()
{
char a;
for (;;)
{
a=P1;
a=a&0x03; /*屏蔽高6位*/
switch (a)
{
case0:P1=0x83;break;
case1:P1=0x43;break;
case2:P1=0x23;break;
case3:P1=0x13;
}
}
}
第3章C51构造数据类型
3.1数组
数组是关键数据的有序集合,数组中的每个元素都是统一类型的数据。数组集合用一个名字来标识。数组中元素的顺序用下标表示,下标表示该元素在数组中的位置。下标为n的元素可以表示为数组名[n]。改变[ ]中的下标就可以访问数组中所有的元素。一个数组元素等同于一个变量,因此又可以说数组是一组相同数据类型的相关变量的有序集合。
1.一维数组
由具有一个下标的数组元素组成称为一维数组。
(1)一维数组的定义。一维数组定义的一般形式为:
类型说明符数组名[元素个数];
数组名是一个标识符,元素个数是一个常量表达式,不能是含有变量的表达式。例如:int a[50]; /**定义一个数组名为a 的数组,数组包含50个整型的元素/
(2)一维数组的初始化。在定义数组时可以对数组整体初始化,若定义后想对数组赋值,则只能对每个元素分别赋值。例如:
int a[5]={1,2,3,4,5}; /*给全部元素赋值,a[0]=1,a[1]=2,a[2]=3,a[3]=4,a[4]=5*/
int b[6]={1,2,6}; /*给部分元素赋值,b[0]=1,b[0]=2,b[0]=6,b[0]= ,b[3]= b[4]=
b[5]=0*/
int d[10];d[0]=4;d[1]=-6;……/*定义完后再赋值*/
2.二维数组
由具有两个下标的数组元素组成的数组称为二维数组。
(1)二维数组的定义。二维数组定义的一般形式是:
类型说明符数组名[行数][列数];
数组名是一个标识符,行数和列数都是常量表达式。例如:
float a[3][4]; /*a数组有3行4列共12个实型元素*/
(2)二维数组的初始化。与一维数组的初始化相似,定义时可以整体初始化,也可以在定义后单个地进行赋值。例如:
int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; /*全部初始化*/
int a[3][4]= {{1,2,3,4},{5,6,7,8},{}}; /*部分初始化,
a[2][0]=a[2][1]=a[2][2]=a[2][3]=0*/
3.字符数组
若一个数组的元素是字符型的,则该数组就是字符数组。
(1)字符数组的定义与赋值。与一维数组的定义赋值的方法类似,例如:
char a[12]={“Chong Qing”};
(2)字符串和字符串结束符。C语言中没有字符串变量,需用字符数组来处理字符串。当数组中存放的实际字符个数与数组的长度不一样时,为了测定字符串的实际长度和使用系统提供的各种字符串函数,C语言规定了字符串结束标志‘\0’,它是一个ASCII码值为0的字符。在一个字符数组中,一旦遇到字符‘\0’,就表示字符串结束,其后的字符忽略不计。上面a数组的后2个元素皆为‘\0’,字符串常量中也自动包含一个字符串结束符‘\0’。
4.查表
数组的一个很重要的用途就是查表。在单片机应用中,常常要对数学公式进行计算以及对一些传感器的非线性进行补偿,这时,采用查表的办法比较简单有效。因为单片机的计算能力有限,可以将复杂的数学公式或补偿算法事先计算成表格,存入程序存储器中,而这个表格就是数组。
3.2指针
关于指针的概念请参考有关资料,下面就C51的指针类型做些说明。
C51支持“基于存储器”的指针和“一般”指针。当定义一个指针变量时,若未指定它所指向的对象的存储器类型,则该指针变量被认为是一般指针;反之若指定了它所指对象的存储类型,则该指针被认为是基于存储器的指针。
基于存储器类型的指针由C源代码中指定的存储类型决定,并在编译时确定,这种指针只需1~2个字节,并且效率高。
一般指针需占3个字节:第一个字节为存储器类型的编码(由编译模式的默认值确定),剩余两个字节为地址偏移量。存储器类型决定了对象所用的51单片机存储空间,偏移量指向实际地址。一个“一般”指针可以访问任何变量而不管它存储空间的具体位置。这就允许一般函数,如memcpy()等将数据从任意一个地址拷贝到另一个地址空间。
(1)基于存储器的指针。基于存储器的指针是在说明一个指针时,指定它所指向的对象的存储类型。一般占2字节。例如:
char xdata *px;
px为指向一个定义在xdata存储器中的字符变量的指针变量。px本身在默认的存储器区域(由编译模式决定),其长度为2字节。
前面已经讲到,C51有三种存储模式:即SMALL、COMPACT和LARGE。C51的存储模式确定函数的参数与局部变量的存储区域。在SMALL模式下,函数的参数与局部变量位于单片机的内部RAM,在COMPACT模式下,函数的参数与局部变量位于单片机外部RAM。例如:
char xdata *data py;
py为指向一个定义在xdata存储器中的字符变量的指针变量。py本身在RAM中,与编译模式无关,其长度也为2字节。
(2)一般指针。在函数的调用中,函数的指针参数需要用一般指针。一般指针的说明形式如下:
char *pz;
这里没有指定指针变量pz所指向的变量的存储类型,pz处于编译模式默认的存储区,长度为3
在用常量指针时,如定义外部端口的地址,必须注意正确定义存储类型和偏移量。例如,要将数值0x41写入地址为0x8000外部数据存储器中,可用下列代码实现:
#include
XBYTE[0x8000]=0x41;
其中XBYTE是一个指针,它在头文件absacc.h中定义的,定义如下:
#define XBYTE ((unsigned char *) 0x20000L)
XBYTE被定义为(unsigned char *) 0x20000L,它是一个一般指针,其存储类型为2,即为xdata型,偏移量是0000,这样,XBYTE成为指向外部数据存储器的零地址单元的指针。而XBYTE[8000]则表示外部数据存储器的0x8000单元。
C51与标准C语言类似,它的构造类型数据还有结构体(struct)、共用体(union)和枚举(enum)等。请大家参阅有关资料,在此就不再介绍了。
第4章C51函数
4.1函数的定义与分类
1.函数的分类
从C语言程序的结构上划分,C语言函数分为主函数main()和普通函数两种。而对于普通函数,从不同的角度或以不同的形式可分为:标准库函数和用户自定义函数。
(1)标准库函数。标准库函数是由C编译系统的库函数提供的,在C便衣系统中将一些独立的功能模块写成公用函数,并将它们集中存放在系统的库函数中,供程序设计时使用。故把这种函数称为标准库函数。C51也提供了比较丰富的库函数资源,将在后面对常用的库函数的功能进行说明。
(2)用户自定义函数。用户自定义函数是用户根据自己的需要而编写的函数。从函数的定义的形式上划分为:无参数函数、有参数函数和空函数。
无参数函数:此种函数被调用时,既无参数输入,也不返回结果给调用函数,它是为完成某种操作而编写的。
有参数函数:在调用此种函数时,必须提供实际的输入参数,必须说明与实际参数一一对应的形式参数,并在函数结束时返回结果供调用它的函数使用。
空函数:此种函数体内无语句,是空白的。调用此种函数时,什么工作也不做。而定义此种函数的目的并不是为了执行某种操作,而是为了给以后程序功能的扩充。
2.函数的定义
函数定义的一般形式为:
返回值类型函数名(形式参数列表)
{
函数体
}
例如:int max(int x,int y,int z)
返回值的数据类型为整型,函数名为max,x、y、z为3个整型入口参数。
(1)关于返回值类型:
①可以是基本数据类型(int,char,float,double)及指针类型。
②当函数没有返回值时,用标识符void说明该函数没有返回值。
③若没有指定返回值类型,默认返回值类型为整型类型。
④一个函数只有一个返回值,该返回值是通过函数中的return语句获得的。
(2)函数名必须是一个合法的标志符。
(3)形式参数列表包括了函数所需全部参数的定义。此时函数的参数称为形式参数,简称形参。形参可以是基本数据类型的数据、指针类型数据、数组等。在没有调用函数时,函数的形参和函数内部的变量未被分配内存单元,即它们是不存在的。
(4)函数体由两部分组成:函数内部变量定义和函数体其他语句。
(5)各函数的定义是独立的。
(6)函数的定义不能在另一个函数的内部。
4.2函数的调用
函数调用的一般形式为:
函数名(实际参数列表);
在一个函数中需要到某个函数的功能时,就调用该函数。调用者称为主调函数,被调用者称为被调函数。若被调函数是有参函数,则主调函数必须把被雕函数所需的参数传递给被调函数。传递给被调函数的数据称为实际参数,简称实参。若被调函数是无参函数,则调用该函数时,可以没有参数列表,但括号不能省。被调函数执行完后再返回主调函数继续执行剩余程序。实参与形参在熟练数量、类型和顺序上都必须一致;实参可以是常量、变量和表达式;实参对形参的数据传递是单向的,即只能将实参传递给形参。
例1-8 编写函数求三个整数中的最大值。
int max(int x,int y,int z)
{
int a=x;
if(y>x) a=y;
if(z>a) a=z;
return a;
}
Main()
{
int x1,y1,z1;
printf(“Enter 3 numbers\n”);
scanf(“%d%d%d”,&x1,&y1,&z1);
printf(“The max is %d\n”,max(x1,y1,z1));
}
例中:
(1)函数max有三个形参x、y、z,都是整型参数;函数main有三个实参x1、y1、z1,
也都是整型。
(2)变量a是函数max的内部变量,只在函数max内有效。
(3)函数的返回值是通过语句return a;实现的。
(4)一个函数只能返回一个值,可以有多个return语句,但只有一个被执行。
(5)函数的形参和内部变量可以与其它函数的形参和内部变量同名。
4.3函数的嵌套调用与递归调用
函数的嵌套调用就是在调用一个函数的过程中,又调用另一个函数。
函数的递归调用就是一个函数在其函数体内调用自己。递归调用是一种特殊的循环结
实训任务二:控制LED灯点亮 实训准备:KeilC51软件, proteus仿真软件,STP-ISC下载软件,单片机实验板,电源线、下载线 分组情况:每4人为一组,组长一名。小老师两名协助老师指导操作过程。知识目标:1.了解单片机各引脚功能; 2.理解单片机最小系统组成部分; 3.掌握C51赋值语句用法; 4.掌握C51语言编程、编译基本方法; 5.掌握proteus仿真软件基本操作方法; 6.掌握C51程序编写、编译、仿真调试、下载流程及方法。 能力目标:1.培养学生数字逻辑分析能力; 2.培养学生分析问题及解决问题的能力; 情感目标:1.培养学生团队合作的精神; 2.培养学生的创新意识; 教学重点:1.C51赋值语句用法; 2.C51语言编程、编译基本方法 教学难点:1.半英文操作界面的理解 2.调试程序的方法 课时:8课时
讲授新课1.单片机引脚功能(40引脚) 电源、接地、I/O端口、控制引脚、时钟引脚、 复位引脚 2.单片机最小系统 组成部分:单片机、电源、接地、复位电路、 时钟电路。 解释时钟电路,比喻为学校的铃声。 区分:单片机系统与最小系统 3.C51语言基本格式 #include
实训任务三:控制LED流水灯 实训准备:KeilC51软件, proteus仿真软件,STC-ISP下载软件, 单片机实验板,电源线、下载线 分组情况:每3-4人为一组,组长一名。小老师两名协助老师指导操作过程。知识目标:1.理解C51语言数据类型; 2.了解单片机的机器周期; 3.理解数组概念及用法; 4.掌握for循环语句的用法; 5.掌握while循环语句的简单用法; 6.掌握C51程序编写、编译、仿真调试、下载流程及方法。 能力目标:1.培养学生思维逻辑分析能力; 2.培养学生分析问题及解决问题的能力; 情感目标:1.培养学生团队合作的精神; 2.培养学生的创新意识; 教学重点:1.for循环语句的用法; 2.数组的概念及用法; 3.C51语言数据类型; 教学难点:1.for循环语句的用法; 2.数组的概念及用法; 课时:4课时 子任务一:控制LED灯闪烁(2课时)
第一章 1.给出下列有符号数的原码、反码和补码(假设计算机字长为8位)。 +45 -89 -6 +112 答:【+45】原=00101101,【+45】反=00101101,【+45】补=00101101 【-89】原=11011001,【-89】反=10100110,【-89】补=10100111 【-6】原=10000110,【-6】反=11111001,【-6】补=11111010 【+112】原=01110000,【+45】反=01110000,【+45】补=01110000 2. 指明下列字符在计算机内部的表示形式。 AsENdfJFmdsv120 答:41H 73H 45H 4EH 64H 66H 4AH 46H 6DH 64H 73H 76H 31H 32H 30H 3. 什么是单片机? 答:单片机是把微型计算机中的微处理器、存储器、I/O接口、定时器/计数器、串行接口、中断系统等电路集成到一个集成电路芯片上形成的微型计算机。因而被称为单片微型计算机,简称为单片机。 4. 单片机的主要特点是什么? 答:主要特点如下: 1) 在存储器结构上,单片机的存储器采用哈佛(Harvard)结构 2) 在芯片引脚上,大部分采用分时复用技术 3) 在内部资源访问上,采用特殊功能寄存器(SFR)的形式 4) 在指令系统上,采用面向控制的指令系统 5) 内部一般都集成一个全双工的串行接口 6) 单片机有很强的外部扩展能力 5. 指明单片机的主要应用领域。 答:单机应用:1) 工业自动化控制;2) 智能仪器仪表;3) 计算机外部设备和智能接口;4) 家用电器多机应用:功能弥散系统、并行多机处理系统和局部网络系统。 第二章 1. MCS-51单片机由哪几个部分组成? 答:MCS-51单片机主要由以下部分组成的:时钟电路、中央处理器(CPU)、存储器系统(RAM和ROM)、定时/计数器、并行接口、串行接口、中断系统及一些特殊功能寄存器(SFR)。 2. MCS-51的标志寄存器有多少位,各位的含义是什么?
基础知识:51单片机编程基础 第一节:单数码管按键显示 第二节:双数码管可调秒表 第三节:十字路口交通灯 第四节:数码管驱动 第五节:键盘驱动 第六节:低频频率计 第七节:电子表 第八节:串行口应用 基础知识:51单片机编程基础 单片机的外部结构: 1. DIP40双列直插; 2. P0,P1,P2,P3四个8位准双向I/O引脚;(作为I/O输入时,要先输出高电平) 3. 电源VCC(PIN40)和地线GND(PIN20); 4. 高电平复位RESET(PIN9);(10uF电容接VCC与RESET,即可实现上电复位) 5. 内置振荡电路,外部只要接晶体至X1(PIN18)和X0(PIN19);(频率为主频的12倍) 6. 程序配置EA(PIN31)接高电平VCC;(运行单片机内部ROM中的程序) 7. P3支持第二功能:RXD、TXD、INT0、INT1、T0、T1 单片机内部I/O部件:(所为学习单片机,实际上就是编程控制以下I/O部件,完成指定任务) 1. 四个8位通用I/O端口,对应引脚P0、P1、P2和P3; 2. 两个16位定时计数器;(TMOD,TCON,TL0,TH0,TL1,TH1) 3. 一个串行通信接口;(SCON,SBUF) 4. 一个中断控制器;(IE,IP) 针对AT89C52单片机,头文件AT89x52.h给出了SFR特殊功能寄存器所有端口的定义。 C语言编程基础: 1. 十六进制表示字节0x5a:二进制为01011010B;0x6E为01101110。 2. 如果将一个16位二进数赋给一个8位的字节变量,则自动截断为低8位,而丢掉高8位。 3. ++var表示对变量var先增一;var—表示对变量后减一。 4. x |= 0x0f;表示为x = x | 0x0f; 5. TMOD = ( TMOD & 0xf0 ) | 0x05;表示给变量TMOD的低四位赋值0x5,而不改变TMOD的高 四位。 6. While( 1 ); 表示无限执行该语句,即死循环。语句后的分号表示空循环体,也就是{;}
《程序设计基础》说课稿 各位评委老师大家好,今天我给大家带来的是电子专业电子技术基础的说课,下面我就以《程序设计基础》的一堂实训课为课题进行我的说课内容。我主要从教材分析、学情分析、教学目标、教学重点及难点、教法学法、教学过程、教学评价与反思七个方面对本堂课进行阐述。 一、教材分析 我选用的教材是朱家建老师编写的《单片机与可编程控制器》,这本教材知识点讲解细腻,结构和内容新颖,并且注重理论和实践相结合,非常符合中职学生以就业为导向的实际。而《单片机与可编程控制器》这么学科本身既是高考和单招考试的必考科目,又是各用人单位招聘相关专业学生的考核重点,因此在整个电子专业的学习体系中地位突出。 我选题的《程序设计基础》是教材中第三章第三节的内容,详见教材P56页—P65页,它既作为指令格式和指令系统知识的延伸学习,又是之后各种程序设计的基础与铺垫,在整个单片机学习过程中起着至关重要的作用。 二、学情分析 我面对的学生是中职二年级电子专业的学生: 知识储备:已经掌握指令格式、寻址方式以及指令系统等基础知识; 学习能力:思维活跃、乐于动手操作,但基础薄弱、缺乏自信、特别是对理论知识的学习积极性不高。 应对措施:针对学生的这些情况,我在教学过程中首先才用鼓励式教学,多鼓励、多表扬,以此提高学生自信心。其次采用理实一体化教学模式,突出 学生爱动手操作的优点,使学生在实践中学习理论,在理论中磨练实 践。 三、教学目标 基于本堂课的教学要求和对学生实际情况的掌握,我将该节课的教学目标设定为: 1、认知目标:掌握汇编语言程序设计的流程图和一般步骤,以及基本程序结构; 2、能力目标:能够独立编写一些简单的汇编语言程序,并进行仿真、调试、烧录直到最后达到实验目的。 3、情感目标:激发学生学习热情、发挥学生主观能动性、培养学生团队合作意识。 (设定以上目标意图:简而言之就是在学生学习理论知识的同时规范和提升学生的实训能力,为今后的职业道路打好基础。) 四、教学重点及难点 1.重点的确立: 根据教学大纲的要求,结合学生以后职业道路的需要,我确定本堂课的重点:程序流程图的画写和汇编语言程序设计的基本步骤及基本程序结构 2.教学难点的确立