AVR学习笔记十四模块化程序设计.docx
- 文档编号:14614295
- 上传时间:2023-06-25
- 格式:DOCX
- 页数:10
- 大小:18.46KB
AVR学习笔记十四模块化程序设计.docx
《AVR学习笔记十四模块化程序设计.docx》由会员分享,可在线阅读,更多相关《AVR学习笔记十四模块化程序设计.docx(10页珍藏版)》请在冰点文库上搜索。
AVR学习笔记十四模块化程序设计
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的内容
#include
#include
//常量声明
#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”
例如:
#include
#defineDEBUG
voidmain()
{
#ifdefDEBUG
cout<<"runit!
"< #else cout<<"notrunit! "< #endif } 在上面的例子中运行结果为runit! 如果前面没有定义: #defineDEBUG 则运行的结果是: notrunit! ÷ 作用: 是为了防止重复定义包含的文件重复时,就用的上了 一般使用的格式: #ifndefNAME #defineNAME 执行语句 #else 执行语句 #endif 14.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__ #include #include //常量声明 #defineBAUD9600 //函数声明 externvoidPort_Init(void);//端口初始化配置 externvoidUsart_Init(void);//USART寄存器设置 externvoidUsart_PutChar(unsignedcharcTXData);//字节发送函数 externvoidUsart_PutString(unsignedchar*pcString); #endif 2)usart.c文件的编写 /************************************************* ATmega16异步串行通信模块化头文件, usart.c 函数定义部分 **********************************************/ #include #include"usart.h" //端口状态初始化设置函数 voidPort_Init() { PORTD=0X00;//USART的发送接收端口分别为PD0和PD1 DDRD|=(1< } //USART寄存器配置函数 voidUsart_Init() { UCSRA=0X00; UCSRC|=(1< //UCSRC寄存器与UBRRH寄存器共用相同的I/O地址,写UCSRC时,URSEL应设置为1。 UBRRL=(F_CPU/BAUD/16-1)%256;//波特率设置 UBRRH=(F_CPU/BAUD/16-1)/256; UCSRB|=(1< } //字节发送函数 voidUsart_PutChar(unsignedcharcTXData) { while(! (UCSRA&(1< 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)主程序文件的编写 /*********************************************** ****AVRUSART1发送字符串及接收数据模块化设计范例*** ****MCU: ATmega16*** ****作者: maweili*** ****编译器: WINAVR*** ******* ****2009.1.13*** ***********************************************/ #include #include #include #include"usart.h" intmain(void) { Port_Init(); Usart_Init(); Usart_PutString("恭喜你! 马伟力"); Usart_PutString("你的串口程序调试成功了! "); sei();//使能全局中断 while (1) { } }
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- AVR 学习 笔记 十四 模块化 程序设计