请读者阅读图2-4参考程序,目标是实现8个发光二极管全部点亮。
图2-48 个发光二极管全部点亮参考程序(未编译通过)
2.2.1.1 程序解析
1.头文件
前面介绍过include。include是预处理命令之一,表示文件包含,是指将一个源文件的全部内容包含到另外一个文件中去,成为其中一部分。假如学校要某班的部分同学去参加活动,我们必须先指定这个班级,比如“电气2班”。类似地,我们可以这样表述:include“电气2班”,将“电气2班”的所有同学信息包含进来,之后活动才可以方便调用这个班的学生,比如让班长去跑步,让学习委员去发放资料等。同理,单片机内部包含了许多不同的功能部件,我们要使用单片机,必须把包含这个单片机相关功能部件的有关信息包含进来,之后才能方便地使用相应的功能部件。
那么头文件到底包含了什么呢?我们可以把鼠标放在<reg51.h>的中间,然后单击右键就会出现如图2-5所示的对话框。然后选择Open document<reg51.h>,就会出现如图2-6所示的内容。通过上一章的学习我们知道,单片机内部有很多个特殊功能寄存器,每个特殊功能寄存器对应一个地址。我们想要使用单片机里的特殊功能寄存器时就要先定义它,也就是头文件里包含的内容。所以只要你想要使用单片机里的特殊功能寄存器时就要把这个头文件包含进来。
图2-5 打开reg51.h的文件
图2-6 头文件里的部分内容
reg51.h头文件包含了传统8051片内资源的相关的特殊功能寄存器,因此用户只要包含了这个头文件,即可自由使用这些片内资源。好比活动主办方包含了“电气2班”,则该班的任何一个同学都可以被“使用”。但某活动如果只要班长参与,则完全可以直接声明该班的班长,而不需要获取带有该班全部信息的“电气2班”。同理,本章我们暂时只用到P0口,查看reg51.h头文件可知,相关定义是“sfr P0=0x80”。我们可以不包含头文件(即include<reg51.h>),而直接使用sfr P0=0x80。需要特别强调的是:既然头文件已经为读者实现了相关特殊功能寄存器的定义,建议读者简单包含头文件为好,不要使用一个自己定义一次。
2.sbit:声明一个可位寻址变量
在刚刚打开的头文件里我们发现里面是没有定义P0口的每个端口的地址,这就说明P0.0~P0.7是没有定义的。C语言规定每个字符在使用前都必须定义,所以我们要对P0.0~P2.7进行定义。P0.0~P0.7就是特殊功能寄存器P0口的某一个位,所以我们定义时就用sbit。bit表示某一个位,sbit表示要定义的对象是特殊功能寄存器。因此,要注意sbit与bit的区别。
动一动
请读者查阅网络资料,回答bit和sbit的区别。
3.变量名:LED0~LED7
在程序中使用的变量名、函数名、标号等统称为标识符。除库函数的函数名由系统定义外,用户函数的函数名由用户自定义。C语言规定,标识符只能是字母(A~Z,a~z)、数字(0~9)、下划线()组成的字符串,并且其第一个字符必须是字母或下划线,即不允许以数字开头!
所以程序里的“LED0~LED7”也可以命名为其他,只要它们都符合上面的命名规则就可以。用户如果觉得喜欢或很有必要,可以定义为miantiao(面条的拼音)。但事实上,作为一个规范的、合格的编程者,在定义用户“标识符”(名字)时,一般都得做到“顾名思义”!LED三个字母,就做到了顾名思义,读者一眼就知道这是一个LED指示灯,但miantiao则会让人想到这是“面条”。
总结:标识符的定义必须做到“顾名思义”,但是一旦定义或声明后,其大小写就确定了,用户在使用时就不能任意修改。
想一想
请读者认真阅读图2-4的源代码,你认为它能编译通过吗?若不行,请将错误提示语句摘录出来,并找出其中的错误并加以纠正。
动一动
请判断以下标识符哪些是合法的,并给出解释。1)a;2)book;3)BOOK;4)1abc;5)s@d。
4.main函数
通过上一章的学习我们已经知道了main函数的基本含义了。
➢main函数基本格式是
➢main函数有无参数和返回值?
➢任何一个C程序可以有多少个main函数?
5.while语句
格式:(www.xing528.com)
执行过程:先判断表达式里的值是否为真(请注意,在逻辑中,非0表示真,0表示假),如果表达式的值为真,那么执行它下面对应大括号里的循环体语句;如果表达式的值为0,则跳出while语句,执行大括号后的语句。while(1)中的1为真,因此while(1)实际上就是一个永远成立的循环,称为死循环,单片机在不断执行while(1)下面对应大括号里的循环体语句。
想一想
如果把while(1)中的“1”换成“2”还会是死循环吗?
注意while(表达式)的括号后是没有分号的。思考:while(表达式)的括号后加分号的结果是怎样的,为什么?提示:C语言以分号作为语句结束标志。
2.2.1.2 编程规范
1.缩进——秩序的重要性
请读者观察图2-7所示的程序段,说说它们有何不同?如果你是老师,你会给哪个图
点赞,而去批评另外的哪一个图?请说出你的想法。
图2-7 大括号匹配示意图
事实上,图2-7a、b所示的程序段内容完全一样,它们的编译结果也是一样的,也就是说,编译器是不会检查你的程序是否美观、规范的,它在乎你是否有语法错误。那么,是否没有错误就满足了呢?良好的编程习惯,是成为一名合格程序员的基本素质。在这里,强烈建议,甚至是要求读者:必须注意编程规范的养成!
使用键盘上【Tab】键实现缩进,体现层次关系(注意合理设置Tab键实现的空格数)。
大括号必须清晰匹配,被大括号包围起来的内容就属于大括号开始出的那个语句的内容!
2.注释——并非为了美观
以两个斜杠“//”开始的,只能注释一行。以“/*”开始,以“*/”结尾的,可以注释任意多行。图2-4使用了两个斜杠的注释方式,每次对当前行前面的语句进行注释。
为什么要注释呢?简单说,注释的好处是:理解+备忘。进行合理、准确的注释可以帮助读者,甚至是程序员自己理解程序,同时更能帮助程序员在一定时间后唤醒当时的编程意图等。所以,作为初学者,请务必养成习惯——给你写的源程序进行合理的注释。
特别强调:注释对编译器而言是“空气”,完全被忽略的,从这个意义上,注释时读者可以自由发挥,想怎么写就怎么写,但一般地,注释必须做到注释的初衷——方便理解与记忆!
2.2.1.3 字节操作
前面使用位操作实现了8个指示灯的全部点亮,但其存在的不足是:当指示灯个数很多时,会使代码显得很冗长。有没有别的办法呢?事实上,我们可以使用字节操作,一次性实现对8个指示灯的全部控制。
传统的8051单片机有四个并行口,分别是P0~P3,每个并口有8个位,分别是Px.0~Px.7(其中x代表0~3)。对应地,我们可以设想一个班级分成4组,每组有8名同学,当要安排某个组执行任务时(比如起立),我们可以一个一个地呼叫这个组的8名同学起立(这样必须呼叫8次),我们也可以直接称呼某某组的同学起立(这样只需要呼叫1次就可以了)。
以P0口为例,它有8个位,用户可以直接对整个P0口直接操作——字节操作,一次性处理8个位,用户也可以对P0口的一个或多个位进行单独操作——位操作,也称为“原子操作”。如图2-8所示,其中MSB代表最高位,LSB代表最低位。我们可以认为P0口是班级中的一个小组,这个小组有8个成员,字节操作就是对整个组进行操作,位操作是直接对这个组的某个成员进行操作。从这个意义上说,字节操作“牵一发动全身”,而位操作则“各自为政,互不干扰”。
图2-8 P0口
明显地,图2-4所示的源程序属于位操作,将P0.0~P0.7这8个位独立进行操作,对要点亮发光二极管而言,要对这8个位分别写0(低电平),对应表2-1。
表2-1 P0口位状态
因此,要点亮P0口所接的8个LED指示灯,我们可以使用位操作,8个灯分别点亮,如图2-9所示的方法1。同样,我们也可以直接对P0口直接操作,如图2-9所示的方法2。特别说明:请读者在编译时,只编译方法1或方法2,即将另外一种方法相关的语句“注释掉”!
图2-9 位操作与字节操作
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。