51c语言字符,51单片机C语言教程(九) 语句应用

本文详细介绍了C语言中的复合语句和条件语句的概念与使用方法,并通过实际编程示例展示了如何利用复合语句组织代码逻辑以及如何通过条件语句实现程序中的条件分支。

曾经在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寄存器。它的各个位的具体定义如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值