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