AVR学习笔记十四、模块化程序设计2.docx
AVR学习笔记十四、模块化程序设计(二)14.1程序模块化设计(二)14.1 .K实例功能在前面一讲里,我们已经初步了解了模块化程序设计的简单知识,今天我们再深入探讨一下模块化程序设计.我们先回忆一下前面,讲的主要内容:模块化程序设计是指将实现同一功能的程序整合起来,时装到一个程序模块中.这样在使用该功能的时候,可以直.接调用该模块中的相关函数进行操作。我们在单片机编程中经常用到的按段检测、液晶显示、数眄管显示.串1.J通信、DS18B20温友检测、DS1302实时时仲等经常用到的程序都可以实现模块化.模块化编程的增大优点是:思路清晰、移植方便、程序简化,通常的做法是将某一模块中所有用到的瑞定义,函数声明,函数定义等一起写到一个“h"或者”.C.文件中.然后在主程序里面使用*inc1.ude预编译指令将这些内容包含到主程序中.这样做调用坦然简单.但是对于模块化程序设计来说还是显得有些不太直观因为我们谓用模块中的函数的时帔,需要在这一个模块文件中很费力的找出需要的函数,同样对于移植到别的单片机系统中的时候,需要遍历该模块文件,然后一一更改。如果不小心,很容易造成楷谈.更为标准的做法是我们将某一个功能模块的端口定义,函数声明这些内容放在一个"h"文件中而把具体的函数实现(执行具体操作的函数放在一个tt-c-文件中.这样我们在编写主程序文件的时候,可以直接使用“"inc1.ude"预编译指令将“.h”文件包含迸主程序文件中,而在埸译的时候将“.c”文件和主程序文件一起8»译.这样做的优点是,我们可以点接在“文件中查找到我们需要的函数名称.从而在主程序里面直接调用.而不用去关心Jc”文件中的具体内容.如果我们要粗该程序移位到不同型号的单片机上,我们同样只需在“.h"文件中改相应的端口定义即可。在这一讲中,我们继续学习一下模块化程序设计.本实例有两个功能模块: 继续了解模块化程序设计的思路和方法。 编用申口通信的模块化程序。通过本实例的学习,掌握以下知识点: 初步掌握模块化程序设计的方法. 了解模块化程序设计中需要注意的一些问题14.2 模块化程序设计需要注意的两点14.2.1 模块化程序设计中的史复声明在编写蟆块程序的过程中,我们在编写具体函数实现的“.c”文件时,需要调用包含相关的端口定义和函数声明的”.h文件,调用时,我们使用使用街nc1.udc”预编译指令来调用”.h文件。同样的,在姐写主程序文件时,我们仍然需要将需要调用模块的“h"文件包含到主程序中,这样就出现了一个单片机系统程序中同一个文件被展次网用的情况,这在很多编洋系统中进行编译的时候会有“某某.h"文件被史友调用的编译警告或错误提示。事实上,再同一个单片机系统程序中,在编译的时候出现fE何的警告或者错误提示都表示我们的程序编写的有问遨.虽然有些警告信息不会影响程序的最终运行结果,但我们希望编译程序的时候不出现任何的警告或者错误提示.那这种Hi复调用的情况该怎么防止呢?一种解决方法是,当我们在调用某一个程序文件时,先判断一下在前面的程序里面是否已经定义或者调用了这个文件,如果这个文件没有被定义或者询用,那么我们就执行调用指令.否则我们就略过调用指令.要实现这种判断.我们需要用到条件预编译指令.'Sifndef',FdefinJ,“tJondif.这三条指令使用格式如下:RifndefXXXKX语句块1defineXXXXXX语句块2endif具体作用是:如果没有定义文件XXXXXX以及语句块1:3ifndefxxxxx(该处Ur以为文件名)语句块1定义文件xxxxxx以及i?1.句块2UdefineXXXXXX语句块2结束文件XXXXXX以及语句块的定义Sendif举例如下;下面是文件"usari.h”的防止正复定义的书用方法,具体作用姑,先判断是否认义了文件“USar1.h",如果没有定义Uifndef)文件“USarI.h",则定义文件“usar1.h",并且填写文件“usar1.h.的具体内容(即需要定义的内容):填写完露要定义的内容后.使用肥ndif结束定义.#ifndef_USARTJI_如果usart.h没有定义defineUSARTI1.则定义USar1.h/下面是USmth的内容#inc1.ude<avrio.h>#inc1.ude<avrir11crrut.h>中断函数头文件常艮声明UdefineRAUD9600函数声明voidPorj1.nit(void);珊口初始化配置voidUsartInit(VOid);/USART存放器设置voidUSar1.pUtChar(UnSignCdcharCTXDaIa);字节发送函数void1.srtPUtS1.rinR(UnSiRn«1charpcStri11):/USart.h的内容写完后.结束条件编洋Rendif结束判断条件指示符可以防止头文件的值复处理.标准格式如下#define表达式Wifndef表达式执行语句1e1.se执行语句2Wenddef意思:在执行#ifdef时。如果在前面使用define定义了“表达式”的话就执行“执行语句K否则执行“执行语句2"例如:#inc1.ude<iostream.h>SdefineDEBUGvoidmain()(#ifdefDEBUGcout<<"runit!"<<end1.:e1.secout<<*notrunit!*<<end1.;ttendif)在上面的例子中运行结果为runit!如果前面没有定义:#defineDEBUG则运行的结果是:notrunit!÷作用:是为JZ防止重史定义包含的文件重划时,就用的上/般使用的格式:UifndefNAMEWefineNAME执行语句4e1.se执行语句Sendif14.2.2不同模块之间的函数调用我们知道,一般情况下.我的定义的函数和变量是有一定的作用域的,也就是说我们在一个模块中定义的变麻和函数.它的作用于只限于本模块文件和调用它的程序文件范围内.而在没有喝用它的模块程序里面.它的函数是不能被使用的。在端耳模块化程序的时候,我们经常会遇到一种侍况,即:一个函数在不同的模块之间都会用到,最常见的就是越时函数.一般的程序中都需要调用延时函数.出现这种情况该怎么办?难道需要在每个模块中都定义相同的函数?那程序编译的时候会提示我们有重短定义的函数.加我们只好在不同的模块中为相同功能的函数起不同的名字,这样岂不是做了很多31双劳动,这样的立或劳动还会造成程序的可读性变得很差。怎么办?同样的情况也会出现在不同模块程序之间传递数据变量的时候。在这样的情况下,一种解决方法是:使用文件包含命令"inc1.ude”招一个模块的文件包含到另一个模块文件中,这种方法在只包含很少的模块文件的时候是很方便的,对于比较大的、很红杂的包含很多模块文件的东片机应用程序中,在好一个模块里面都是用包含命令就很麻烦了,并且很容易出错,出现这种情况的原因是我们在编写单片机程序的时候,我们所定义的函数和变小都被俄认为是局部函数和变fit那么它们的作用范国当然是在调用他们的程序之间了.如果我的将这些函数和变及定义为全局的函数和变址.那么,在整个单片机系统程序中,所有的模块之间都可以使用这些函数和变麻.好的解决方法是:将需要在不利模块之间互相调用的文件声明为外部函数、变量(或者全局函数、变量).将函数和变业声明为全局函数和变量的方法是:在该函数和变诉前面加“exicrn,修饰符。“extern"的英文意思就是“全局”,这样我们就可以将加了“extern"修饰符的函数和变址声明为全局函数和变Sb那么在整个胞片机系统程序的任何地方,我们怖可以随意网用这些全局函数和变1九例如:externvoidPortOInit(void);埼U初始化限置externvoidUsartOInit(void):/USART存放JiS设置externvoidUsartOPutChar(unsignedcharcTXDa1.a):/字节发送函数externvoidUSarI(1.PU1.SCring(UnSignedchar*cString):在这里,我们将这4个函数都定义成为全局函数,那么,在一个单片机系统中,在整个程序的任何地方,我们都可以真接网用这写函数.注在提用的之后宣控/用函数名称即可,extern这个假饰符不必再调用的时候写上.M样的,对于变址的定义我归可以使用同样的方法:externintsecond:在这里,我们定义广-个全局的整形变出SeCorKh14.3串11通信模块化程序的实现1) usart.h文件的编写在这里,我们使用了条件漏译指令,对于函数,我In使用“cxienJ关键字,将函数声明为全局函数*jM*1*Mt*M;*林*1»*AT峡ga1.6弁步申行通信模块化头文件,usar(.h变埴以及函数声明ffifndef_USARTI1.Udefine_USARTi1.ttinc1.ude<avrio.h>Hinc1.ude<avrinterrupi.h>中断函数头文件/常显声明defineBAUD9600函数声明externvoidPortInit(void):/端口初始化配JRexternvoidUsartInit(void);/tSART存放器设JSexternvoidUsartPutChartunsignedcharCTXDH1.a);字节发送函数externvoidUsartJ1UtString(unsignedchar*pcString);Itendif2) usnrt.c文件的编写ATMgaI6弁步申行通信模块化头文件,usart.c函数定义局部*g*g*g*g*,inc1.ude<avrio.h>Sinc1.ude*usart.h*端门状态初始化设置函数voidPortInitOPORTD=0X00;/USART的发送接收端门分别为PDO和PD1.DDRDI=(1«FDD:/PDO为接收端口,网为输入口;PDI为发送端口,跟为输出口)USART存放器配置函数voidUsarjIniiO1.CSRA=0X00:ICSRC=(1.<<URSEI.(I«1.CSZ1.)(1«ICSZO):/弁步,数据楙式8,N,I/UCSRC存放器与UBRRH存放器共用相同的1/0地址,写ICSRCW,URSE1.应设置为1.IBRR1.(FCPU/BAuD/16-I)%256:波特军设置IBRRH=(F_CPU/IJACD/16-1)/256:ICSRBI=(1«RXCIE)(1«RXEN)(1«TXEN);发送使徒字节发送函数voidUsartPutChar(unsignedcharCTXData)whi1.e(!(UCSRA&(1«CDRE):只有数据存放器为空时才能发送数据IDR=CTXOaia;发送数据送USARTI/O数据存放港一UDR接收中断函数ISRdSARTRXCvect)unsignedcharRev:Rev=UDR:从“ARTI/O数据存放器一UDR中读出数据“arJPuiChaNRe*;/将接收到的数据发送voidUsartPUtS1.rinR(UnSiRrWdchar*pcString)whiIe(*cString)(Isnrt1.,utChar(*pcStrinR);1.sart_PuiChar(OxOD);IarjPu1.Char(OxOA);结尾发送回车换行3)主程序文件的编写,次才*含才杆*衰*朴*林*林姓AYRUSARn发送字符件及接收数据模块化设计的例“择 *MCU:ATitega16 *作者Xmawei1.i”林秘编译涔:W1.NAVK和今* *2009.1.13*)M(*>A*>*,*4(*"*/inc1.ude<avio.h>Uinc1.ude<uti1./de1.ay.h>"inc1.ude<avrinterrupt.h>中断函数头文件inc1.ude*usart.h*intmain(void)Port_1.nitO;1.,sartInit():UsarJpUtString("恭喜你!马伟力”);UsartPUIStringr你的中门程序调试成功了!”):sci:使能全局中断WhiIe