跟家电维修工深入浅出学单片机编程入门《四》
编辑时间:2018-06-30 作者:LCDHOME论坛网友 浏览量:2772 来源:LCDHOME论坛

今天讲讲8051编程时常用的语句(语言句子),也是C语言的基础语句。

  一、条件语句:条件语句分为单分支if语句,双分支if语句和多分支if语句。

  1、单分支if语句:

  在小学时有用“如果”来造句。实际英文“if”的意思就是“如果”的意思。单分支if语句实际就是当满足条件后,执行下面的语句(命令或程序)

  例如:

    如果明天天气晴朗:

        带着老婆逛超市去;

        顺便买点日用品!

“如果明天天气晴朗”是个条件,当真的“明天天气晴朗”时 ,就带着老婆逛超市去了,还能“顺便买点日用品!”,当“明天天气不晴朗”是,就不带着老婆逛超市了,当然也就买不成日用品了。C语言“if”语句和这类似,只不过是要把要干的事情用大括号括起来,大括号里面的每一个步骤还要加“;”号(这个分号在C语言中很重要,一般我们写程序要单片机执行动作的语言每一步都要有个“;”,不然keil编译时会报错,但也不能乱加,不该加的地方有它的话编译时keil也会报错).例如:

     if(k==0)

{

   P0=0x00;

    P1=0x55;

   P2=0xff;

  这里,小括号里面的“k==0”是个条件,“k”是你在写这些语句前事先声明(事先说明)的变量或定义的(确定的)开关等代码,大括号里面的是你让单片机执行的命令,P0/P1/P2分别表示单片机的P0口(32---35脚)、P1口(1---8脚)、P2口(21---27脚)。每个“=”右边的“0x”只是代表的是十六进制数,即后边的“00”、“55”、“ff”写的都是十六进制。P0=0x00表示P0口全部输出低电平; P1=0x55表示P1口输出隔一个低电平,一个高电平(二进制01010101);P2=0xff表示P2口全部输出高电平。

  当条件不满足时,实际大括号里面的命令等于无效。

  注意:以上的条件(k==0)里面的是“==”,这里表示相等;大括号的执行命令是“=”,这里表示赋值或者叫参数传递,二者不要搞反。再一个,keil C51编译是区分字母大小写的,P0---P3口的P不能用小写p。

  

==============================================================================================================

  有时我们为了方便理解,在写的语句后面加上注释,在写复杂的程序时尤为重要。为了不至于编译出错,要在每行注释的前面加两道“//”,或在每行注释的前面加“/*”,后面加“*/”。有了它们之后,keil就不再编译以后的注释。

.

如:

       if(k==0)

{

   P0=0x00;   //P0口全部输出低电平

    P1=0x55;   //P1口输出隔一个低电平,一个高电平

   P2=0xff;   //P2口全部输出高电平

或者:

     if(k==0)

{

   P0=0x00;   /*P0口全部输出低电平*/

    P1=0x55;   /*P1口输出隔一个低电平,一个高电平*/

   P2=0xff;   /*口全部输出高电平*/

  “//”适用于单行注释,“/* */”适用于在程序调试中屏蔽多行(即取消这些范围内的编译)

  注释有两种含义,一种是它的动作步骤,比如本例,初学着就是这种方法。另一种是目的,比如P0=0x00这条语句你想来干什么?是来点亮它上面连接的发光管吗?,如果是,注释就这样写了:“点亮P0口所有LED”。

=============================================================================================

当大括号内只有一条语句时,大括号可以省略,如:

       if(k==0)

{

   P0=0x00;   

    

可以写成:

    if(k==0)

     P0=0x00; 

===============================================

注意:if(k==0)后面和大括号最后都不能有分号。

 


2、双分支if语句:

  在小学我们学过用“如果..否则..”造没造过句子?比如:

  如果明天天气晴朗,带著老婆逛公园去;否则在家搂着老婆睡觉。

  这实际是有两个事情要做,当满足这个条件时就做第一件事,不满足这个条件时就做另一件事。

  

双分支if语句和这一样,只不过它是用英文单词代替了,它就是:if和else, "if"翻译成中文是“如果”的意思,"else"翻译成中文是“否则”的意思。它的格式是:

  if(条件)

  在这个大括号里写上你满足“条件”时你让单片机做第一件的事(也就是你编写的语句程序)

  else

{

    在这个大括号里写上你不满足“条件”时你让单片机做的另一件事(也就是你编写的语句程序)

}

举例:

  if(n==5)             //条件是n等于5

{

    P0=0x00;         //P0口全部输出低电平

  P1=0xff;           //P1口全部输出高电平


}

  else

{

  P2=0x00;             //P2口全部输出低电平

  P3=0xff;             //P3口全部输出高电平

}

当大括号内的语句只有一条时,大括号可以省略。如下:

  if(n==5)             //条件是n等于5

      P0=0x00;         //P0口全部输出低电平

  else

      P2=0x00;             //P2口全部输出低电平

 

多分支if语句是有多个条件,满足哪个条件做哪个事情,不满足的就不做。用中文举例:

  如果明天天气晴朗:

      带着老婆逛公园去!

  如果明天下小雨:

      带着老婆逛商厂去!

  如果明天朋友打电话:

      带着老婆和朋友喝酒去!

  如果..

        

    如果明天下大雨:

      搂着老婆在家睡觉!

其实多分支if语句和上面的基本上一样,只不过第一个第一个用“if”,中间的用“else   if”,最后的用“else”.

  格式如下:

  if(条件1)

{

    这里写上满足条件1时你要让单片机做的事情(即程序语句)

}

  else if(条件2)

{

    这里写上满足条件2时你要让单片机做的事情(即程序语句)

}

  else if(条件3)

{

    这里写上满足条件3时你要让单片机做的事情(即程序语句)

}

.

  else if(条件n)

{

    这里写上满足条件n时你要让单片机做的事情(即程序语句)

}

  else

{

  这里写上满足最后的一个条件时你要让单片机做的事情(即程序语句)

}

 


多分支if语句举例:

  if(a>b)                     //a和b是事先声明的变量(即给变量起的代码或叫做名字),这是根

                      据实际需要你自己定的。

  P1=0x00;                 //P1口全部输出低电平

    P2=0x01;                 //P2口最低位(bit0位)输出高电平,其它位全部输出低电平


  else if(a<b)                     

  P1=0x01;                 //P1口最低位(bit0位)输出高电平,其它位全部输出低电平

  P2=0x02;                 //P2口bit1位输出高电平,其它位全部输出低电平


  else (a==b)

{

    P1=0xff;                 //P1口全部输出高电平

  P2=0xff;                 //P2口全部输出高电平

}

 


二、switch开关语句

  "switch"开关语句是另一种多分支语句."switch"翻译成中文的意思就是“开关”的意思。不知道大家用没用过以前的多波段收音机,开关语句相当于多波段收音机的波段开关,当拨到不同的位置,就收听不同频段的广播节目,比如中波、短波1、短波2、.调频波段等。

  也可以把开关语句比成是你买火车票外出旅行,当你拿着火车票上车后,看着上面的座位号找到位置,对号入座。

  switch语句的工作过程是:有一个变化的量(这个变量可以是某个端口输入的不确定的值,或者是某些经过处理的数据),与多个固定的量(数值)做比较,如果与哪个固定的量(数值)相等,就执行哪个固定的量里面的程序,其它不符合条件的内容一概不再执行。

  变化的量(变量)就是switch后面小括号里的内容,固定的量(常量)就是每个case后面的数。

switch语句的格式是:

  switch(这个括号里填上变量代码)   

{

      case 数值1:                       

    在这里写上switch后面括号里的变量与数值1相等时你要让单片机做的事情;

    break;

    case 数值2:                       

      在这里写上switch后面括号里的变量与数值2相等时你要让单片机做的事情;

    break;

    case 数值3:                       

      在这里写上switch后面括号里的变量与数值3相等时你要让单片机做的事情;

      .       

      case 数值n :                       

      在这里写上switch后面括号里的变量与数值n相等时你要让单片机做的事情;

      break;

      default:

      在这里写上switch后面括号里的变量与最后一个数值相等时你要让单片机做的事情;

    

}

  要注意:每个case 数值后面和default后面是冒号“:”,你要单片机做的事情后面是分号“;”每个break后面也是分号。

    break 的意思在这里是“跳出”的意思,就是执行完对应的语句后跳出总的开关语句,不再执行开关语句中的其它内容,因为它们都不符合条件。如果你把break弄丢了,程序就接着执行它下面的的语句,程序就出错了。

    default是默认的意思。这个也可不用。

switch 语句举例:

    switch(n)     //n为你事先定义的变量

{

  case 10:           //当n等于10时执行

      P0=0x00;

      P1=0xff;

  break;

  case 20:           //当n等于20时执行

      P0=0xff;

      P1=0x00;

  break;

  case 30:         //当n等于30时执行


      P0=0x0f;

      P1=0xaa;

  break;

  case 40:           //当n等于40时执行

      P0=0xf0;

      P1=0x55;

  break;

}

for语句举例:

  for(a=0;a<8;a++)   //自己事先定义的变量a从0开始每次加1,一直加到小于8结束

{

    data=data<<1;     //自己事先定义的变量data每次左移一位

    P1=data;           //把左移后的变量送到P1口

}

这个语句意思是把大括号里的内容循环执行了8次,每次把data的内容左移一位后送到P1口。以上后面的注释是每行语句的动作步骤,是便于初学者理解的,在实际中可不这样注释,应该注释写这个语句的目的。假设P1口你接的是LED发光管,data是要显示的内容(数据),那么P1=data后面的注释就是:"显示"

再特别说明一下下面的意思:

  for(a=0;a<8;a++) 

{

  data=data<<1;   

  P1=data;       

}

  这个程序的动作过程是:

  a从0开始计数。假设data开始的值换算成二进制是11111111,程序开始后,data(即11111111)左移一位后变成了11111110,这个数值被送到了P0口,完事后a便加1即成了2,第二步再把data(此时是11111110)左移一位就变成了11111100,又把它送到P1口,完事后a又加1即变成了3,再把data(此时是11111100)左移一位就变成了11111000,再把它送到P1口,完事后a再加1变成了3,如此循环,直到a<8. 实际a的大小就是循环次数。

for语句大部分是用来做延时用,每一步延时长短与单片机外接的晶振有关。晶振频率高,时间短,否则时间就长。时间与频率成倒数关系,即:

  T=1/f

式中,T-------周期(时间),单位:秒(s);

    f--------频率,单位:赫兹(Hz)

我们做实验时常用12M或11.0592M的。

    为什么单片机要用晶体振荡器呢?原因是单片机是一个数字电路,它有很多存储器,存储器是由双稳态电路组成的,每个双稳态电路在工作时它的速度是不一样的,有的快,有的慢,就像是很多人干活一样。为了使它们工作一致不至于乱套,就用一个时钟控制它们,这就好像军训时教官喊的口令一样:“一二一,一二一,立正!”,这样后面的人才不至于踩着前面人的脚后跟。

当8051单片机选用12M晶振时,在for语句中变量的每一次自身加1或减1所消耗的时间是1us(微秒),1s(秒)=1000ms(毫秒)=1000000us(微秒),当用其它频率的晶振时,可用下式计算:

  时间(us)=12/晶振频率(Mhz)

下面来写一个完整的延时函数来说明一下for语句的用法。

  一个完整的延时函数包括函数名,定义的局部变量和延时语句组成。

关于函数的命名(定义或叫“起名子”)请看以前所述,这里再说一下:

  一个函数名包括函数返回值类型、函数名和形式参数三部分。因延时函数不返回任何数值,所以就用“void”表示;函数名只要避开C语言的关键字、第一个必须用字母或下画线可随便起,也可以用拼音。这里为了显示咱也有文化,就叫他“delay“(英文延时的意思);先写不带形参的函数,括号里面的“void”咱把它省掉,那么,这个函数名就是:

void delay()

函数名有了,大括号有了,接下来就是定义变量。

  单片机工作时实际就是处理数据,如果把数据比作是水,那么变量名就是装水的盆子(容器),整个单片机的RAM(内部用户存储器)就是工作车间。所以定义变量要带数据类型,数据类型好比是盆子的大小,我们要考虑它的大小,因为单片机的RAM有限。定义变量时能小不大,但需要大时也不能太小。这就好比100千克的水你选50吨的水罐,恐怕连车间的门就进不去了,就是进去恐怕车间也无法工作了!相1反,100千克的水你选20千克的盆子,倒进去怎么样?这个盆子的水就是20千克,其它全都溢出了!定义数据类型和这一样!

变量按存在的位置分:可分为“全局变量”和“局部变量”两种。

  写在程序开头的变量就是全局变量,这种变量整个程序的函数都可以调用,你可以把它理解为整个工厂公用装水的盆子。

  全局变量会一直占用RAM,相当于占用工作车间,所以程序大时要尽可能少用。

  写在各个子函数内部的变量就是局部变量,这种变量只有在本函数使用,其它函数都不能用。你可以把它理解为每个车间自用装水的盆子,不让别的人用。

  以下我们要写的延时子函数的变量就是局部变量。

  局部变量只有临时占用RAM。

  全局变量和局部变量可以重名,它们之间互不影响。这就好比工厂里管理人员与车间里普通工人重名一样,不会叫错。而同一个函数内不同的局部变量不不能重名,这就好比同一个车间两个人同名,往往叫错。

数据类型解释:

  以上说了:变量好比水,数据类型就是装水的盆子。即数据类型就是划分需要保留数的大小范围。

你定义了后,就把它限制成多大了。数有正负之分,所以数据类型分有符号(包括负整数、0、正整数)与无符号(包括0、正整数),定义它们的区别是:无符号数据类型前面多了一个“unsigned”单词。

我们很多情况下是用无符号的。

  C51的数据类型划分范围大家可以查8051C语言方面的书籍,这里因时间关系省略。

延时程序举例:

viod delay()

{

    unsigned char a:   

    for(a=200;a=0;a--):

}

  这里,viod delay()是一个函数名,它是一个无返回值不带参数的子函数。

    unsigned char a是定义了一个无符号的字符型变量a,unsigned即无符号(0和自然数)的意思,char即字符型数据,数值取值范围0----255,即a最大赋值不能超过255。这个延时函数在晶振12M时大约延时200us.如果你嫌延时时间较小,可以把char改写成int整型数据,这样,a的取值范围是0----65535,即最大不能超过65535.

当延时时间较长时,一个变量循环是不够的,要多增加几个变量组成二循环、三循环等如:

void delay()

{

  unsigned char a;

  unsigned char b;

  for(a=100;a=0;a--)

  {

      for(b=200;b=0;b--);

    } 

}

这样,整个延时时间约为axb=20000us.

或者三重循环:

void delay()

{

  unsigned char a;

  unsigned char b;

  unsigned char c;

  for(a=100;a=0;a--)

  {

      for(b=200;b=0;b--)

        {

          for(c=250;c=0;c--);

        }

    } 

}

这样,整个延时时间为axbxc,约为500000us(0.5s).

照这样你可以写多数个重循环,也可以把变量定义成int型,每个变量最大可达65535,延时时间会更长。

大家注意for语句的内部才有“;”小括号里只有前面两个有“;”。

因为相同类型的变量可以写在一起(不相同的当然不行,如char型与int型),for语句内部只有一条的可以省略大括号,所以,上边的延时函数可以简写成:

void delay()

{

unsigned char a,b,c;   //请注意前面标点符号

  for(a=100;a=0;a--)

    for(b=200;b=0;b--)

            for(c=250;c=0;c--);   //请注意只有这里才有分号“;”

}

unsigned char或unsigned int特别是unsigned这个单词写起来太麻烦,为了省劲,我们在编写程序时,只要在程序的开头写上以下所谓的“宏定义”:

#define uchar unsigned char

#define uint unsigned int

在整个程序中,就可以用uchar代替unsigned char;用uint代替unsigned int,上面的延时函数就可以简写成:

void delay()

{

  uchar a,b,c;   

for(a=100;a=0;a--)

  for(b=200;b=0;b--)

        for(c=250;c=0;c--);   

}

当然,在程序开头你不写“#define uchar unsigned char 或#define uint unsigned int

”时是不行的,否则在keil中编译会报错。

2、while语句

  while的意思是“在某段时间”的意思,在8051 C语言里是在某段时间要做的事情或等待这一会儿。

  炎热的夏天,你口干舌燥,当你碰见一个卖西瓜的你会怎样?当然要看看兜里有没有“银子”了,有的话当然要买一块解解渴了!吃完了还不解渴怎么办?这时是不是还要看看兜里的钱够不够了?如果够的话再来一块!如此再买第三块、第四块...

  这个过程是你先看看有没有钱,然后才决定去买,接着再判断钱的问题,再买,如此循环。

  while语句与此类似,它的工作步骤是:首先判断条件满足不满足,满足就执行while内部的语句,执行完后再判断满足不满足条件,不满足再执行一遍;接着在判断,再执行,如此循环,直到满足条件为止。

  简短的讲:while语句是先判断条件,后决定执行。条件不满足一直循环,条件满足时立即跳出循环。

while语句格式

while(循环条件)

{

  这个大括号里写上你想让单片机做的工作;

}

while语句举例:

while(P1^0==0) //当P1.0引脚为低电平时一直循环:

  P0=0x00;   //P0口全部输出低电平

    delay();   //延时一段时间

  P0=0xff;   //P0口全部输出高电平

  delay();   //再延时一段时间


这实际是一个闪灯控制程序,假设P1.0对地接了一个开关,当它按下时,P0口一直循环输出高低电平,相应地,接在上面的LED会不停的闪烁。如果不要while(P1^0)和大括号,LED只会亮一下就会灭(LED串一个330Ω以上的电阻接到+5v),或者LED一直亮,不会闪动(LED串一个330Ω以上的电阻接到地)。当然了,延时程序的延时时间要适当,否则也不会闪烁。

do-while语句

  “do”英文的意思是“做”,"while"在这里是循环的意思。do-while总的动作是先做一遍,在看看满足不满足条件,如果满足条件继续大括号的语句,不满足时跳出do-while语句。

  while语句是先判断条件,符合条件时执行大括号的语句,不符合条件不执行while语句;do-while语句是先执行一遍大括号里面的语句,再判断条件,条件真是再执行,否则跳出。

  这样说吧:while语句是先判断,再决定执行;do-while语句是先执行,再判断。

    用打临时工做比方:while好比是你事先和老板谈好了一天给多少工资,再决定干不干;do-while好比是你事先没和老板谈工资,先干一天,看看这一天下班后老板给你多少钱,再决定从明天以后干不干。

do-while语句举例:

do

{

  unsgned char n;   //定义一个变量(正数),用这个变量的值来作判断条件,当然也可用一个全局变量(在程序开头部分定义)

  P0=0xff;           //P0口全部输出高电平

  delay();           //延时程序(这个延时程序是调用已写好的)

  P0=0x00;         //P0口全部输出低电平

  delay();           //同上

  n++;             //变量加1

}

  while(n<10);   //如果n小于10时继续重复以上的过程,否则退出。

  这实际是一个闪灯程序,定义一个变量n,每次做完大括号里的过程后n就加1,第一次做完后n为2,第二次做完后n为3,..,当n大于或等于10时就不再执行这个程序了。

四、跳转语句:跳转语句主要是根据条件转移到不同的地方执行程序。主要有“goto”、“break”、“continue”。

1、goto语句

  “goto”是一种无条件跳转语句。中文的意思就是“去到”,去到哪儿呢?要有个地方,即作一个标记,这个地方就叫做“标号”。这个标号的名字你随便起,但不能和C语言的关键字相同。在它的后面要注意加冒号“:” 。

  注意:goto语句只能在一个函数中从内层跳到外层,可跳出多重循环(即可从最里面的一对大括号一直可跳到最外面的一对大括号),不能从外层跳到内层,也不能从一个函数跳到另一个函数(即不能从一个子程序跳到另一个子程序)。

  这就好比是你下楼时可以隔着台阶往下蹦,不能隔着台阶往上蹦,更不能从这一座楼的台阶蹦到另一座楼的台阶上。

goto语句的一般格式:

  goto 标号名;   //标号为英文,不能和C语言关键字重名。标号根据情况可放在同一函数内

                的开头、中间、结尾等任何地方。

goto语句举例:

  以一个闪灯显示函数为例。

  void display()         

{

    unsigned char n;     //定义一个整型计数变量n,在没给n赋值时一般默认初始值为0.

loop:                       //跳转的位置标号。注意后面是冒号

    n++;                 //每一次循环让n加1

    P0=0x00;             //P0口全部输出低电平

    delay();               //延时一段时间。注意这个延时函数要另外单独写

    P0=0xff;               //P0口全部输出高电平

    delay();               //作用同上

    if(n<=5)               //判断     

    goto loop;         //如果n等于1------4时继续循环,否则退出本程序。注意后面是分号。


}

2、break语句

  break 译成中文是“打破”的意思,在C51语言里是“退出”的意思。主要用于无条件跳出开关分支或循环结构。注意:break只能向外跳出一层循环,即只能从内层循环跳到它所在的大括号外边,不能跳出多重循环

    在前面讲到的开关语句switch语句中,每个小分支后面都有一个“break”.break在for、while、do-while语句中可以强制跳出循环。它一般和if一起配合使用。

break语句举例:

  以一个按键按下来举例。

void key()                 //自己起的函数名

{

  unsigned char n;     //定义一个字符型计数变量

    if(key==0)           //判断按键是否按下(按下为0)。key是事先在程序开头部分定义的位变量,表示一个按键接到某一个端口与地之间(如事先定义:sbit key=P1^1,即表示把一个按键接到了标准8051单片机的第二脚)。

      n++;               //按键每按下一次n值就加上1

    if(n==10);         //当按键按下10次后退出本程序。

    break;

}

 

3、continue语句:

  continue译成中文的意思是“继续”,即继续做的意思。它一般用在for、while、do-while等循环语句中,当在循环体内一遇见它,就返回前面继续循环,不再执行它后面的语句。这就好比你加工一个产品,做着做着发现它不合格(再做也是废品),不再继续往下做了,另从新开始生产一个。

  continue语句的作用:用于强制结束大括号里它所在位置的后续语句,转而继续执行循环它前面的循环语句。

为了让大家学好学会8051单片机,我向大家推荐以下几本书:

  《例说51单片机》(C语言版) 张仪和 等编著。

  《新概念51单片机C语言教程入门、提高、开发。拓展全攻略》郭天祥编著,这本书是最好的入门书。

    《21天学通51单片机开发》 陆彬 等编著,这是较好的51单片机C语言查询书。

以后我所讲的搞不通的地方,大家可从这些书或者其它书上查询,因为时间和版面原因,我不可能都说的面面俱到,我重点讲那些初学时遇到的问题和搞不懂的地方。

 


来说两句吧