《单片机原理及接口技术》课件第8章_第1页
《单片机原理及接口技术》课件第8章_第2页
《单片机原理及接口技术》课件第8章_第3页
《单片机原理及接口技术》课件第8章_第4页
《单片机原理及接口技术》课件第8章_第5页
已阅读5页,还剩129页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第8章单片机C程序设计及应用8.1单片机C51程序设计

8.2采用C51程序的设计实例

本章小结

练习与思考题

8.1单片机C51程序设计

8.1.1单片机C程序的基本概念

目前仍在使用的单片机开发设计语言大致有两类:汇编语言和C语言。C语言由于其结构化、移植性好和高效性的特点,是进行单片机开发的一种利器。C语言是一种源于编写UNIX操作系统的语言,与汇编语言相比,有以下优点:

(1)不需要了解51单片机的指令系统,仅仅要求初步了解存储器结构。

(2)程序有规范的结构,可分为不同的函数,使程序结构化。

(3)程序可读性好。

(4)提供很多标准库函数,数据处理能力强。

(5)程序易于做到模块化,移植性好。8.1.2单片机C程序的基本结构

C语言是一种通用性很强的结构化程序设计语言。从程序流程的角度来看,单片机C程序可以分为3种基本结构:顺序结构、选择结构和循环结构。这3种基本结构可以组成各种复杂程序。

1.顺序结构

顺序结构程序是仅包含一个main()函数的简单程序,适当运用表达式语句就能设计出具有某特定功能的顺序结构C51程序。这是一种最简单的基本结构,程序只由低地址向高地址顺序执行指令代码,如图8-1所示。虽然该程序设计方法简单,但在具体运用中的算法仍然采用自顶向下逐步求精的方法进行设计。图8-1顺序结构

2.选择结构

使单片机具有决策能力的是选择结构,这种结构也称为分支结构,如图8-2(a)所示。选择结构中包含一个判断框,执行流程根据判断条件P的成立与否,选择执行其中的一路分支。图8-2(b)所示的是特殊的选择结构,即一路为空的选择结构。这种选择结构中,当P条件成立时,执行A操作,然后脱离选择结构;如果P条件不成立,则直接脱离选择结构。它包括if语句结构和switch语句结构两种。图8-2选择结构

1) if语句结构

C语言的if语句有3种形式:基本if形式、if-else形式、if-else-if形式。

基本if形式语法结构如下:

if(表达式)

处理程序;

处理机理是:如果表达式的值为“真”,则执行“处理程序”的语句内容,否则不执行该语句内容。例如:

voidmain()

{

ucharsp0=0,buffer0[2]={0,0};

if(sp0==0)

buffer0[sp0]=RXBUF0;//接收串口数据

}

if-else形式语法结构如下:

if(表达式)

处理程序1;

else

处理程序2;处理机理是:如果if表达式的值为“真”,则执行“处理程序1”的语句内容,否则执行“处理程序2”语句内容。例如:

voidmain()

{

ucharend,rev_flag;

if(end==1)

rev_flag0=1;

else

rev_flag0=0;

}

if-else-if形式语法结构如下:

if(表达式1)

处理程序1;

elseif(表达式2)

处理程序2;

else(表达式n)

处理程序n;处理机理是:如果表达式1的值为“真”,则执行与之相对应的“处理程序1”的语句内容;如果表达式2的值为“真”,则执行与之相对应的“处理程序2”的语句内容,依次判断表达式“x”是否为“真”,如果为“真”则执行与之相对应的“处理程序x”语句内容。例如:

voidmain()

{

ucharsp0=0,rev_byte0,buffer0[2]={0,0},

receive_frame[10];

if(sp0==0)

{

buffer0[sp0]=RXBUF0;//接收串口数据

if(buffer0[0]==0xEB)sp0++;

}

/*判断第二个字节*/

elseif(sp0==1)

{

buffer0[sp0]=RXBUF0;

if((buffer0[0]==0xEB)&&(buffer0[1]==0x90))

sp0++;//数据为EB90

elsesp0=0;

}

elseif(sp0>=2)

{

receive_frame[rev_byte0]=RXBUF0;

rev_byte0++;

}

2) switch语句结构

C语言的switch语句又称开关语句,它可以从多种情况中选择满足条件的一种情况,是多分支选择结构语句。

switch形式语法结构如下:switch(表达式)

{

case<常量表达式1>:[处理序列1;[break]]

case<常量表达式2>:[处理序列2;[break]]

case<常量表达式n>:[处理序列n;[break]]

[default:处理序列n+1;]

}处理机理是:计算表达式的值,并逐个与其后的常量表达式的值相比较,当表达式的值与某个常量表达式的值相等,即执行其后的处理序列语句,然后不再进行判断,继续执行后面所有case后的处理序列语句。如表达式的值与所有case后的常量表达式都不相同时,则执行default后的处理序列语句。C语言还提供了一种break语句,专用于跳出switch语句。例如:voidmain()

{

ucharflag=0,up=0,down=0,left=0,right=0;

switch(flag)

{

case1:up=1;break;//置标志

case2:down=1;break;

case3:left=1;break;

case4:right=1;break;

default:error=1;break;

}

}本例中输入一个整数,经switch判断后,选择输出是一周中的星期几,整数不在1~7的范围内时就输出“Error”。可见此类多分支程序用switch语句可以很容易实现。

3.循环结构

计算机的基本特征之一就是具有重复执行一组语句的能力——循环能力。循环结构是程序中一种很重要的结构。其特点是:在给定条件成立时,反复执行某程序段,直到条件不成立时为止。

循环结构有两种形式:当型循环和直到型循环。

1)当型循环

判断条件P是否成立,若成立,则重复执行A操作,直至某次判断条件P不再成立,就不再执行A操作而退出循环结构,见图8-3(a)。

2)直到型循环

重复执行A操作,然后判断条件P是否成立,如果不成立再执行A操作,直到条件P成立,不再执行A操作,退出循环,见图8-3(b)。图8-3循环结构无论是顺序结构、选择结构,还是循环结构,它们有一个共同点,即只有一个入口且只有一个出口。从示意图中可以看到,如果把基本结构看做是一个整体(用虚线框表示),执行流程从a点进入基本结构,而从b点脱离基本结构。整个程序由若干个这样的基本结构组合而成,必然有良好的可读性和可维护性。它包括for语句结构和while语句结构两种。

(1) for语句结构。C语言中,for语句是一种使用最为方便灵活的循环控制语句结构,它提供了一个应用非常灵活的控制部分,既可以实现计数循环程序设计,又可以实现条件控制循环程序设计。

for形式语法结构如下:

for(表达式1;表达式2;表达式3)

处理程序;处理机理是:首先计算“表达式1”的值;再计算“表达式2”的值,若值为“真”则执行循环体一次,否则跳出循环;然后再计算“表达式3”的值,转回第2步重复执行。在整个for循环过程中,“表达式1”只计算一次,作为for的入口语句条件,“表达式2”和“表达式3”则可能计算多次。循环体也可能多次执行,也可能一次都不执行。例如:

voidmain()

{

intn,sum=0;

for(n=0;n<=50;n++)

sum+=n;

}

在使用for语句中有几点要特别注意:①循环体内的处理程序可以为空操作;②for语句的各表达式都可以省,而分号不能省,在省略各表达式时要特别小心分析,防止造成无限死循环。

(2) while语句结构。while形式语法结构有当while形式和直到while形式两种。

当while形式如下:

while(表达式)

处理程序;

处理机理是:计算“表达式”的值,若为“真”则执行循环体的处理程序一次,然后再对表达式进行计算执行,直到表达式的值为“假”时停止循环。循环体也可能多次执行,也可能一次都不执行。例如:

voidmain()

{

intn=0,sum=0;

while(n<50)

{

n+=1;

sum+=n;

}

}

直到循环do-while形式如下:

do

处理程序;

while(表达式);

处理机理是:先执行处理程序,到while时计算“表达式”的值,若为“真”则再次执行循环体的处理程序一次,直到表达式的值为“假”时停止循环。循环体至少会执行一次。例如:

voidmain()

{

intn=0,sum=0;

do

{

n+=1;

sum+=n;

}

while(n<=50);

}8.1.3单片机C程序的数据类型

数据是计算机操作的对象。无论用何种语言、算法进行程序设计,最终在计算机中运行的只有数据流。数据的不同格式称为数据类型。开始单片机C程序的学习之前先应该熟悉它所支持的数据类型。

C语言数据类型包括:基本类型、构造类型、指针类型以及空类型。其中,基本数据类型包括位(bit)、字符(char)、整型(int)、短整型(short)、长整型(long)、浮点型(float)以及双精度浮点型(double);构造类型包括数组(array)、结构体(struct)、共用体(union)以及枚举类型(enum)。对于51系列单片机编程而言,支持的数据类型是和编译器有关的,比如C51编译器中整型(int)和短整型(short)相同,浮点型(float)和双精度浮点型(double)相同。表8-1列出了C51编译器KEILuVision2C51编译器所支持的数据类型。表8-1KEILuVision2C51编译器所支持的数据类型下面来看看它们的具体定义。

1.字符类型char

char类型的长度是一个字节,通常用于定义处理字符类型的变量或常量。它分为无符号字符类型unsignedchar和有符号字符类型signedchar,默认值为signedchar类型。unsignedchar类型用字节中所有位表示数值,可以表达的数值范围是0~(28-1)。signedchar类型用字节中最高位表示数据的符号,“0”表示正数,“1”表示负数,负数用补码表示,可以表达的数值范围是 -27~+(27-1)。

2.整型int

int类型的长度是两个字节,用于存放一个双字节数据,它分为无符号整型unsignedint和有符号整型signedint, 默认值为signedint类型。 unsignedint表示的数值范围是0~216-1。signedint类型用字节中最高位表示数据的符号,“0”表示正数,“1”表示负数,负数用补码表示,可以表达的数值范围是-215~+(215-1)。

3.长整型long

long类型的长度为4个字节,用于存放一个4字节数据,分为无符号长整型unsignedlong和有符号长整型signedlong,默认值为signedlong类型。unsignedlong表示的数值范围是0~232-1。signedlong类型用字节中最高位表示数据的符号,“0”表示正数,“1”表示负数,负数用补码表示,可以表达的数值范围是 -231~+(231-1)。

4.浮点型float

float类型的长度是32位,占用4个字节,与整型数据相比,浮点类型带有小数位,并且可以表示更大范围的数值。它用符号位表示数的符号,用阶码与尾数表示数的大小。浮点型数据在运算时往往有舍入误差,它与该浮点数在计算机内的表示值,可能会有微小差别。

5.指针型*

*(星号)类型本身就是一个变量,在这个变量中存放指向另一个数据的地址。这个指针变量要占据一定的内存单元。对不同的处理器其长度也不尽相同,在C51中,它的长度一般为1~3个字节。

6.位类型bit

bit(位)类型是C51编译器的一种扩充数据类型,利用它可以定义一个位变量,但不能定义位指针,也不能定义位数组。它的值是一个二进制位,不是0就是1,类似一些高级语言中的布尔变量True和False。

7.特殊功能寄存器sfr

sfr类型也是一种扩充数据类型,占一个内存单元,值域为0~255。利用它可以访问51单片机内部的所有特殊功能寄存器。例如:

sfrP0=0x80;//定义P0为P0端口在片内的寄存器,P0

口地址为80H

sfrP1=0x90;//定义P1为P1端口在片内的寄存器,P1

口地址为90H

8051单片机片内有21个特殊功能寄存器,它们分散在片内RAM区的高128字节中,地址为80H~0FFH。对sfr的操作,只能用直接寻址方式。

8.16位特殊功能寄存器sfr16

sfr16类型占用两个内存单元,值域为0~65535。sfr16和sfr一样用于操作特殊功能寄存器,所不同的是它用于操作占两个字节的寄存器,如定时器T0和T1。

9.可寻址位sbit

sbit类型也是C51中的一个扩充类型,利用它可以访问芯片内部的RAM中的可寻址位或特殊功能寄存器中的可寻址位。例如:

sbitP1_1=P1^1;//P1_1为P1中的P1.1引脚

这样在以后的程序语句中就可以用P1_1来对P1.1引脚进行读/写操作。8.1.4单片机C程序的算术表达式

C程序的运算符主要有7种类型。

1.算术运算符和表达式

算术运算符用于各类数值运算,包括加(+)、减(-)、乘(*)、除( / )、求余(或称模运算,%)、自增(++)、自减(--)共7种。用算术运算符和括号将运算对象连接起来的式子称为算术表达式,其中运算对象包括常量、变量、函数、数组、结构等。加法运算符“+”为双目运算符,即应有两个量参与加法运算,如a+b,4+8等。具有左结合性。

减法运算符“-”为双目运算符。但减号“-”也可作负值运算符,此时为单目运算,如 -x,-5等,具有左结合性。

乘法运算符“*”为双目运算符,具有左结合性。

除法运算符“ / ”为双目运算符,具有左结合性。参与运算量均为整型时,结果也为整型,舍去小数。如果运算量中有一个是实型,则结果为双精度实型。

求余运算符(模运算符)“%”为双目运算符,具有左结合性。要求参与运算的量均为整型。求余运算的结果等于两数相除后的余数。自增1运算符记为“++”,其功能是使变量的值自增1。自减1运算符记为“--”,其功能是使变量值自减1。自增1、自减1运算符均为单目运算,都具有右结合性。可有以下几种形式:

++i:i自增1后再参与其他运算;

--i:i自减1后再参与其他运算;

i++:i参与运算后,i的值再自增1;

i--:i参与运算后,i的值再自减1。

在理解和使用上容易出错的是i++和i--,特别是当它们出现在较复杂的表达式或语句中时,常常难于弄清,因此应仔细分析。

2.关系运算符和表达式

关系运算符用于比较运算,包括大于(>)、小于(<)、大于等于(>=)、小于等于(<=)、等于(= =)和不等于(!=)6种。

前4种优先级相同,后两种优先级相同,前4种的优先级又高于后两种。关系运算符的优先级低于算术运算符,但高于赋值运算符。例如:

c>a+b等效于 c>(a+b)

a>b!=c等效于 (a>b)!=c

关系运算符的结合性为左结合。

用关系运算符将两表达式(算术表达式、关系表达式、逻辑表达式等)连接起来的式子,称为关系表达式。关系表达式的结果为逻辑真或假。C语言以1代表真,0代表假。例如:若a=5,b=3,c=0,则:

a>b的值为真,表达式的值为1;

d=a>b,d的值为1;

b+c<a的值为假,表达式的值为0;

e=a>b>c,由于结合性为左结合,故a>b的值为1,而1>c的值为0,因此e的值为0。3.逻辑运算符和表达式

C语言提供了3种逻辑运算符:与运算(&&),或运算(||),非运算(!)。

与运算符“&&”和或运算符“||”均为双目运算符,具有左结合性。非运算符“!”为单目运算符,具有右结合性。非运算符“!”的优先级是这3种中最高的,比算术运算符、关系运算符、与运算符、或运算符及赋值运算符都高。与运算符“&&”和或运算符“||”只比赋值运算符的优先级高。例如:

a>b&&c>d等价于 (a>b)&&(c>d)

!b==c||d<a等价于 ((!b)==c)||(d<a)

a+b>c&&x+y<b等价于

((a+b)>c)&&((x+y)<b)

用逻辑运算符将关系表达式或逻辑量连接起来的式子称为逻辑表达式,逻辑表达式的结合性为自左向右,其值应该是一个逻辑的真或假。逻辑表达式的值和关系表达式的值相同,以1代表真,以0代表假。

例如:若a=8,b=6,则:

因为a=8为真,所以 !a为假(0);

因为a、b均为真,所以a||b为真(1),a&&b为真(1);

因为“!”的优先级高于“&&”,所以 !a&&b为假(0)。

4.位操作运算符和表达式

位操作运算符是用来进行二进制位运算的运算符,包括逻辑位运算符和移位运算符。逻辑位运算符是位与(&)、位或( | )、位取反(~)和位异或(^);移位运算符是位左移(<<)和位右移(>>)。除了位取反(~)是单目运算符,其他位操作运算符均为双目运算符。

位取反(~)用来将二进制数按位取反,即1变0,0变1。位取反(~)运算符优先级比别的算术运算符、关系运算符和其他运算符都高。位与运算符(&)的运算规则如下:参与运算的两个运算对象,若两者相应的位都为1,则该位结果为1,否则为0。

位或运算符( | )的运算规则如下:参与运算的两个运算对象,若两者相应的位都为0,则该位结果值为零,否则为1。

位异或运算符(^)的运算规则如下:参与运算的两个运算对象,若两者相应的位值相同,则结果值为0,若两者相应的位值不同,则结果值为1。

位左移运算符(<<)、位右移运算符(>>)用来将一个数的二进制位全部左移或右移若干位,移位后,空白位补0,而溢出的位舍弃。例如:若a=ABH=10101011B,则:

a=a<<2,将a值左移2位,其结果为10101100B=ACH;

a=a>>2,将a值右移2位,其结果为00101010B=2AH。

5.赋值运算符和表达式

赋值运算符用于赋值运算,分为简单赋值(=)、复合算术赋值(+=,- =,*=,/=,%=)和复合位运算赋值(&=,|=,^=,>>=,<<=)3类共11种。

简单赋值运算符记为“=”,由“=”连接的式子称为赋值表达式。其形式为

变量=表达式

例如:

x=a+b

w=sin(a)+sin(b)

赋值表达式的功能是计算表达式的值再赋予左边的变量。赋值运算符具有右结合性。因此a=b=c=5可理解为a=(b=c=5)。

在其他高级语言中,赋值构成了一个语句,称为赋值语句。而在C中,把等于符号“=”定义为运算符,从而组成赋值表达式。例如,式子x=(a=3)+(b=6)是合法的。它的意义是把3赋予a,6赋予b,再把a、b相加,其和赋予x,故x应等于9。

如果赋值运算符两边的数据类型不相同,则系统自动进行类型转换,即把赋值号右边的类型换成左边的类型。具体规定如下:

(1)实型赋予整型,舍去小数部分。

(2)整型赋予实型,数值不变,但将以浮点形式存放,即增加小数部分(小数部分的值为0)。

(3)字符型赋予整型,由于字符型为一个字节,而整型为两个字节,故将字符的ASCⅡ码值放到整型量的低8位中,高8位为0。

(4)整型赋予字符型,只把低8位赋予字符量。在赋值符“=”之前加上其他二目运行符可构成复合赋值符。如:+=,-=,*=,/=,%=,<<=,>>=,&=,^=,!=。构成复合赋值表达式的一般形式为

变量双目运算符=表达式

它等效于:

变量=变量运算符表达式例如:

a+=5等价于a=a+5;

x*=y+7等价于x=x*(y+7)

r%=p等价于r=r%p。

复合赋值符的这种写法,可能对初学者来说不习惯,但十分有利于编译处理,能提高编译效率并产生质量较高的目标代码。

6.条件运算符

这是一个三目运算符,唯一的三目运算是条件运算,条件运算符是“?:”。条件表达式的形式为

<表达式1>?<表达式2>:<表达式3>

其含义为:若<表达式1>的值为“真”,则条件表达式取<表达式2>的值;否则取<表达式3>的值。

7.指针运算符

指针运算符用于进行取内容(*)和取地址(&)两种运算。C语言中,运算符的运算优先级共分为15级。在表达式中,优先级较高的先于优先级较低的进行运算。而在一个运算量两侧的运算符优先级相同时,则按运算符的结合性所规定的结合方向处理。C语言中各运算符的结合性分为两种,即左结合性(自左向右)和右结合性(自右向左)。例如算术运算符的结合性是自左向右,即先左后右。如表达式x-y+z,则y应先与减号“-”结合,执行x-y运算,然后再执行+z的运算。这种自左向右的结合方向就称为“左结合性”。而自右向左的结合方向称为“右结合性”。最典型的右结合性运算符是赋值运算符。如x=y=z,由于“=”的右结合性,应先执行y=z再执行x=(y=z)运算。C语言运算符中有不少为右结合性,应注意区别,以避免理解错误。8.1.5单片机C程序的一般语法结构

51单片机的C程序的基本程序结构与一般的C语言没什么差别,也是函数定义的集合体,即一个单片机的C语言的程序是由若干个函数组成的,集合中仅有一个名为main的主函数。在这些函数中,main函数是系统执行这个程序的入口,它在整个程序中起着总体控制的作用。在main函数中,可以调用其他函数完成总的设计目标,其他函数也可以互相调用,其中的所有执行语句执行完毕,则程序执行结束。

单片机C语言程序的一般组成结构如下:全局变量声明

main()//主函数

{

局部变量声明

执行语句

}

函数1(形参表)

{

局部变量声明

执行语句

}

函数n(形参表)

{

局部变量声明

执行语句

}

单片机C程序的执行从main()函数开始,调用其他函数后返回到主函数main()中,最后在main()函数中结束整个程序的运行。8.1.6C51语言程序设计

1.KeilC51软件应用

KeilC51是美国KeilSoftware公司出品的51系列兼容单片机C语言软件开发环境,是众多单片机应用开发的优秀软件之一,它集编辑、编译、仿真于一体,支持汇编、PLM语言和C语言的程序设计。KeilC51软件提供丰富的库函数和功能强大的集成开发调试工具,全Windows界面,界面友好,易学易用。

下面介绍KeilC51软件的使用方法。

1)软件启动

进入KeilC51后,先是Keil公司的屏幕显示,几秒钟后出现KeilC51的编辑界面,如图8-4所示。图8-4进入KeilC51后的编辑界面

2)简单程序的调试

学习程序设计语言,学习某种程序软件,最好的方法是直接操作实践。下面通过简单的编程、调试,引导大家学习KeilC51软件的基本使用方法和基本的调试技巧。

(1)建立一个新工程。单击“Project”菜单,在弹出的下拉菜单中选择“NewProject”选项。

(2)然后选择要保存的路径,输入工程文件的名字,比如保存到C51目录里,工程文件的名字为C51,如图8-5所示,然后单击“保存”按钮。图8-5建立新工程

(3)这时会弹出一个对话框,要求选择单片机的型号,你可以根据你使用的单片机来选择。KeilC51几乎支持所有的51核的单片机,这里以大家用得比较多的Atmel的AT89C52来说明,如图8-6所示,选择AT89C52之后,右边栏是对这个单片机的基本说明,然后单击“确定”按钮。图8-6单片机选型对话框

(4)完成上一步骤后,屏幕如图8-7所示。图8-7建立好一个新工程

(5)在图8-7中,单击“File”菜单,再在下拉菜单中单击“New”选项,屏幕上出现如图8-8所示对话框。图8-8建立新文件保存该空白的文件,单击菜单上的“File”,在下拉菜单中选中“SaveAs”选项单击,屏幕如图8-9所示。在“文件名”栏右侧的编辑框中,键入欲使用的文件名,同时,必须键入正确的扩展名。如果用C语言编写程序,则扩展名为(.c);如果用汇编语言编写程序,则扩展名必须为(.asm)。然后,单击“保存”按钮。图8-9保存文件

(6)回到编辑界面后,单击“Target1”前面的“+”号,然后在“SourceGroup1”上单击右键,弹出如图8-10所示的菜单。图8-10添加源文件菜单在弹出的菜单上单击“AddFiletoGroup‘SourceGroup1’”选项,屏幕如图8-11所示。图8-11添加源文件到工作组选中Text1.c,然后单击“Add”按钮,出现如图8-12所示屏幕。

注意到“SourceGroup1”文件夹中多了一个子项“Text1.c”了吗?子项的多少与所增加的源程序的多少相同。图8-12完成文件到工作组的添加

(7)然后,就可以输入如下程序,进行C51语言的编程了。

#include<reg52.h>

//包含文件

#include<stdio.h>

voidmain(void)

//主函数

{

SCON=0x52;

TMOD=0x20;

TH1=0xf3;

TR1=1;

//此行及以上3行为

PRINTF函数所必须

printf("HelloIamKEIL.\n");

//打印程序执行的信息

printf("Iwillbeyourfriend.\n");

while(1);

}图8-12完成文件到工作组的添加

(8)在图8-12中,单击“Project”菜单,再在下拉菜单中单击“BuiltTarget”选项(或者使用快捷键F7),编译成功后,再单击“Project”菜单,在下拉菜单中单击“Start/StopDebugSession”(或者使用快捷键Ctrl+F5),屏幕如图8-13所示。图8-13编译工程

(9)调试程序。在图8-13中,单击“Debug”菜单,在下拉菜单中单击“Go”选项(或者使用快捷键F5);然后再单击“Debug”菜单,在下拉菜单中单击“StopRunning”选项(或者使用快捷键Esc);单击“View”菜单,再在下拉菜单中单击“SerialWindows#1”选项,就可以看到程序运行后的结果,其结果如图8-14所示。图8-14程序调试完成

2.程序设计基本规范

编程规范只是一个规范,也可以不遵守,但是要做一个有良好编程风格的程序员,就一定要遵守编程规范,不仅方便自己以后的阅读,也方便与其他程序员的交流。要做到这一点,程序员应遵循一定的规范并贯穿程序的始终。首要考虑的是程序的可行性、可读性、可移植性、可维护性及可测试性,这是总则。

1)头文件

一个C源文件配置一个h头文件,或者整个项目的C文件配置一个h头文件,而一般采用整个项目的C文件配置一个h头文件的方法。头文件应该按固定的顺序编写,首先使用 #include包含语句编写本头文件中需要包含的其他头文件,其次声明函数原型,接着定义用到的数据常量。定义数据常量使用 #define语句,并且使用 #ifndef/#define/#endif宏来防止重复定义,方便各模块之间相互调用。在每个文件的开头写清楚这个文件归属哪个项目里的哪个模块,是在什么编译环境下编译的,编程者(或修改者)和编程日期。值得注意的是,一定不要忘了编程日期,因为以后再看文件时,会知道大概是什么时候编写的,有些什么功能,并且可以知道类似模块之间的差异。对于一些固定的常量如圆周率PI或者常需要在调试时修改的参数最好用 #define定义,但要注意宏定义只是简单的替换,因此有些括号不可少。

2)变量命名

变量的命名应该能够反映变量的数据类型和含义,最好能做到见名知意。命名必须具有一定的实际意义。例如:

常量的命名:全部用大写。

变量的命名:变量名加前缀,前缀反映变量的数据类型,用小写;反映变量意义的第一个字母大写,其他小写。

函数的命名:函数名首字大写,若包含有两个单词的,则每个单词首字母大写。函数原型说明包括引用外部函数及内部函数,外部引用必须在右侧注明函数来源(模块名及文件名),内部函数只要注释其定义文件名。

3)几条好的书写规范

(1)书写代码时要注意括号对齐,固定缩进。一个{}各占一行,根据个人习惯,缩进4个字符,应该还是比较合适的。if/for/while/do等语句各占一行,执行语句不得紧跟其后。无论执行语句多少都要加{},千万不要写成如下格式:

for(i=0;i<100;i++){fun1();fun2();}

for(i=0;i<100;i++){

fun1();

fun2();

}

而应该写成:

for(i=0;i<100;i++)

{

fun1();

fun2();

}

(2)一行只实现一个功能,比如:

a=2;b=3;c=4;

宜改成:

a=2;

b=3;

c=4;

(3)重要难懂的代码要写注释,每个函数要写注释,每个全局变量要写注释,一些局部变量也要写注释。注释写在代码的上方或者右方,千万不要写在下方。

(4)对各运算符的优先级有所了解,记不清没关系,加括号就是了,不要自作聪明说自己记得很牢。

(5)不管有没有无效分支,switch函数一定要default这个分支。一来让阅读者知道程序员并没有遗忘default,并且防止程序运行过程中出现的意外(健壮性)。

(6)函数的参数和返回值没有的话最好使用void。

(7)goto语句。从汇编转型成C的人很喜欢用goto,但goto是C语言的大忌。但是老实说,程序出错是程序员自己造成的,不是goto的过错;最好只在一种情况下使用goto语句,即从多层循环体中跳出时。

(8)指针是C语言的精华,但是在C51中少用为妙。一来有时反而要花费更多的空间,二来在对片外数据进行操作时会出错(可能是时序的问题)。

9)当项目比较大时,最好分模块编程,一个模块一个程序,既方便修改,也便于重用和阅读。

(10)程序应该能方便地进行测试,其实这也与编程的思维有关。程序的编程方式一般有3种:一种是自上而下先整体再局部;一种是自下而上先局部再整体;还有一种是结合两者往中间凑。通常情况下,可以先大概规划一下整个编程,然后一个模块一个模块独立编程,每个模块调试成功再拼凑在一块调试。另外,如果程序不大,可以用一个文件直接编定;如果程序很大,宜采用自上而下的方式;但更多的是那种处在中间的情况,宜采用自下而上或者结合的方式。

4)编辑风格

(1)缩进。缩进以Tab为单位,一个Tab为4个空格大小。预处理语句、全局数据、函数原型、标题、附加说明、函数说明、标号等均顶格书写。语句块的“{”“}”配对对齐,并与其前一行对齐。

(2)空格。数据和函数在其类型、修饰名称之间适当留空格并依据情况对齐。关键字原则上空一格,如:if(...)等,运算符的空格规定如下:“->”、“[”、“]”、“++”、“-”、“~”、“!”、“+”、“-”(指正负号),“&”(取址或引用)、“*”(指使用指针时)等几个运算符两边不空格(其中单目运算符系指与操作数相连的一边),其他运算符(包括大多数二目运算符和三目运算符“?:”两边均空一格,“(”、“)”运算符在其内侧空一格,在作函数定义时还可依据情况多空或不空格来对齐,但在函数实现时可以不用。“,”运算符只在其后空一格,需对齐时也可不空或多空格,对语句行后加的注释应用适当空格与语句隔开并尽可能对齐。

(3)对齐。原则上关系密切的行应对齐,对齐包括类型、修饰、名称、参数等各部分对齐。每一行的长度不应超过屏幕太多,必要时适当换行,换行时尽可能在“,”处或运算符处,换行后最好以运算符打头,并且以下各行均以该语句首行缩进,但该语句仍以首行的缩进为准,即如其下一行为“{”,应与首行对齐。

(4)空行。程序文件结构各部分之间空两行,若不必要也可只空一行,各函数实现之间一般空两行。

(5)修改。版本封存以后的修改一定要将老语句用 /**/ 封闭,不能自行删除或修改,并要在文件及函数的修改记录中加以记录。

(6)形参。在定义函数时,在函数名后面括号中直接进行形式参数说明,不再另行说明。

8.2采用C51程序的设计实例

8.2.18×8点阵LED显示屏的设计

1.设计要求

在8 × 8点阵LED上显示柱形,让其先从左到右平滑移动3次,其次从右到左平滑移动3次,再次从上到下平滑移动3次,最后从下到上平滑移动3次,如此循环下去。

2.电路原理图

电路原理图如图8-15所示。图8-158×8点阵LED显示原理图

3.硬件电路连线

(1)把“单片机系统”区域中的P1端口用8芯排线连接到“点阵模块”区域中的“DR1~DR8”端口上。

(2)把“单片机系统”区域中的P3端口用8芯排线连接到“点阵模块”区域中的“DC1~DC8”端口上。

4.程序设计内容

8×8点阵共需要64个发光二极管,且每个发光二极管是放置在行线和列线的交叉点上。当对应的某一列置1电平,某一行置0电平,则相应的二极管就亮。因此要实现一根柱形的亮法,对应的一列为一根竖柱,或者对应的一行为一根横柱,则实现柱形的亮法如下

所述:

一根竖柱:对应的列置1,而行则采用扫描的方法来实现。

一根横柱:对应的行置0,而列则采用扫描的方法来实现。

5.C语言源程序

#include<AT89S51.H>

//定义字形字位码

unsignedcharcodetaba[]={0xfe,0xfd,0xfb,0xf7,0xef,

0xdf,0xbf,0x7f};

unsignedcharcodetabb[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};

voiddelay(void)

{

unsignedchari,j;

for(i=10;i>0;i--)

for(j=248;j>0;j--);

}

voiddelay1(void)

{

unsignedchari,j,k;

for(k=10;k>0;k--)

for(i=20;i>0;i--)

for(j=248;j>0;j--);

}

voidmain(void)

{

unsignedchari,j;

while(1)

{

for(j=0;j<3;j++)

//fromlefttoright3time

{

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

{

P3=taba[i];

P1=0xff;

delay1();

}

}

for(j=0;j<3;j++)

//fromtoptobottom3time

{

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

{

P3=0x00;

P1=tabb[7-i];

delay1();

}

}for(j=0;j<3;j++)

//fromtoptobottom3time

{

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

{

P3=0x00;

P1=tabb[7-i];

delay1();

}

}for(j=0;j<3;j++)

//frombottomtotop3time

{

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

{

P3=0x00;

P1=tabb[i];

delay1();

}

}

}

}8.2.2数字电压表的设计

1.设计要求

利用单片机AT89S51与ADC0809设计一个数字电压表,能够测量0~5V之间的直流电压值,4位数码显示,但要求使用的元器件数目最少。

2.电路原理图

数字电压表的原理图如图8-16所示。图8-16数字电压表电路原理图

3.系统板上硬件连线

(1)把“单片机系统”区域中的P1.0~P1.7与“动态数码显示”区域中的a、b、c、d、e、f、g、dp端口用8芯排线连接。

(2)把“单片机系统”区域中的P2.0~P2.3与“动态数码显示”区域中的S1、S2、S3、S4端口用8芯排线连接。

(3)把“单片机系统”区域中的P3.0与“模数转换模块”区域中的START端子用导线相连接。

(4)把“单片机系统”区域中的P3.1与“模数转换模块”区域中的ENABLE端子用导线相连接。

(5)把“单片机系统”区域中的P3.2与“模数转换模块”区域中的EOC端子用导线相连接。

(6)把“单片机系统”区域中的P3.3与“模数转换模块”区域中的CLOCK端子用导线相连接。

(7)把“模数转换模块”区域中的ADDA、ADDB、ADDC端子用导线连接到“电源模块”区域中的GND端子上。

(8)把“模数转换模块”区域中的IN0端子用导线连接到“三路可调电压模块”区域中的VR1端子上。

(9)把“单片机系统”区域中的P0.0~P0.7用8芯排线连接到“模数转换模块”区域中的2-1、2-2、2-3、2-4、2-5、2-6、2-7、2-8端子上。

4.程序设计内容

(1)由于ADC0809

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论