曾经在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.0592MHz 1200 波特率 TL1 = 0xE8;
a = 5; b = 6; c = 7; d = 8; //这会在整个函数有用
printf(“0: %d,%d,%d,%d”,a,b,c,d);
{ //复合句子 1
unsigned int a,e; //只在复合句子 1 中有用
printf(“1: %d,%d,%d,%d,%d”,a,b,c,d,e);
{ //复合句子 2
unsigned int b,f; //只在复合句子 2 中有用
b = 11,f = 200;
}//复合句子 2 完毕
printf(“1: %d,%d,%d,%d,%d”,a,b,c,d,e);
}//复合句子 1 完毕
printf(“0: %d,%d,%d,%d”,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 相配对。一般条件句子只会用作单一条件或少数量的分支,假如多数量的分支时则更多的会用到下一篇中的开 关句子。假如运用条件句子来编写超越 3 个以上的分支程序的话,会使程序变得不是那么明晰易读。
switch (表达式)
{
case 常量表达式 1: 句子 1; break;
}
运转中 switch 后边的表达式的值将会做为条件,与 case 后边的各个常量表达式的值相 比照,假如持平时则履行 case 后边的句子,再履行 break(连续句子)句子,跳出 switch 句子。假如 case 后没有和条件持平的值时就履行 default 后的句子。当要求没有契合的条 件时不做任何处理,则能不写 default 句子。
在上面的章节中咱们一向在用 printf 这个规范的 C 输出函数做字符的输出,运用它当然会很便利,但它的功用强大,所占用的存储空间天然也很大,要 1K 左右字节空间,假如 再加上 scanf 输入函数就要到达 2K 左右的字节,这样的话假如要求用 2K 存储空间的芯片时 就无法再运用这两个函数,例如 AT89C2051。在这些小项目中,一般咱们仅仅要求简略的字符输入输出,这儿以笔者宣布在自己网站的一个简略的串行口运用实例为例,一来学习运用开 关句子的运用,二来简略了解 51 芯片串行口根本编程。这个实例是用 PC 串行口经过上位机程序 与由 AT89c51 组成的下位机相通讯,完结用 PC 软件操控 AT89c51 芯片的 IO 口,这样也就可 以再经过相关电路完结对设备的操控。为了便利试验,在此所运用的硬件仍是用回以上课程中做好的硬件,以串行口和 PC 衔接,用 LED 检查试验的成果。原代码请到在笔者的网站 下载,上面有 单片机c言语 下位机源码、PC 上位机源码、电路图等材料。
代码中有多处运用开关句子的,运用它对不相同的条件做不相同的处理,如在 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 等是代表什么?它们是特别 功用存放器。
SBUF 数据缓冲存放器这是一个能直接寻址的串行口专用存放器。有朋友这样问起 过“为安在串行口收发中,都仅仅运用到同一个存放器 SBUF?而不是收发各用一个存放器。” 实践上 SBUF 包含了两个独立的存放器,一个是发送存放,另一个是接纳存放器,但它们都一起运用同一个寻址地址-99H。CPU 在读 SBUF 时会指到接纳存放器,在写时会指到发送存放器,并且接纳存放器是双缓冲存放器,这样能防止接纳中止没有及时的被呼应,数据没有被取走,下一帧数据已到来,而形成的数据堆叠问题。发送器则不需求用到双缓冲,一般情况下咱们在写发送程序时也不必用到发送中止去外理发送数据。操作 SBUF 存放器的办法 则很简略,只需把这个 99H 地址用关键字 sfr 界说为一个变量就能对其进行读写操作了,
如 sfr SBUF = 0x99;当然你也能用其它的称号。一般在规范的 reg51.h 或 at89x51.h 等头文件中已对其做了界说,只需用#include 引证就能了。
SCON 串行口操控存放器 一般在芯片或设备中为了监督或操控接口状况,都会引证 到接口操控存放器。SCON 便是 51 芯片的串行口操控存放器。它的寻址地址是 98H,是一个 能位寻址的存放器,效果便是监督和操控 51 芯片串行口的作业状况。51 芯片的串行口能 作业在几个不相同的作业形式下,其作业形式的设置便是运用 SCON 存放器。它的各个位的具 体界说如下:
(MSB) (LSB) SM0 SM1 SM2 REN TB8 RB8 TI RI
表 8-1 串行口操控存放器 SCON
SM0、SM1 为串行口作业形式设置位,这样两位能对应进行四种形式的设置。看表 8
-2 串行口作业形式设置。
SM0SM1模 式功用波特率
000同步移位存放器fosc/12
0118 位 UART可变
1029 位 UARTfosc/32 或 fosc/64
1139 位 UART可变
表 8-2 串行口作业形式设置
在这儿只阐明最常用的形式 1,其它的形式也就逐个略过,有爱好的朋友能找相关的 硬件材料检查。表中的 fosc 代表振动器的频率,也便是晶体震动器的频率。UART 为(Universal Asynchronous Receiver)的英文缩写。
SM2 在形式 2、形式 3 中为多处理机通讯使能位。在形式 0 中要求该位为 0。
REM 为答应接纳位,REM 置 1 时串行口答应接纳,置 0 时制止接纳。REM 是由软件置位或 清零。假如在一个电路中接纳和发送引脚 P3.0,P3.1 都和上位机相连,在软件上有串行口中止处理程序,当要求在处理某个子程序时不答应串行口被上位机来的操控字符发生中止,那么可 以在这个子程序的开端处参加 REM=0 来制止接纳,在子程序完毕处参加 REM=1 再次翻开串行口 接纳。咱们也能用上面的实践源码参加 REM=0 来进行试验。
TB8 发送数据位 8,在形式 2 和 3 是要发送的第 9 位。该位能用软件依据需求置位或铲除,一般这位在通讯协议中做奇偶位,在多处理机通讯中这一位则用于标明是地址帧仍是 数据帧。
RB8 接纳数据位 8,在形式 2 和 3 是已接纳数据的第 9 位。该位可能是奇偶位,地址/ 数据标识位。在形式 0 中,RB8 为保存位没有被运用。在形式 1 中,当 SM2=0,RB8 是已接 收数据的中止位。
TI 发送中止标识位。在形式 0,发送完第 8 位数据时,由硬件置位。其它形式中则是在 发送中止位之初,由硬件置位。TI 置位后,请求中止,CPU 呼应中止后,发送下一帧数据。 在任何形式下,TI 都有必要由软件来铲除,也便是说在数据写入到 SBUF 后,硬件发送数据,
中止呼应(如中止翻开),这个时分 TI=1,标明发送已完结,TI 不会由硬件铲除,所以这个时分有必要用软件对其清零。
RI 接纳中止标识位。在形式 0,接纳第 8 位完毕时,由硬件置位。其它形式中则是在接纳中止位的半中心,由硬件置位。RI=1,请求中止,要求 CPU 取走数据。但在形式 1 中,SM2=1 时,当未收到有用的中止位,则不会对 RI 置位。相同 RI 也有必要要靠软件铲除。
常用的串行口形式 1 是传输 10 个位的,1 位开端位为 0,8 位数据位,低位在先,1 位中止 位为 1。它的波特率是可变的,其速率是取决于守时器 1 或守时器 2 的守时值(溢出速率)。 AT89c51 和 AT89C2051 等 51 系列芯片只需两个守时器,守时器 0 和守时器 1,而守时器 2是 89C52 系列芯片才有的。
波特率 在运用串行口做通讯时,一个很重要的参数便是波特率,只需上下位机的波特率相同时才干进行正常通讯。波特率是指串行端口每秒内能传输的波特位数。有一些开端学习 的朋友以为波特率是指每秒传输的字节数,如规范 9600 会被误以为每秒种能传送 9600 个字节,而实践上它是指每秒能传送 9600 个二进位,而一个字节要 8 个二进位,如用串口形式 1 来传输那么加上开端位和中止位,每个数据字节就要占用 10 个二进位,9600 波特 率用形式 1 传输时,每秒传输的字节数是 9600÷10=960 字节。51 芯片的串行口作业形式 0 的波特率是固定的,为 fosc/12,以一个 12M 的晶体震动器来核算,那么它的波特率能到达 1M。 形式 2 的波特率是固定在 fosc/64 或 fosc/32,详细用那一种就取决于 PCON 存放器中的 SMOD 位,如 SMOD 为 0,波特率为 focs/64,SMOD 为 1,波特率为 focs/32。形式 1 和形式 3 的波 特率是可变的,取决于守时器 1 或 2(52 芯片)的溢出速率。那么咱们怎样去核算这两个模 式的波特率设置时相关的存放器的值呢?能用以下的公式去核算。
波特率=(2SMOD÷32)×守时器 1 溢出速率
上式中如设置了 PCON 存放器中的 SMOD 位为 1 时就能把波特率进步 2 倍。一般会运用 守时器 1 作业在守时器作业形式 2 下,这个时分守时值中的 TL1 做为计数,TH1 做为主动重装值 , 这个守时形式下,守时器溢出后,TH1 的值会主动装载到 TL1,再次开端计数,这样能不必软件去干涉,使得守时更准确。在这个守时形式 2 下守时器 1 溢出速率的核算公式如下:
溢出速率=(计数速率)/(256-TH1)
11.0592M
9600=(2÷32)×((11.0592M/12)/(256-TH1))
TH1=250 //看看是不是和上面实例中的运用的数值相同?
12M
9600=(2÷32)×((12M/12)/(256-TH1)) TH1≈249.49
上面的核算能看出运用 12M 晶体的时分核算出来的 TH1 不为整数,而 TH1 的值只能取整数,这样它就会有必定的差错存在不能发生准确的 9600 波特率。当然必定的差错是能 在运用中被承受的,就算运用 11.0592M 的晶体振动器也会因晶体自身所存在的差错使波特率发生差错,但晶体自身的差错对波特率的影响是十分之小的,能忽略不计。
==================================================================================================
goto 句子
void main(void)
{
start: a++;
if (a==10) goto end;
goto start;
end:;
}
上面一段程序仅仅阐明一下 goto 的运用办法,实践编写很少运用这样的方法。这段程序的意思是在程序开端处用标识符“start:”标识,标明程序这是程序的开端,“end:”标识程序的完毕,标识符的界说应遵从前面所讲的标识符界说准则,不能用 C 的关键字也不能和其它变 量和函数名相同,否则就会犯错了。程序履行 a++,a 的值加 1,当 a 等于 10 时程序会跳到 end 标识处完毕程序,否则跳回到 start 标识处持续 a++,直到 a 等于 10。上面的示例阐明 goto 不光能无条件的转向,并且能和 if 句子构成一个循环结构,这些在 C 程序员的程序中都不太常见,常见的 goto 句子运用办法是用它来跳出多重循环,不过它只能从内层循环跳到外层循环,不能从外层循环跳到内层循环。在下面提到 for 循环句子时再略为提一提。 为何大多数 C 程序员都不喜爱用 goto 句子?那是由于过多的运用它时会程序结构不明晰,过多的跳转就使程序又回到了汇编的编程风格,使程序失去了 C 的模块化的长处。
while 句子
while 句子的意思很不难理解,在英语中它的意思是“当…的时分…”,在这儿咱们能够理解为“当条件为真的时分就履行后边的句子”,它的语法如下:
while (条件表达式) 句子;
运用 while 句子时要留意当条件表达式为真时,它才履行后边的句子,履行完后再次回到 while 履行条件判别,为真时重复履行句子,为假时退出循环体。当条件一开端就为假时, 那么 while 后边的循环体(句子或复合句子)将一次都不履行就退出循环。在调试程序时要留意 while 的判别条件不能为假而形成的死循环,调试时恰当的在 while 处参加断点,或许会使你的调试作业愈加顺畅。当然有时会运用到死循环来等候中止或 IO 信号等,如在榜首 篇时咱们就用了 while(1)来不断的输出“Hello World!”。下面的比如是显现从 1 到 10 的累 加和,读者能修正一下 while 中的条件看看成果会假如,然后领会一下 while 的运用办法。