AVR学习笔记十四、模块化程序设计2

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

AVR学习笔记十四、模块化程序设计(二)14.1程序模块化设计(二)14.1.1、实例功能在前面一讲里,我们已经初步了解了模块化程序设计的简单知识,今天我们再深入探讨一下模块化程序设计。我们先回顾一下前面一讲的主要内容:模块化程序设计是指将实现同一功能的程序整合起来,封装到一个程序模块中,这样在使用该功能的时候,可以直接调用该模块中的相关函数进行操作。我们在单片机编程中经常用到的按键检测、液晶显示、数码管显示、串口通信、DS18B20温度检测、DS1302实时时钟等经常用到的程序都可以实现模块化。模块化编程的最大优点是:思路清晰、移植方便、程序简化。通常的做法是将某一模块中所有用到的端口定义,函数声明,函数定义等一起写到一个“.h”或者“.c”文件中,然后在主程序里面使用“#include”预编译指令将这些内容包含到主程序中,这样做调用虽然简单,但是对于模块化程序设计来说还是显得有些不太直观,因为我们调用模块中的函数的时候,需要在这一个模块文件中很费劲的找出需要的函数。同样对于移植到别的单片机系统中的时候,需要遍历该模块文件,然后一一更改。如果不小心,很容易造成错误。更为标准的做法是我们将某一个功能模块的端口定义,函数声明这些内容放在一个“.h”文件中,而把具体的函数实现(执行具体操作的函数)放在一个“.c”文件中。这样我们在编写主程序文件的时候,可以直接使用“#include”预编译指令将“.h”文件包含进主程序文件中,而在编译的时候将“.c”文件和主程序文件一起编译。这样做的优点是,我们可以直接在“.h”文件中查找到我们需要的函数名称,从而在主程序里面直接调用,而不用去关心“.c”文件中的具体内容。如果我们要将该程序移植到不同型号的单片机上,我们同样只需在“.h”文件中修改相应的端口定义即可。在这一讲中,我们继续学习一下模块化程序设计。本实例有两个功能模块:●继续了解模块化程序设计的思路和方法。●编写串口通信的模块化程序。通过本实例的学习,掌握以下知识点:●初步掌握模块化程序设计的方法。●了解模块化程序设计中需要注意的一些问题14.2模块化程序设计需要注意的两点14.2.1模块化程序设计中的重复声明在编写模块程序的过程中,我们在编写具体函数实现的“.c”文件时,需要调用包含相关的端口定义和函数声明的“.h”文件,调用时,我们使用使用“#include”预编译指令来调用“.h”文件。同样的,在编写主程序文件时,我们仍然需要将需要调用模块的“.h”文件包含到主程序中,这样就出现了一个单片机系统程序中同一个文件被多次调用的情况,这在很多编译系统中进行编译的时候会有“某某.h”文件被重复调用的编译警告或错误提示。事实上,再同一个单片机系统程序中,在编译的时候出现任何的警告或者错误提示都表示我们的程序编写的有问题,虽然有些警告信息不会影响程序的最终运行结果,但我们希望编译程序的时候不出现任何的警告或者错误提示。那这种重复调用的情况该怎么避免呢?一种解决方法是,当我们在调用某一个程序文件时,先判断一下在前面的程序里面是否已经定义或者调用了这个文件,如果这个文件没有被定义或者调用,那么我们就执行调用指令,否则我们就略过调用指令。要实现这种判断,我们需要用到条件预编译指令。“#ifndef”,“#define”,“#endif”这三条指令使用格式如下:#ifndefxxxxx语句块1#definexxxxxx语句块2#endif具体作用是://如果没有定义文件xxxxxx以及语句块1;#ifndefxxxxx(该处可以为文件名)语句块1//定义文件xxxxxx以及语句块2#definexxxxxx语句块2//结束文件xxxxxx以及语句块的定义#endif举例如下:下面是文件“usart.h”的避免重复定义的书写方法,具体作用是,先判断是否定义了文件“usart.h”,如果没有定义(#ifndef)文件“usart.h”,则定义文件“usart.h”,并且填写文件“usart.h”的具体内容(即需要定义的内容);填写完需要定义的内容后,使用#endif结束定义。#ifndef__USART_H__//如果usart.h没有定义#define__USART_H__//则定义usart.h//下面是usart.h的内容#includeavr/io.h#includeavr/interrupt.h//中断函数头文件//常量声明#defineBAUD9600//函数声明voidPort_Init(void);//端口初始化配置voidUsart_Init(void);//USART寄存器设置voidUsart_PutChar(unsignedcharcTXData);//字节发送函数voidUsart_PutString(unsignedchar*pcString);//usart.h的内容写完后,结束条件编译#endif//结束判断条件指示符可以防止头文件的重复处理。标准格式如下#define表达式#ifndef表达式执行语句1#else执行语句2#enddef意思:在执行#ifdef时。。如果在前面使用define定义了“表达式”的话就执行“执行语句1”否则执行“执行语句2”例如:#includeiostream.h#defineDEBUGvoidmain(){#ifdefDEBUGcoutrunit!endl;#elsecoutnotrunit!endl;#endif}在上面的例子中运行结果为runit!如果前面没有定义:#defineDEBUG则运行的结果是:notrunit!÷作用:是为了防止重复定义包含的文件重复时,就用的上了一般使用的格式:#ifndefNAME#defineNAME执行语句#else执行语句#endif14.2.2不同模块之间的函数调用我们知道,一般情况下,我们定义的函数和变量是有一定的作用域的,也就是说,我们在一个模块中定义的变量和函数,它的作用于只限于本模块文件和调用它的程序文件范围内,而在没有调用它的模块程序里面,它的函数是不能被使用的。在编写模块化程序的时候,我们经常会遇到一种情况,即:一个函数在不同的模块之间都会用到,最常见的就是延时函数,一般的程序中都需要调用延时函数。出现这种情况该怎么办?难道需要在每个模块中都定义相同的函数?那程序编译的时候会提示我们有重复定义的函数。那我们只好在不同的模块中为相同功能的函数起不同的名字,这样岂不是做了很多重复劳动,这样的重复劳动还会造成程序的可读性变得很差。怎么办?同样的情况也会出现在不同模块程序之间传递数据变量的时候。在这样的情况下,一种解决办法是:使用文件包含命令“#include”将一个模块的文件包含到另一个模块文件中,这种方法在只包含很少的模块文件的时候是很方便的,对于比较大的、很复杂的包含很多模块文件的单片机应用程序中,在每一个模块里面都是用包含命令就很麻烦了,并且很容易出错。出现这种情况的原因是我们在编写单片机程序的时候,我们所定义的函数和变量都被默认为是局部函数和变量,那么它们的作用范围当然是在调用他们的程序之间了。如果我们将这些函数和变量定义为全局的函数和变量,那么,在整个单片机系统程序中,所有的模块之间都可以使用这些函数和变量。最好的解决方法是:将需要在不同模块之间互相调用的文件声明为外部函数、变量(或者全局函数、变量)。将函数和变量声明为全局函数和变量的方法是:在该函数和变量前面加“extern”修饰符。“extern”的英文意思就是“全局”,这样我们就可以将加了“extern”修饰符的函数和变量声明为全局函数和变量,那么在整个单片机系统程序的任何地方,我们都可以随意调用这些全局函数和变量。例如:externvoidPort0_Init(void);//端口初始化配置externvoidUsart0_Init(void);//USART寄存器设置externvoidUsart0_PutChar(unsignedcharcTXData);//字节发送函数externvoidUsart0_PutString(unsignedchar*pcString);在这里,我们将这4个函数都定义成为全局函数,那么,在一个单片机系统中,在整个程序的任何地方,我们都可以直接调用这写函数。注意:在调用的之后直接调用函数名称即可,“extern”这个修饰符不必再调用的时候写上。同样的,对于变量的定义我们可以使用同样的方法:externintsecond;在这里,我们定义了一个全局的整形变量second;14.3串口通信模块化程序的实现1)usart.h文件的编写在这里,我们使用了条件编译指令,对于函数,我们使用“extern”关键字,将函数声明为全局函数/*************************************************ATmega16异步串行通信模块化头文件,usart.h变量以及函数声明**********************************************/#ifndef__USART_H__#define__USART_H__#includeavr/io.h#includeavr/interrupt.h//中断函数头文件//常量声明#defineBAUD9600//函数声明externvoidPort_Init(void);//端口初始化配置externvoidUsart_Init(void);//USART寄存器设置externvoidUsart_PutChar(unsignedcharcTXData);//字节发送函数externvoidUsart_PutString(unsignedchar*pcString);#endif2)usart.c文件的编写/*************************************************ATmega16异步串行通信模块化头文件,usart.c函数定义部分**********************************************/#includeavr/io.h#includeusart.h//端口状态初始化设置函数voidPort_Init(){PORTD=0X00;//USART的发送接收端口分别为PD0和PD1DDRD|=(1PD1);//PD0为接收端口,置为输入口;PD1为发送端口,置为输出口}//USART寄存器配置函数voidUsart_Init(){UCSRA=0X00;UCSRC|=(1URSEL)|(1UCSZ1)|(1UCSZ0);//异步,数据格式8,N,1//UCSRC寄存器与UBRRH寄存器共用相同的I/O地址,写UCSRC时,URSEL应设置为1。UBRRL=(F_CPU/BAUD/16-1)%256;//波特率设置UBRRH=(F_CPU/BAUD/16-1)/256;UCSRB|=(1RXCIE)|(1RXEN)|(1TXEN);//发送使能}//字节发送函数voidUsart_PutChar(unsignedcharcTXData){while(!(UCSRA&(1UDRE)));//只有数据寄存器为空时才能发送数据UDR=cTXData;//发送数据送USARTI/O数据寄存器-UDR}//接收中断函数ISR(USART_RXC_vect){unsignedcharRev;Rev=UDR;//从USARTI/O数据寄存器-UDR中读出数据Usart_PutChar(Rev);//将接收到的数据发送}voidUsart_PutString(unsignedchar*pcString){while(*pcString){Usart_PutChar(*pcString++);}Usart_PutChar(0x0D);Usart_PutChar(0x0A);//结尾发送回车换行}3)主程序文件的编写/****

1 / 8
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功