3.3 实例程序解析
下面我们将对实例3-1的程序进行详细说明。
3.3.1 reg52.h及其他头文件
在代码中引用头文件,其实际意义就是将头文件中的全部内容放到引用头文件的位置处,免去每次编写同类程序都要将头文件的语句重复填写。将鼠标移到reg52.h上,单击右键,选择Open document <reg52.h>选项,即可打开该头文件,如图3-39和3-40所示。以后要打开工程中的其他头文件,也可采用这种方式。
图3-39 打开头文件的方法
图3-40 头文件的内容
其全部内容如下:
从上面代码可以看出,该头文件定义了52单片机内部所有的功能寄存器,用到了sfr和sbit两个关键字,如“sfr P0=0x80;”语句的意义是,把单片机内部地址0x80处的寄存器重新起名叫P0,就相当于对单片机内部的0x80地址处的寄存器进行操作。也就是通过sfr这个关键字,让Keil编译器在单片机与人之间搭建一条可以进行沟通的桥梁。用户操作的是P0口,而单片机不知道什么是P0口,但它知道P0口的内部地址0x80。以后凡是编写51内核单片机程序时,在源代码的第一行就可直接包含该头文件。
在程序中还可以看到,“sbit CY=PSW^7;”语句的意思是,将PSW这个寄存器的最高位,重新命名为CY,以后要单独操作PSW寄存器的最高位时,便可直接操作CY,其他类同。
C51通常还有reg51.h、math.h、intrins.h、absacc.h、stdio.h、stdlib.h、ctype.h等头文件。其中reg51.h和reg52.h头文件一样,都是定义特殊功能寄存器和位寄存器的,它们中大部分内容是一样的,52单片机比51单片机多一个定时器T2,因此,reg52.h中就比reg51.h中多了几行定义T2寄存器的内容。math.h是定义数学运算的,求方根、正余弦、绝对值等,该头文件中包含各种数学运算函数,当需要使用时可以直接调用它的内部函数。intrins.h是固有函数头文件。absacc.h可以访问特殊功能寄存器。stdio.h是动态内存分配函数头文件。stdlib.h是标准库文件函数头文件。
3.3.2 C语言中注释的写法
下面再回到编辑界面,能看到紧接着头文件后面有注释“//……”。
在C语言中,注释有两种写法:
1)//......,两个斜杠后面跟着的为注释语句。这种写法只能注释一行,当换行时,又必须在新行上重新写两个斜扛。
2)/∗. . .∗/,斜扛与星号结合使用,这种写法可以注释任意行,即斜扛星号与星号斜扛之间的所有文字都作为注释。
所有注释都不参与程序编译,编译器在编译过程会自动删去注释,注释的目的是为了读程序方便,一般在编写较大的程序时,分段加入注释,这样回过头来再次读程序时,因为有了注释,其代码的意义便一目了然了。若无注释,我们将会特别费力地将程序重新阅读一遍方可知道代码含义。养成良好的书写代码格式的习惯,经常为自己编写的代码加入注释,以后定能方便许多。
如:
3.3.3 main()主函数的写法
程序中的核心内容主函数main(),无论一个单片机程序有多大或多小,所有的单片机在运行程序时,总是从主函数开始运行,关于主函数的写法,下面简要介绍。
格式:void main()
注意:后面没有分号。
特点:无返回值,无参数。
无返回值表示该函数执行完后不返回任何值,上面main前面的void表示“空”,即不返回值的意思,后面会讲到有返回值的函数,到时大家一对比便会更加明白。
无参数表示该函数不带任何参数,即main后面的括号中没有任何参数,只写“()”就可以了,也可以在括号里写上void,表示“空”的意思,如void main(void)。
任何一个单片机C程序有且仅有一个main函数,它是整个程序开始执行的入口。大家注意看,在写完main()之后,在下面有两个花括号,这是C语言中函数写法的基本要求之一,即在一个函数中,所有的代码都写在这个函数的两个大括号内,每条语句结束后都要加上分号,语句与语言之间可以用空格或〈Enter〉键隔开。
例如:
接下来看到以下语句:
以上语句是该程序中最核心的部分,其中:
表示循环执行大括号里边的语句。
下面看下两句:
temp=0x01的含义是将temp赋值为0000 0001,P2=~temp的含义是将temp逐位取反后把值赋P2口。在数字电路中,电平只有两种状态:高电平:1;低电平:0。显然,当执行for(i=0;i<8;i++)语句的第一轮i第一次赋值i=0时,该语句的意思是,P2=11111110,即让P2口的最低位清0。由于没有操作其他口,所以其余口均保持原来状态不变。假设P2口对应的八个引脚均连接发光二极管,那么当P2口的最低位清0时,板上的第一个发光二极管就会亮。
temp<<=1的含义是将temp左移1位,当执行for(i=0;i<8;i++)语句的第一轮i第二次赋值i=1时,temp=00000010,P2=11111101,板上的第二个发光二极管被点亮。当执行for(i=0;i<8;i++)语句的第一轮i第三次赋值i=2时,temp=00000100,P2=11111011,板上的第三个发光二极管被点亮。当执行for(i=0;i<8;i++)语句的第一轮i第三次赋值i=3时,temp=00001000,P2=11110111,板上的第四个发光二极管亮。……当执行for(i=0;i<8;i++)语句的第一轮i第七次赋值i=7时,temp=10000000,P2=01111111,板上的第八个发光二极管被点亮。此时i<8,for(i=0;i<8;i++)语句的第一轮循环结束,完成了一次八个发光二极管的循环点亮。
图3-1电路中,除单片机外,还有电阻和发光二极管。发光二极管具有单向导电性,通过5mA左右电流即可发光,电流越大,其亮度越强,但若电流过大,会烧毁二极管,一般控制在3~20mA之间。在这里,给发光二极管串联一个电阻的目的就是为了限制通过发光二极管的电流不要太大,因此这个电阻又称为“限流电阻”。当发光二极管发光时,测量它两端电压约为1.7V,这个电压又称为发光二极管的“导通压降”。发光二极管正极又称阳极,负极又称阴极,电流只能从阳极流向阴极。关于电阻大小的选择:根据欧姆定律,当发光二极管正常导通时,其两端电压约为1.7V。发光二极管的阳极为VCC,阴极串接一电阻,电阻的另一端为低电平,即0V。本例中,电阻选择1kΩ,其电流为(5-1.7)V/1kΩ=3.3mA。
【例3-2】编写一个完整的使点亮第8个发光二极管的程序。