基于Proteus的AVR单片机C语言程序设计与仿真
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

3.2 流程控制

C语言是一种结构化编程语言,用户可采用结构化方式编写相关源程序。采用结构化设计的程序具有结构清晰、层次分明、易于阅读修改和维护等特点。

结构化程序由若干个模块组成,每个模块中包含若干个基本结构,而每个基本结构中可有若干条语句。在C语言中,有3种基本结构:顺序结构、选择结构和循环结构。

1)顺序结构顺序结构是一种最基本、最简单的编程结构。在这种结构中,程序由低地址向高地址顺序执行指令代码。如图3-2所示,程序要先执行A,然后再执行B,两者是顺序执行的关系。

图3-2 顺序结构

2)选择结构选择结构是先对给定的条件进行判断,再根据判断的结果决定执行哪一个分支。如图3-3所示,图中的P代表一个条件,当P条件成立(或称为“真”)时,执行A,否则执行B。注意,只能执行A或B之一,两条路径汇合在一起,然后从一个出口退出。

图3-3 选择结构

3)循环结构循环结构是指在给定条件成立时,反复执行某段程序。在C语言中,循环结构又分成“当”(while)型循环结构和“直到”(do while)型循环结构。

“while”型循环结构如图3-4(a)所示。当P条件成立(或称为“真”)时,反复执行A操作;直到P为“假”时,才停止循环。

“do while”直到型循环结构如图3-4(b)所示。先执行A操作,再判断P是否为“假”,若P为“假”,再执行A,如此反复,直到P为“真”为止。

图3-4 循环结构

1.条件语句与控制结构

编程解决实际问题时,通常需要先根据某些条件进行判断,然后决定执行哪些语句,这就是条件选择语句。在C语言中提供了3种形式的if条件选择语句和switch多分支选择语句。

1)if语句

(1)if语句的结构形式:if语句是C语言中的一个基本判断语句,它的3种结构形式语句如下所示。

①形式一:

            if(表达式)
            {语句};

在这种结构形式中,如果括号中的表达式成立,则程序执行“{}”中的语句;否则程序将跳过“{}”中的语句部分,顺序执行其他语句。例如:

            if  (PINB.0==0)
            {                       //如果PB.0端口为低电平,则执行下述语句
            PORTB.4=~PORTB.4;        //PB.4端口输出相反的状态
            PORTB.5=0;               //PB.5端口输出为低电平
            }

②形式二:

            if(表达式)
            {语句1;}
            else
              {语句2;}

在这种结构形式中,如果括号中的表达式成立,则程序执行“{语句1;}”中的语句;否则程序执行“{语句2;}”中的语句。例如:

            if  (PINB.0==0)
            {                       //如果PB0端口为低电平,则执行下述语句
            PORTB.4=~PORTB.4;        //PB4端口输出相反的状态
            PORTB.5=0;               //PB5端口输出为低电平
            }
                else
            {                       //如果PB0端口不是低电平,则执行下述语句
            PORTB.7=~PORTB.7;        //PB7端口输出相反的状态
            PORTB.5=1;               //PB5端口输出为高电平
            }

③形式三:

            if(表达式1)
            {语句1;}
            else if(表达式2)
              {语句2;}
            else if(表达式3)
              {语句3;}
            ……
            else if(表达式m)
              {语句m;}
            else
              {语句n;}

在这种结构形式中,如果括号中的表达式1成立,则程序先执行“{语句1;}”中的语句,然后退出if选择语句,不执行下面的语句;如果表达式2成立,则程序先执行“{语句2;}”中的语句,然后退出if选择语句,不执行下面的语句;如果表达式3成立,则程序先执行“{语句3;}”中的语句,然后退出if选择语句,不执行下面的语句;……;如果表达式m成立,则程序先执行“{语句m;}”中的语句,然后退出if选择语句,不执行下面的语句;如果上述表达式均不成立,则程序执行“{语句n;}”中的语句。

例如:根据a值的大小决定numb系数,编写的程序段如下所示。

            if  (a>6500)
            {numb=1;}
            else if  (a>6000)
            {numb=0.8;}
            else if  (a>5800)
              {numb=0.6;}
            else if  (a>5600)
              {numb=0.4}
            else
             {numb=0;}

(2)if语句的嵌套:如果if语句中又包含1个或多个if语句时,这种情况称为if语句的嵌套。if语句的嵌套基本形式如下:

下面以ATmega16单片机为例说明if条件语句,其发光二极管控制电路原理如图3-5所示。在ATmega16单片机的PC0和PC4端口分别接D1和D2这两个发光二极管,该实例的控制任务是当开关K1闭合时,发光二极管D1点亮,D2熄灭;当开关K1断开时,发光二极管D1熄灭,而D2点亮。编写的程序如下:

图3-5 发光二极管控制电路原理

            /****************************************************
             File name:       if语句应用.C
             Chip type:       ATmega16
             Clock frequency:  8.0MHz
            ****************************************************/
            #include"mega16.h"
            #define uchar unsigned char
            //定义开关及LED与端口的连接
            #define  redLED      PORTC.0
            #define  greenLED    PORTC.4
            void main(void)
            {   DDRC=0xFF;
              PORTC=0xFF;
                DDRD=0x00;
              PORTD=0xff;
                while(1)
                {if(PIND&0x01)          //检测PD0端口上的开关为断开状态
                    {
                      redLED=0x1;        //发光二极管D1熄灭(低电平有效)
                    greenLED=0x0;        //发光二极管D2点亮
                    }
                  else                    //检测PD0端口上的开关为闭合状态
                    {
                      redLED=0x0;        //发光二极管D1点亮
                    greenLED=0x1;        //发光二极管D2熄灭
                    }
                }
            }

2)switch语句在实际使用中,通常会碰到多分支选择问题,此时可以使用if嵌套语句来实现,但是如果分支很多,if语句的层数太多、程序冗长,则可读性会降低,而且很容易出错。基于此,在C语言中使用switch语句可以很好地解决多重if嵌套容易出现的问题。switch语句是另一种多分支选择语句,是用来实现多方向条件分支的语句。

(1)switch语句格式:

            switch(表达式)
              case  常量表达式1:
                    {语句1;}break;
              case  常量表达式2:
                    {语句2;}break;
              case  常量表达式3:
                    {语句3;}break;
                    ︙
              case  常量表达式m:
                    {语句m;}break;
                  default:
                    {语句n;}break;

(2)switch语句使用说明。

① switch后面括号内的“表达式”既可以是整型表达式或字符型表达式,也可以是枚举型数据。

②当switch后面表达式的值与某一“case”后面的常量表达式相等时,就执行该“case”后面的语句,如果后面遇到break语句就退出switch语句。若所有“case”中常量表达式的值都没有与表达式的值相匹配,就执行default后面的语句。

③每一个case的常量表达式的值必须互不相同,否则就会出现互相矛盾的现象(对同一个值,提供有两种或者多种解决方案)。

④每个case和default的出现次序不影响执行结果,可先出现“default”再出现其他的“case”。

⑤假如在case语句的最后没有“break;”,则流程控制将转移到下一个case继续执行。因此,在执行完一个case分支后,应使流程跳出switch结构,即终止switch语句的执行,这可使用一个break语句来完成。

下面仍以图3-5的电路原理图为例说明switch的用法。此时该例的任务是当按下开关K1时,发光二极管D1亮;当按下开关K2时,发光二极管D2亮。编写的程序如下:

            /****************************************************
             File name:       switch语句应用.C
             Chip type:       ATmega16
             Clock frequency:  8.0MHz
            ****************************************************/
            #include"mega16.h"
            #define uchar unsigned char
            //定义开关及LED与端口的连接
            #define  redLED      PORTC.0
            #define  greenLED    PORTC.4
            void main(void)
            {   DDRC=0xFF;
              PORTC=0xFF;
                DDRD=0x00;
              PORTD=0xff;
                while(1)
                {switch(PIND&0x03)       //该表达式判断K1和K2是否闭合,如果闭合则与之相连
                                        //的引脚为低电平
                  {
                    case 0x02:             //表达式的值等于0x02,表示K1开关处于闭合状态
                      redLED=0x0;         //发光二极管D1亮
                      break;              //跳出switch结构,不执行下面的语句
                    case 0x01:             //表达式的值等于0x01,表示K2开关处于闭合状态
                      greenLED=0x0;       //发光二极管D2亮
                      break;
                    default:               //表达式的值既不等于0x01,又不等于0x02表示两个开关
                                        //均未闭合
                      redLED=0x1;         //两个发光二极管均不亮
                      greenLED=0x1;
                      break;
                  }
                }
            }

2.循环语句与控制结构

在许多实际问题中,需要程序进行具有规律的重复执行,此时可以采用一些循环语句来实现。在C语言中,用来实现循环的语句有goto语句、while语句、do-while语句和for语句等。

1)goto语句 goto语句为无条件转向语句,该语句可以实现循环。goto语句的一般格式如下:

            goto  语句标号;

其中,语句标号不必特殊加以定义,它是一个任意合法的标识符,其命名规则与变量名相同,由字母、数字和下画线组成,并且第一个字符必须为字母或下画线,不能用整数作为标号。当这个标识符加上一个“:”一起出现在函数内某处时,则执行goto语句后,程序将跳转到该标号处并执行其后的语句。标号必须与goto语句同处于一个函数中,但可以不在一个循环层中。

结构化程序设计主张限制使用goto语句,主要是因为它将使程序层次不清,且不易读,但也并不绝对禁止使用goto语句。在多层嵌套退出时,用goto语句则比较合理。一般来说,使用goto语句可以有以下两种用途:与if语句一起构成循环结构;从循环体中跳转到循环体外。

(1)与if语句一起构成循环结构。例如:用if语句和goto语句构成循环结构,求,编写的程序如下所示。

            /****************************************************
             File name:       goto语句应用.C
             Chip type:       ATmega16
             Clock frequency:  8.0MHz
            ****************************************************/
            #include"mega16.h"
            void main(void)
              {
              int i=0,sum=0;
              loop:if(i<=50)
                  {
                  sum=sum+i;
                  i++;
                  goto loop;
                  }
              }

该程序的运行结果为1275(十六进制为04FB),在AVR Studio或者在Proteus中可观察到R19中的内容为04,R18中的内容为FB。

(2)从循环体中跳转到循环体外。在C语言中,如果要跳出本层循环和结束本次循环,可以使用break语句和continue语句。goto语句的使用机会已大大减少,只是需从多层循环的内层跳到多层循环体外时才用到goto语句。但是这种用法不符合结构化原则,一般不宜采用,只有在特殊情况(如需要大大提高生成代码的效率)时才使用。

2)while语句 while语句很早就出现在C语言编程的描述中了,它是最基本的控制元素之一,用来实现“当型”循环结构。while语句的一般格式如下:

                while(表达式)
                {语句;}

若程序的执行进入while循环的顶部时,将对表达式求值。如果该表达式为“真”(非零),则执行while循环内的语句。当执行到循环底端时,马上返回到while循环的顶部,再次对表达式进行求值。如果值仍为“真”,则继续循环,否则完全绕过该循环,而继续执行紧跟在while循环之后的语句,其流程如图3-6所示。

图3-6 while语句的流程图

例如:用while语句,求,编写的程序如下所示。

            /****************************************************
             File name:       while语句应用.C
            Chip type:       ATmega16
            Clock frequency:  8.0MHz
           ****************************************************/
           #include"mega16.h"
           void main(void)
             {
              int n=0,sum=0;
              while(n<=50)
                {
                  sum=sum+n;
                  n++;
                  }
             }

该程序的运行结果为1275(十六进制为04FB),在AVR Studio或者在Proteus中可观察到R19中的内容为04,R18中的内容仍为FB。

3)do-while语句 do-while循环与while循环十分相似,这两者的区别在于:do-while语句是先执行循环后判断,即循环内的语句至少执行一次,然后再判断是否继续循环,其流程如图3-7所示;while语句是在每次执行的指令前先判断。do-while语句的一般格式如下:

图3-7 do-while语句的流程图

            do
            {语句;}
            While(条件表达式);

例如:用do-while语句,求,编写的程序如下所示。

            /************************************************
            *File name:       do-while语句应用.C
            Chip type:       ATmega16
            Clock frequency:  8.0MHz
            ****************************************************/
            #include"mega16.h"
            void main(void)
              {
              int n=0,sum=0;
              do
                  {
                  sum=sum+n;
                  n++;
                  }
              while(n<=50);
              }

4)for语句在C语言中,for语句使用得最为灵活,完全可以取代while语句或者do-while语句。它不仅可以用于循环次数已经确定的情况,而且可以用于循环次数不确定而只给出循环结束条件的情况。for语句的一般格式如下:

            for(表达式1;表达式2;表达式3)
            {语句;}

for语句的流程如图3-8所示,其执行过程如下所示。

图3-8 for语句的流程图

①先对表达式1赋初值,进行初始化。

②判断表达式2是否满足给定的循环条件,若满足循环条件,则执行循环体内语句,然后执行第③步;若不满足循环条件,则结束循环,转到第⑤步。

③若表达式2为“真”,则在执行指定的循环语句后,求解表达式3。

④回到第②步继续执行。

⑤退出for循环,执行下面的语句。

for语句最简单的应用形式也就是最易理解的形式如下:

            for(循环变量赋初值;循环条件;循环变量增值)
            {语句;}

例如:用for语句,求,编写的程序如下所示。

            /****************************************************
             File name:       while语句应用.C
             Chip type:       ATmega16
             Clock frequency:  8.0MHz
            ****************************************************/
            #include"mega16.h"
            void main(void)
              {
              int n,sum=0;
              for(n=0;n<=50;n++)
                  {
                  sum=sum+n;
                  }
              }

显然,用for语句简单、方便。for语句的一般形式还可以用相应的while循环形式来表示:

            表达式1;
            while(表达式2)
              {
            语句;
                表达式3;
              }

同样,for语句的一般形式还可以用相应的do-while循环形式来表示:

            表达式1;
            do
              {
            语句;
                表达式3;
              }
            while(表达式2)

for语句使用得最为灵活,除了可以取代while语句或者do-while语句外,还在结构形式上体现了其灵活性,下面对for循环语句的几种特例进行说明。

(1)for语句中小括号内的表达式1缺省时,应在for语句之前给循环变量赋初值。注意,虽然表达式1省略了,但是表达式1后面的分号不能省略。例如:

            int n,sum=0;
            for(;n<=50;n++)
            {
            sum=sum+n;
            }

该程序段执行时,不对n设置初值,直接跳过“求解表达式1”这一步,而其他不变。

(2)for语句中小括号内的表达式2缺省时,不判断循环条件,默认循环条件始终为“真”,使循环无终止地进行下去。例如:

            int n,sum=0;
            for(n=0;;n++)
            {
            sum=sum+n;
            }
            它相当于:
            int n,sum=0;
                while(1)
                  {
                  sum=sum+n;
                  n++;
              }

(3)for语句中小括号内的表达式3缺省时,在程序中应书写相关语句以保证循环能正常结束。例如:

            int n,sum=0;
            for(n=0;n<=50;)
            {
            sum=sum+n;
              n++;
            }

在此程序段中,将n++的操作不放在for语句的表达式3的位置处,而作为循环体的一部分,其效果是一样的,都能使循环正常结束。

(4)for语句中小括号内的表达式1和表达式3缺省,而只给出循环条件时,在此种情况下,完全等效于while语句。例如:

            int n,sum=0;
            for(;n<=50;)
            {
            sum=sum+n;
              n++;
            }

它相当于:

            int n,sum=0;
            while  (n<=50)
            {
            sum=sum+n;
              n++;
            }

(5)for语句中小括号内的3个表达式都缺省时,既不设置初值,也不判断条件,而循环变量也不增值,则会使程序无终止地执行循环体。例如:

            for(;;)
              {…… /*循环体*/}
            它相当于:
            while(1)
              {…… /*循环体*/}

(6)for语句中没有循环体。例如:

            int  n;
             for(n=0;n<1000;n++)
                {;}

此例在程序段中起延时作用。

5)break和continue语句

(1)break:break语句通常可以用在switch语句或者循环语句中。当break语句用于switch语句中时,可使程序跳出switch而执行switch以后的语句;当break语句用于while、do-while、for循环语句中时,可使程序提前终止循环而执行循环后面的语句,通常break语句总是与if语句连在一起的,即满足条件时便跳出循环。break语句的一般格式如下:

            break;

注意:①break语句不能用在循环语句和switch语句之外的任何其他语句中;②break语句只能跳出它所处的那一层循环,而不像goto语句那样可以直接从最内层循环中跳出来。因此,要退出多重循环时,采用goto语句比较方便。

(2)continue语句:continue语句一般用在while、do-while、for循环语句中,其功能是跳过循环体中剩余的语句而强行执行下一次循环。通常continue语句总是与if语句连在一起的,用来加速循环。continue语句的一般格式如下:

            continue;

continue语句和break语句的区别:break语句结束循环时,不再进行条件判断;continue语句只能结束本次循环,不终止整个循环。