当前位置:首页 > 单片机 > 单片机
[导读] 曾经在BBS上有朋友问过我{}是什么意思?什么作用?在C中是有不少的括号,如{},[],()等,确实会让一些初入门的朋友不解。在VB等一些语言中同一个()号会有不同的作用,它可以用于组合若干条语句形成功能块

曾经在BBS上有朋友问过我{}是什么意思?什么作用?在C中是有不少的括号,如{},[],()等,确实会让一些初入门的朋友不解。在VB等一些语言中同一个()号会有不同的作用,它可以用于组合若干条语句形成功能块,可以用做数组的下标等,而在C中括号的分工较为明显,{}号是用于将若干条语句组合在一起形成一种功能块,这种由若干条语句组合而成的语句就叫复合语句。复合语句之间用{}分隔,而它内部的各条语句还是需要以分号";"结束。复合语句是允许嵌套的,也是就是在{}中的{}也是复合语句。复合语句在程序运行时,{}中的各行单语句是依次顺序执行的。以C语言中可以将复合语句视为一条单语句,也就是说在语法上等同于一条单语句。对于一个函数而言,函数体就是一个复合语句,也许大家会因此知道复合语句中不单可以用可执行语句组成,还可以用变量定义语句组成。要注意的是在复合语句中所定义的变量,称为局部变量,所谓局部变量就是指它的有效范围只在复合语句中,而函数也算是复合语句,所以函数内定义的变量有效范围也只在函数内部。关于局部变量和全局变量的具体用法会在说到函数时具体说明。下面用一段简单的例子简单说明复合语句和局部变量的使用。
#include
#include

void main(void)
{
unsigned int a,b,c,d; //这个定义会在整个main函数中?

SCON = 0x50; //串口方式1,允许接收
TMOD = 0x20; //定时器1定时方式2
TH1 = 0xE8; //11.0592MHz1200波特率
TL1 = 0xE8;
TI = 1;
TR1 = 1; //启动定时器

a = 5;
b = 6;
c = 7;
d = 8; //这会在整个函数有效
printf("0: %d,%d,%d,%dn",a,b,c,d);
{ //复合语句1
unsigned int a,e; //只在复合语句1中有效
a = 10,e = 100;
printf("1: %d,%d,%d,%d,%dn",a,b,c,d,e);
{ //复合语句2
unsigned int b,f; //只在复合语句2中有效
b = 11,f = 200;
printf("2: %d,%d,%d,%d,%d,%dn",a,b,c,d,e,f);
}//复合语句2结束
printf("1: %d,%d,%d,%d,%dn",a,b,c,d,e);
}//复合语句1结束
printf("0: %d,%d,%d,%dn",a,b,c,d);

while(1);
}

运行结果:
0:5,6,7,8
1: 10,6,7,8,100
2: 10,11,7,8,100,200
1: 10,6,7,8,100
0:5,6,7,8

结合以上的说明想想为何结果会是这样。

看到题目后相信大家都会大概对条件语句这个概念有所认识。是的,就如学习语文中的条件语句一样,C语言也一样是"如果XX就XX"或是"如果XX就XX否则XX"。也就是当条件符合时就执行语句。条件语句又被称为分支语句,其关键字是由if构成。C语言提供了3种形式的条件语句:
1: if (条件表达式) 语句
当条件表达式的结果为真时,就执行语句,否则就跳过。
如 if (a==b) a++; 当a等于b时,a就加1
2: if (条件表达式) 语句1
else 语句2
当条件表达式成立时,就执行语句1,否则就执行语句2
如 if (a==b)
a++;
else
a--;
当a等于b时,a加1,否则a-1。

3:if (条件表达式1) 语句1
else if (条件表达式2) 语句2
else if (条件表达式3) 语句3
else if (条件表达式m) 语句n
else 语句m
  这是由if else语句组成的嵌套,用来实现多方向条件分支,使用时因注意if和else的配对使用,要是少了一个就会语法出错,记住else总是与最临近的if相配对。

我们学习了条件语句,用多个条件语句可以实现多方向条件分支,但是可以发现使用过多的条件语句实现多方向分支会使条件语句嵌套过多,程序冗长,这样读起来也很不好读。这时使用开关语句同样可以达到处理多分支选择的目的,又可以使程序结构清晰。它的语法为下:

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

  运行中switch后面的表达式的值将会做为条件,与case后面的各个常量表达式的值相对比,如果相等时则执行后面的语句,再执行break(间断语句)语句,跳出switch语句。如果case没有和条件相等的值时就执行default后的语句。当要求没有符合的条件时不做任何处理,则可以不写default语句。
  在上面的课程中我们一直在用printf这个标准的C输出函数做字符的输出,使用它当然会很方便,但它的功能强大,所占用的存储空间自然也很大,要1K左右字节空间,如果再加上sCANf输入函数就要达到2K左右的字节,这样的话如果要求用2K存储空间的芯片时就无法再使用这两个函数,例如AT89C2051。在这些小项目中,通常我们只是要求简单的字符输入输出,这里以笔者发表在《无线电杂志》的一个简单的串口应用实例为例,一来学习使用开关语句的使用,二来简单了解51芯片串口基本编程。这个实例是用PC串口通过上位机程序与由AT89C51组成的下位机相通讯,实现用PC软件控制AT89C51芯片的IO口,这样也就可以再通过相关电路实现对设备的控制(这里是控制继电器)。在笔者的网站http://www.cdle.net还可以查看相关文章。所使用的硬件还是用回我们以上课程中做好的硬件,以串口和PC连接,用LED查看实验的结果。下面是源代码。
/*----------------------------------------
CDLE-J20_Main.c

PC串口控制IO口电路
可以用字符控制和读取IO口
简单版本V2.0
更加好的单片机版本和PC控制软件和DLL动态库
请访问磁动力工作室http://www.cdle.net

Copyright 2003 http://www.cdle.net

All rights reserved.
明浩 E-mail: pnzwzw@163.com
pnzwzw@cdle.net
----------------------------------------*/

#include

statICunsigned char data CN[4];
static unsigned char data CT;
unsigned char TS[8] = {254,252,248,240,224,192,128,0};

void main(void)
{
void InitCom(unsigned char BaudRate);
void ComOutChar(unsigned char OutData);
void CSToOut(void);
void CNToOut(void);
unsigned int a;

CT = 0; //接收字符序列
CN[0] = 0;
CN[1] = 51;
CN[2] = 51;
CN[3] = 0;
InitCom(6); //设置波特率为9600 1-8波特率300-57600
EA = 1;
ES = 1; //开串口中断
do
{
for (a=0; a<30000; a++)
P3_6 = 1;
for (a=0; a<30000; a++) //指示灯闪动
P3_6 = 0;
}
while(1);
}


//串口初始化晶振为11.0592M 方式1 波特率300-57600
void InitCom(unsigned char BaudRate)
{
unsigned char THTL;
switch (BaudRate)
{
case 1: THTL = 64; break; //波特率300
case 2: THTL = 160; break; //600
case 3: THTL = 208; break; //1200
case 4: THTL = 232; break; //2400
case 5: THTL = 244; break; //4800
case 6: THTL = 250; break; //9600
case 7: THTL = 253; break; //19200
case 8: THTL = 255; break; //57600
default: THTL = 208;
}
SCON = 0x50; //串口方式1,允许接收
TMOD = 0x20; //定时器1定时方式2
TCON = 0x40; //设定时器1开始计数
TH1 = THTL;
TL1 = THTL;
PCON = 0x80; //波特率加倍控制,SMOD位
RI = 0; //清收发标志
TI = 0;
TR1 = 1; //启动定时器
}

//向串口输出一个字符(非中断方式)
void ComOutChar(unsigned char OutData)
{
SBUF = OutData; //输出字符
while(!TI); //空语句判断字符是否发完
TI = 0; //清TI
}

//串口接收中断
void ComInINT(void) interrupt 4 using 1
{
if (RI) //判断是不是收完字符
{
if (CT>3)
{
CT = 0; //收完一组数据,序列指针清零
CN[0] = 0;
CN[1] = 51;
CN[2] = 51;
CN[3] = 0;
}
CN[CT] = SBUF;
CT++;
RI = 0; //RI清零
if (CN[0]==0x61 && CN[3]==0x61) //用aXXa的简单方式保证接收的可靠性,可以满足业余的要求
{ //a也可以为板下的ID号,在同一个串行口上可以挂上一块以上的板
CSToOut(); //收到的数据格式正确时,调用控制输出函数
} //要想更为可靠的工作则要用到数据检验和通讯协议
}
}

//根据全局变量输出相应的控制信号
void CSToOut(void)
{
unsigned char data a;
unsigned int data b;
switch(CN[1]) //aXXa的格式定义是第一个X为端口,0为P0,1为P1,2为P2,3为关闭所有(同时要第2个X为3,XX=33)
{ //XX=44为测试用,5为读取端口状态,大于5则为无效数据,
case 0: //第一个X小于3时,第二个X为要输出的数据。
P0 = CN[2];
CNToOut();
break;
case 1:
P1 = CN[2];
CNToOut();
break;
case 2:
P2 = CN[2];
CNToOut();
break;
case 3:
P0 = 0xFF;
P1 = 0xFF;
P2 = 0xFF;
CNToOut();
break;
case 4:
P0 = 0xFF;
P1 = 0xFF;
P2 = 0xFF;
for (a=0; a<8; a++)
{
P0 = TS[a];
for (b=0; b<50000; b++);
}
P0 = 0xFF;
for (a=0; a<8; a++)
{
P1 = TS[a];
for (b=0; b<50000; b++);
}
P1 = 0xFF;
for (a=0; a<4; a++)
{
P2 = TS[a];
for (b=0; b<50000; b++);
}
P2 = 0xFF;
CNToOut();
break;
case 5: //根据CN[2]返回所要读取的端口值
switch(CN[2])
{
case 0:
ComOutChar(CN[0]);
ComOutChar(CN[1]);
ComOutChar(P0);
ComOutChar(CN[3]);
break;
case 1:
ComOutChar(CN[0]);
ComOutChar(CN[1]);
ComOutChar(P1);
ComOutChar(CN[3]);
break;
case 2:
ComOutChar(CN[0]);
ComOutChar(CN[1]);
ComOutChar(P2);
ComOutChar(CN[3]);
break;
case 3:
ComOutChar(CN[0]);
ComOutChar(CN[1]);
ComOutChar(P3);
ComOutChar(CN[3]);
break;
}
break;
}
}

void CNToOut(void)
{
ComOutChar(CN[0]);
ComOutChar(CN[1]);
ComOutChar(CN[2]);
ComOutChar(CN[3]);
}

  代码中有多处使用开关语句的,使用它对不同的条件做不同的处理,如在CSToOut函数中根据CN[1]来选择输出到那个IO口,如CN[1]=0则把CN[2]的值送到P0,CN[1]=1则送到P1,这样的写法比起用if (CN[1]==0)这样的判断语句来的清晰明了。当然它们的效果没有太大的差别(在不考虑编译后的代码执行效率的情况下)。
在这段代码其主要的作用就是通过串口和上位机软件进行通讯,跟据上位机的命令字串,对指定的IO端口进行读写。InitCom函数,原型为void InitCom(unsigned char BaudRate),其作用为初始化串口。它的输入参数为一个字节,程序就是用这个参数做为开关语句的选择参数。如调用InitCom(6),函数就会把波特率设置为9600。当然这段代码只使用了一种波特率,可以用更高效率的语句去编写,这里就不多讨论了。
  看到这里,你也许会问函数中的SCON,TCON,TMOD,SCOM等是代表什么?它们是特殊功能寄存器,在以前也略提到过,51芯片的特殊功能寄存器说明可以参看附录二的'AT89C51特殊功能寄存器列表',在这里简单的说说串口相关的硬件设置。
  SBUF 数据缓冲寄存器 这是一个可以直接寻址的串行口专用寄存器。有朋友这样问起过“为何在串行口收发中,都只是使用到同一个寄存器SBUF?而不是收发各用一个寄存器。”实际上SBUF包含了两个独立的寄存器,一个是发送寄存,另一个是接收寄存器,但它们都共同使用同一个寻址地址-99H。CPU在读SBUF时会指到接收寄存器,在写时会指到发送寄存器,而且接收寄存器是双缓冲寄存器,这样可以避免接收中断没有及时的被响应,数据没有被取走,下一帧数据已到来,而造成的数据重叠问题。发送器则不需要用到双缓冲,一般情况下我们在写发送程序时也不必用到发送中断去外理发送数据。操作SBUF寄存器的方法则很简单,只要把这个99H地址用关键字sfr定义为一个变量就可以对其进行读写操作了,如sfr SBUF = 0x99;当然你也可以用其它的名称。通常在标准的reg51.h或at89x51.h等头文件中已对其做了定义,只要用#include引用就可以了。
  SCON 串行口控制寄存器 通常在芯片或设备中为了监视或控制接口状态,都会引用到接口控制寄存器。SCON就是51芯片的串行口控制寄存器。它的寻址地址是98H,是一个可以位寻址的寄存器,作用就是监视和控制51芯片串行口的工作状态。51芯片的串口可以工作在几个不同的工作模式下,其工作模式的设置就是使用SCON寄存器。它的各个位的具体定义如下:

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭