项目二 LED显示系统设计
任务1 8路LED闪烁设计
一、教学目标
【能力目标】
(1)学会I/O接口基本电路设计。学会阅读程序流程图及分析程序。
(2)学会正确使用开发软件,掌握C51语句基本格式及部分语句功能。
【知识目标】
(1)掌握单片机LED驱动电路设计。掌握单片机P0~P3共4个I/O接口相关基础知识。(2)初步了解延时函数程序设计步骤。掌握数据类型、变量和常量使用方法。
(3)基本掌握include,while,sbit和for语句的具体应用。
二、工作任务
(1)从P1.0引脚输出方波信号,让LED实现周期闪烁,设晶振频率为12MHz。
(2)P1口控制8只LED(LED1~LED8),使8个指示灯实现周期闪烁。
三、任务实施
1.任务分解1
1)硬件电路及工作原理
硬件电路如图2-1-1所示,AT89C51是MCS—C51系列单片机中的一种,通常采用40引脚DIP封装,相关电路选择“上电复位电路”、“内部时钟电路”和“片内ROM”。从如图2-1-1所示可以得出,当P1.0引脚输出低电平时,则LED上有电流流过,LED发光,当P1.0引脚为高电平时,LED上无电流流过,LED熄灭。只要控制P1.0高电平/低电平的亮灭间隔时间,就能实现LED周期闪烁任务。程序流程图如图2-1-2所示。
2)源程序输入与调试
#include <REG52.h> //预处理命令,REG52.h定义了单片机的SFR sbit P1_0=P1^0; main( ) //主函数名 { unsigned int a; //定义变量a为无符号整形类型 while(1) //条件为真,无限循环“{……}”里的语句 { P1_0=0; //设P1.0 口为低电平,点亮LED for(a=0; a<50000; a++); //这是一个循环 P1_0=1; //设P1.0 口为高电平,熄灭LED for(a=0; a<50000; a++); //这是一个延时循环 } }
图2-1-1 单灯闪烁硬件电路图
图2-1-2 显示闪烁流程图
3)程序阅读
●C语言程序是从main()函数开始运行,因此,必须编写主函数main。
●#include <REG52.h> ,在“REG52.h”头文件中,存放了C51单片机的SFR功能定义,可以加载后直接使用。用“#include”关键字实现加载,结束处无“;”。
●sbit P1_0=P1^0;由于运算符“.”为结构运算符,所以不能直接使用P1.0。本程序中用一个位标量P1_0来代替P1.0,当然,也可以用其他标识符代替。位使用选择sbit关键字。
●P1_0=0;→点亮LED,P1_0=1;→熄灭LED。LED的点亮和熄灭与硬件电路设计有关,请注意电路设计。
●while(1);作为无限循环。要实现循环闪烁,必须考虑点亮→延时→熄灭→延时→点亮循环过程,while(表达式)语句实现循环。
●程序延时是通过语句“for(a=0; a<50000; a++);”重复执行实现。重复循环次数为变量a的取值范围0~50000。可设置变量类型为unsigned int,选择int或char类型将不符合范围要求。循环变量选择增量a++,因此a初始值为a=0,结束条件为a≥50000。同理,也可以选择增量a--,此时语句变为for(a=50000; a>0; a--);
2.任务分解2
1)硬件电路及工作原理
硬件电路设计如图2-1-3所示,选择了8路LED作为输出显示,P1.0~P1.7引脚直接驱动LED。当P1.0~P1.7引脚同时输出低电平时,则对应LED上有电流流过,LED发光;同时输出高电平时,LED熄灭。因此,只要控制P1口同时点亮和熄灭间隔时间,就能实现任务。任务分解2与任务分解1相仿,选择“上电复位电路”、“内部时钟电路”和“片内ROM”。任务中点亮和熄灭间隔时间有相同之处,可编写共用延时函数。程序流程图如图2-1-4所示。
图2-1-3 8路循环显示硬件电路图
图2-1-4 8路循环显示流程图
2)源程序输入与调试
#include <reg52.h> //预处理命令,装载C51单片机头文件 delay( ) //定义延时函数,无形式参数 { unsigned char i, j; //定义无符号整形变量i和j for(i=0; i<100; i++) //循环总次数为:i(=100)×j(=100)=10 000次 for(j=0; j<100; j++); } main( ) //主函数 { while(1) //无限循环 { P1=0x00; //点亮LED1~LED8 delay( ); //调用延时函数 P1=0xff; //熄灭LED1~LED8 delay( ); //调用延时函数 } }
3)程序阅读
●选择了8路LED作为输出显示。0x00,0xff是C语言十六进制数码,为8位,可以送单片机P1口。对应硬件电路,P1=0x00;P1=0xff;分别表示LED全亮和全灭。
●延时时间相同,可以编写延时函数delay( )。delay( )不带入口参数,即延时时间固定,不可调,选用两个变量i和j,采用双重循环,总的延时时间为(100×100×一次循环时间)。函数先定义后使用,因此不需要函数说明。delay( )函数设计了双重循环,本任务中用for语句实现延时循环。因变量取值范围为0~100,可选char或unsigned char。本任务选择unsigned char。
●其他程序分析和任务分解1相同。
四、相关知识
1.单片机I/O接口
40引脚单片机AT89C51有4个8位的并行I/O接口,记为P0、P1、P2和P3,共占用单片机的32个引脚。每个口可以整体作为I/O接口,也可以部分作为I/O接口。作为输入口使用时输入电流较大(可达20mA),作为输出口使用时输出电流较小(小于0.5mA),使用中应注意驱动电路的设计。
1)P0口和P2口
如图2-1-5和图2-1-6所示分别为P0口和P2口的电路图。由图可见,电路中包含一个数据输出锁存器和两个三态数据输入缓冲器,另外还有一个数据输出的驱动和控制电路。它们除了作为普通的I/O口,直接和外部设备进行数据传递外,还可以作为外部地址总线,P0口身兼两职,既可作为地址总线,也可作为数据总线。
图2-1-5 P0口锁存器和缓冲器结构
图2-1-6 P2口锁存器和缓冲器结构
在系统外部扩展数据存储器或程序存储器时,P2口可作为地址总线的高8位AD15~AD8,P0口作为地址总线的低8位AD7~AD0,由ALE 地址锁存器锁存低8位地址。
P0口在作为输出入口使用时,由于它无内部上拉电阻,为了在口线上输出高电平并具有一定的驱动能力,必须外接上拉电阻。作为输入口使用时,为了防止口锁存器对输入口线的输入信号造成影响,必须先往口锁存器写“1”(称为准双向口)。同理,P2口作为输入口时,也必须先往口锁存器写“1”,才能使用。
2)P1口
图2-1-7为P1口其中一位的电路图,P1口也为8位准双向口,每一位均可单独定义为输入口或输出口,当作为输入口时,“1”写入锁存器,T2截止,内部上拉电阻将电位拉至“1”,此时该口输出为“1”;当“0”写入锁存器,T2导通,输出则为“0”。作为输入口时,锁存器置“1”,T2截止,此时该位既可以把外部电路拉成低电平,也可由内部上拉电阻拉成高电平,正因为这个原因,所以P1口常称为准双向口。
3)P3口
P3口的电路如图2-1-8所示,P3口为准双向口,它除了有I/O接口第一功能外,还具有第二功能。为适应引脚的第二功能需要,增加了第二功能控制逻辑,在真正的应用电路中,第二功能显得更为重要。
图2-1-7 P1口锁存器和缓冲器结构
图2-1-8 P3口线逻辑电路图
P3口的第二功能各引脚定义如下:P3.0 串行输入口(RXD)
P3.1 串行输出口(TXD)
P3.2 外中断0()
P3.3 外中断1()
P3.4 定时/计数器0的外部输入口(T0)
P3.5 定时/计数器1的外部输入口(T1)
P3.6 外部数据存储器写选通()
P3.7 外部数据存储器读选通()
2.单片机C语言词汇
随着单片机技术的不断发展,以单片机C语言为主流的高级语言也不断被更多的单片机爱好者和工程师所喜爱。学习单片机C语言,一定要养成一个良好的习惯,这样在完成一个项目后,既可以方便别人阅读,也可为自己的工作做一个良好而又完整的记录。
C51是由C语言产生的,因此与C语言有着完全相同的语法规则,针对不同的CPU,有着不同的编译环境和不同的数据存储结构,所以在语言表达上有它自身的特点。
在C语言中使用的词汇分为6类:标识符,关键字,运算符,分隔符,常量和注释符等。
(1)标识符
标识符是用来标识源程序中某个对象的名字的,这些对象可以是语句、数据类型、函数、变量、数组等,即标识符就是名称。
作为标识符必须满足以下规则:
●标识符由字母、数字(0~9)和下画线等组成。
●第一个字符必须是字母或下画线。
●标识符在命名时应当简单,含义清晰,做到“顾名思义”。
●标识符不能使用C51的关键字。
注意:C语言区分大小写。大小写不同字母代表不同的标识符。如M和m代表不同变量。
(2)关键字
关键字则是编程语言保留的专用特殊标识符,它们具有固定名称和含义,在程序编写中不允许标识符与关键字相同。在标准C语言中基本的数据类型char,int,short,long,float和double就是关键字。
(3)运算符
C语言中含有相当丰富的运算符。运算符与变量、函数一起组成表达式,表示各种运算功能。运算符由一个或多个字符组成。
(4)分隔符
在C语言中采用的分隔符有逗号和空格两种。逗号主要用在类型说明和函数参数表中,分隔各个变量。空格多用于语句各单词之间,作间隔符。在关键字,标识符之间必须要有一个以上的空格符作间隔,否则将会出现语法错误,例如,把“int a;”写成“inta;”,C编译器会把inta当成一个标识符处理,其结果必然出错。
(5)常量
C语言中使用的常量可分为数字常量、字符常量、字符串常量、符号常量、转义字符等多种。
(6)注释符
C51有两种注释方式:
●以“/*”开头并以“*/”结束,对一段内容进行注释。不允许嵌套“/*”和“*/”。
●在C51中增加了“//”对一行后面的内容进行注释。
程序编译时,不对注释作任何处理。因此,注释可出现在程序中的任何位置。
(7)书写程序时应遵循的规则
从书写清晰,便于阅读、理解和维护的角度出发,在书写程序时应遵循以下规则:
●一个说明或一个语句占一行。语句一般以“;”结尾。
●用{}括起来的部分,通常表示了程序的某一层次结构。{}一般与该结构语句的第一个字母对齐,并单独占一行。
●低一层次的语句或说明可比高一层次的语句或说明缩进若干格后书写,以便看起来更加清晰,增加程序的可读性。
在编程时应力求遵循这些规则,以养成良好的编程风格。
3.单片机C语言的数据
无论我们学习哪一种语言,首先遇到的是数据类型,C51共有以下几种数据类型,如图2-1-9所示。
图2-1-9 C51的数据类型
单片机中,基本类型使用较多,其他数据类型在后面介绍。KEIL C51所支持的基本数据说型说明见表2-1-1。
表2-1-1 C51所支持的基本数据说型
(1)bit位标量。
bit位标量是C51编译器的一种扩充数据类型,利用它可定义一个位标量,但不能定义位指针,也不能定义位数组。它的值是一个二进制位,不是“0”就是“1”。
(2)char字符类型。
char类型的长度是一个字节,通常用于定义处理字符数据的变量或常量。分无符号字符类型“unsigned char”和有符号字符类型“signed char”,默认值为“signed char”类型(通常用char表示)。“unsigned char”类型用字节中所有的位来表示数值,可以表达的数值范围是0~255。“signed char”类型用字节中最高位表示数据的符号,“0”表示正数,“1”表示负数,负数用补码表示。“unsigned char”常用于处理ASCII字符或用于处理小于或等于255的整型数。
(3)int整型。
int整型长度为两个字节,用于存放一个双字节数据。分有符号整型数“signed int”和无符号整型数“unsigned int”,默认值为“signed int”类型(通常用int表示)。符号表示同“signed char”。
(4)long长整型。
long长整型长度为四个字节,用于存放一个四字节数据。分有符号长整型“signed long”和无符号长整型“unsigned long”,默认值为“signed long”类型。
(5)float和double浮点型。
因浮点数的结构较复杂,需要字节数较多,C51中使用不多,可参考其他资料。
(6)指针型。
指针型本身就是一个变量,在这个变量中存放的指向另一个数据的地址。这个指针变量要占据一定的内存单元,对不同的处理器长度也不尽相同,在C51中它的长度一般为1~3个字节。
●在编程时应按照变量可能的取值范围、精度要求去选择恰当的数据类型。这样不仅节省了存储空间,而且还可以提高程序的运行速度。
●如果不涉及负数运算,尽量采用无符号类型,这样可提高编译后目标代码的效率。
4.C语言的常量和变量
对于基本数据类型量,按其取值是否允许变化可分为常量和变量两种。在程序执行过程中,其值不发生改变的量称为常量,其值可变的量称为变量。它们可与数据类型结合起来分类,可分为“整型常量”、“整型变量”、“浮点常量”、“浮点变量”、“字符常量”、“字符变量”、“枚举常量”和“枚举变量”等。在程序中,常量是可以不经说明而直接引用的,而变量则必须先定义后使用。
1)常量和符号常量
在程序执行过程中,其值不发生改变的量称为常量。可分为“直接常量”、“标识符常量”和“符号常量”。
(1)直接常量(字面常量)。
直接常量又可分为整型常量、实型常量和字符常量。
整型常量:12,0,-3;
实型常量:4.6,-1.23;
字符常量:‘a’,‘b’。
(2)标识符常量。
用来标识变量名、符号常量名、函数名、数组名、类型名和文件名的有效字符序列。
(3)符号常量。
用标识符代表一个常量。在C语言中,可以用一个标识符来表示一个常量,称为符号常量。符号常量在使用之前必须先定义,其一般形式为
#define 标识符 常量
一经定义,以后在程序中所有出现该标识符的地方均代之以该常量值。
注意:习惯上符号常量的标识符用大写字母,变量标识符用小写字母,以示区别。
【例2-1-1】 符号常量的使用。
#define PRICE 30 /* 定义常量 */ main() { int num, total; /*定义两个有符号整型变量*/ num=10; /*赋值语句*/ total=num* PRICE; /* total=*/ while(1); }
2)常量的数据类型
(1)整型常量可以表示为十进制如123,0,-89等。十六进制则以0x开头如0x34,-0x3B等。长整型就在数字后面加字母L,如104L,034L,0xF340L等。
长整数158L和基本整常数158 在数值上并无区别。但对158L,因为是长整型量,C编译系统将为它分配4个字节存储空间。而对158,因为是基本整型,只分配2个字节的存储空间。因此在运算和输出格式上要予以注意,避免出错。
无符号数也可用后缀表示,整型常数的无符号数的后缀为“U”或“u”。如358u,0x38Au,235Lu均为无符号数。
前缀、后缀可同时使用以表示各种类型的数。如0XA5Lu表示十六进制无符号长整数A5,其十进制为165。
(2)浮点型常量可分为定点数十进制和指数表示形式。十进制由数字和小数点组成,如0.888,3345.345,0.0等。指数表示形式为[±]数字[.数字]e[±]数字,[ ]中的内容为可选项,其中内容根据具体情况可有可无,但其余部分必须有,如125e3,7e9,-3.0e-3。
(3)字符型常量是单引号内的字符,如‘a’、‘,’、‘d’等。不可以显示的控制字符,可以在该字符前面加一个反斜杠“\”组成专用转义字符。常用转义字符表参见表2-1-2。转义字符主要用来表示用一般字符不便于表示的控制代码。
表2-1-2 常用转义字符表
(4)字符串型常量由双引号内的字符组成,如“test”,“OK”等。当引号内的没有字符时,为空字符串。在使用特殊字符时同样要使用转义字符如双引号。在C中字符串常量是作为字符类型数组来处理的,在存储字符串时系统会在字符串尾部加上“\o”转义字符以作为该字符串的结束符。字符串常量“A”和字符常量‘A’是不同的,前者在存储时多占用一个字节的空间。
(5)位标量。它的值是一个二进制。
3)变量及变量定义
其值可以改变的量称为变量。一个变量应该有一个名字,在内存中占据一定的存储单元。变量定义必须放在变量使用之前。一般放在函数体的开头部分。
注意:要区分变量名、变量值和变量地址3个不同的概念。例如:
变量定义的一般形式为
类型说明符 变量名标识符,变量名标识符,...;
例如:
int a, b, c; //a,b,c为有符号整型变量 long x, y; //x,y为有符号长整型变量 unsigned int p, q; //p,q为无符号整型变量
在书写变量定义时,应注意以下几点:
●允许在一个类型说明符后,定义多个相同类型的变量。各变量名之间用逗号“,”间隔。类型说明符与变量名之间至少用一个空格间隔。
●最后一个变量名之后必须以分号(;)结尾。
●变量定义必须放在变量使用之前。一般放在函数体的开头部分。
●变量必须先定义后使用。
4)变量赋初始值
在程序中常需要对变量赋初值,以便使用变量。在作变量定义的同时给变量赋予初值的方法称为初始化(可以不赋)。在变量定义中赋初值的一般形式为
类型说明符 变量1=值1,变量2=值2,……;
例如:
int a=3; int b, c=5; float x=3.2, y=3f, z=0.75; char ch1='K', ch2='P';
注意:在定义中不允许连续赋值,如a=b=c=5是不合法的。
5)各类数值型数据之间的混合运算
变量的数据类型是可以转换的。转换的方法有两种,一种是自动转换,一种是强制转换。自动转换发生在不同数据类型的量混合运算时,由编译系统自动完成。
(1)自动转换规则。
●若参与运算量的类型不同,则先转换成同一类型,然后进行运算。
●转换按数据长度增加的方向进行,以保证精度不降低。如int型和long型运算时,先把int量转成long型后再进行运算。
●所有的浮点运算都是以双精度进行的,即使仅含float单精度量运算的表达式,也要先转换成double型,再作运算。
●在赋值运算中,赋值号两边量的数据类型不同时,赋值号右边量的类型将转换为左边量的类型。如果右边量的数据类型长度比左边长时,将丢失一部分数据,这样会降低精度,丢失的部分按四舍五入向前舍入。
下面表示了类型自动转换的规则:
bit--→char--→short--→int--→unsigned--→long--→double
(2)强制类型转换。
强制类型转换是通过类型转换运算来实现的。一般形式为
(类型说明符)(表达式)
功能是把表达式的运算结果强制转换成类型说明符所表示的类型。例如:
(float)a //把a转换为实型 (int)(x+y) //把x+y的结果转换为整型
在使用强制转换时应注意类型说明符和表达式都必须加括号(单个变量可以不加括号),如把(int)(x+y)写成(int)x+y则成了把x转换成int型之后再与y相加了。
无论是强制转换或是自动转换,都只是为了本次运算的需要而对变量的数据长度进行的临时性转换,而不改变数据说明时对该变量定义的类型。
注意:区别表达式和语句。
6)初步了解函数
用汇编语言或C语言编写的程序称为源程序。C语言源程序是由函数组成的。函数是C源程序的基本模块,通过对函数模块的调用,实现特定的功能。函数从用户角度上分为标准函数(库函数)和用户自定义函数。程序员的任务就是编写一系列的函数模块,并在适当的时候调用这些函数模块,完成程序功能。
(1)标准函数(库函数)。
C51运行库提供了100多个预定义函数和宏,用户可以在自已的C语言源程序中使用这些函数和宏。标准函数已由编译器软件商编写定义,使用者直接调用就行了,而无须定义。这些函数通常又称为内部函数,存放在不同的头文件“*.h”中,使用中以
#include<*.h>
或
#include“*.h”
加载后才能使用。
注意:结尾无“;”。
(2)main()函数。
Main()函数为程序的主函数,其他若干个函数可以理解为是一些子程序。程序中必须有且只能有一个名为main()的主函数,C语言源程序总是从main()函数开始执行。
(3)用户自定义函数。
用户自定义函数使用主要区别函数定义、函数说明和函数调用3个方面。
A.函数定义。
标准函数不足以满足使用者的特殊要求,因此C语言允许使用者根据需要编写特定功能的函数,要调用它必须要先对其进行定义才能使用。定义的模式为
函数类型 函数名称(形式参数表) //此处无“;” { 函数体 }
【例2-1-2】 定义一个延时函数。
void delay(unsigned int t) //定义延时函数,无返回值 { unsigned int i,j; //定义无符号整形变量i和j for(i=0;i<t; i++) for(j=0;j<t; j++); }
函数定义说明:
●函数类型是说明所定义函数返回值的类型。
●函数名符合标识符命名格式。
●形式参数是指调用函数时要传入函数体内参与运算的变量,它能有一个、几个或没有,当不需要形式参数也就是无参函数,括号内为空或写入“void”表示。
●函数体中能包含有局部变量的定义和程序语句,如函数要返回运算值则要使用 return语句进行返回。在函数的【】号中也能什么都不写,这就成了空函数,在一个程序项目中写一些空函数,在以后的修改和升级中能方便地在这些空函数中进行功能扩充。
B.函数说明。
没有函数体的函数定义称为函数说明。一般形式为
函数类型 函数名(形参类型 [形参名],….. ); //此处有“;”
或
函数类型 函数名();
注意:告诉编译系统函数类型、参数个数及类型,以便检验。
C语言规定,在以下两种情况下,可以省去对被调用函数的说明。
●当被调用函数的函数定义出现在调用函数之前时:
●如果在所有函数定义之前,在函数外部(如文件开始处)预先对各个函数进行了说明,则在调用函数中可默认对被调用函数的说明。
C.函数的调用。
在程序中,是通过对函数的调用来执行函数体的,其过程与其他语言的子程序调用相似。C语言中,函数调用的一般形式为
函数名([实际参数表])
注意:实际参数的个数、类型和顺序,应该与被调用函数所要求的参数个数、类型和顺序一致,才能正确地进行数据传递。例如:
delay(1000); //调用延时函数
7)语句
C51语句可分为简单语句和复合语句两种。简单语句以“;”结束,可分为说明语句和执行语句。
(1)简单语句。
A.说明语句。
说明语句用来说明变量的类型和初值。要求先定义后使用。例如:
int sum=0; //说明一个有符号整型变量sum,其初始值为0,以“;”结束
B.执行语句。
执行语句用来完成一定的功能。例如:
sum=0; //将“0”赋值给变量sum
(2)复合语句。
复合语句是使用一对花括号“{”和“}”用于将若干条简单语句组合在一起形成一种功能块,使它们在语法上等价于一个简单语句,称为复合语句。程序中应把复合语句看成是一条简单语句,而不是多条语句。复合语句之间用“{}”分隔,它内部的各条语句还是以分号“;”结束。复合语句可以嵌套使用。例如:
{ for(a=0; a<50000; a++); P1_0=0; for(a=0; a<50000; a++); P1_0=1; }
8)任务所用语句
注意:C语言区分大小写,使用中请细心体会。
(1)赋值运算符“=”。
赋值语句其一般形式为
变量=表达式;
功能:将右边表达式值赋值给左边变量。以分号“;”结束。赋值运算符具有右结合性。例如,
P1_0=0; //C51特殊功能寄存器均为大写
(2)#include功能。
预处理命令,用于编译连接使用。一般形式为
#include<库函数>或#include'库函数' //此处无“;”
功能:加载预先编好的库函数、头文件。
注意:不是真正语句,结束无“;”。
#include<REG51.H>
(3)sbit语句。
功能:位标量名的替换。例如:
sbit P1_0=P1^0; //将P1_0替换P1口的第0位,即P1.0
注意:区分大小写。
(4)while循环语句。
while语句为循环语句,先判断后运行。while语句的一般形式为
while(条件表达式) 语句;
其中表达式是循环条件,语句为循环体。执行过程可用图2-1-10表示。
while语句的语义是:先计算条件表达式的值,当值为真(非0)时执行循环体语句,为假时跳出循环。
图2-1-10 while语句工作流程图
使用while语句应注意以下几点:
●while语句中的表达式一般是关系表达式或逻辑表达式,只要表达式的值为真(非0)即可继续循环。
●循环体如包括有一个以上的语句,则必须用“{}”括起来,组成复合语句。如果不加大括号,则while语句的范围只到while后面的第一个“;”处。例如:
while(1); //条件为“1”==真,无限原地循环 while(1) {… ;} //条件为“1”,无限循环{ }里的语句
(5)自增和自减语句。
C语言有两个很有用的运算符——自增“++”和自减“--”。
运算符“++”是操作数自加1,“--”是操作数自减1。换句话说:“a=a+1”等同于“a++”,“a=a-1”等同于“a--”。其他表示方式见后面部分。
(6)for循环语句。
在C语言中,for语句使用最为灵活,它完全可以取代 while 语句。它的一般形式为
for(表达式1;表达式2;表达式3) 语句;
for语句最简单的应用形式也是最容易理解的形式为
for(循环变量赋初值;循环条件;循环变量增/减量) 语句;
循环变量赋初值总是一个赋值语句,它用来给循环控制变量赋初值;循环条件是一个关系表达式,它决定什么时候退出循环;循环变量增量,定义循环控制变量每循环一次后按什么方式变化。这3个部分之间用“;”分开。例如:
for(i=1, sum=0; i<=100; i++) sum=sum+i; P1_0=0;
for语句的执行过程如下:
●第一步:先求解表达式1(赋变量初始值i=1,sum=0)。
●第二步:求解表达式2,若其值为真,则执行for语句中指定的内嵌语句,然后执行下面第三步;若其值为假,则结束循环,转到第五步(i=1,即i≤100,条件为真,执行“sum=sum+i;”语句;直到i累计到i=101,i>100,条件为假,结束循环,转到第五步,执行“P1_0=0;”语句)。
●第三步:求解表达式3(执行“i++;”语句)。
●第四步:转回上面第2步继续执行(条件判断)。
●第五步:循环结束,执行for语句下面的一个语句(执行“P1_0=0;”语句)。其执行过程可用图2-1-11表示。
图2-1-11 for语句执行过程工作流程图
注意:
·for循环中的“表达式1(循环变量赋初值)”、“表达式2(循环条件)”和“表达式3(循环变量增量)”都是可选择项,即可以默认,但“;”不能默认。
·省略了“表达式1(循环变量赋初值)”,表示不对循环控制变量赋初值。
·省略了“表达式2(循环条件)”,则不做其他处理时便成为死循环。
·省略了“表达式3(循环变量增量)”,则不对循环控制变量进行操作,这时可在语句体中加入修改循环控制变量的语句。
·3个表达式都可以省略。相当于:while(1)语句。
【例2-1-3】 for语句循环的嵌套(延时程序设计)。
delay( ) //定义延时函数 { unsigned int i, j; //定义无符号整形变量i和j,注意“;”的位置 for(i=0; i<100; i++) for(j=0; j<100; j++); //注意“;”的位置 }
共执行i×j=100×100次延时时间,延时时间通过软件调试得出。
(7)do-while语句。
do-while语句的一般形式为
do 语句; while(表达式);
这个循环与while循环的不同在于:它先执行循环中的语句,然后再判断表达式是否为真,如果为真则继续循环;如果为假,则终止循环。因此,do-while循环至少要执行一次循环语句。其执行过程可用图2-1-12表示。
图2-1-12 do-while语句工作流程图
同样当有许多语句参加循环时,要用“{”和“}”把它们括起来。
五、实践练习
(1)P1口控制8只LED(LED1~LED8),使8个指示灯同时点亮或熄灭,实现周期可调的闪烁。显示周期自定。