模块化编程是指将一个庞大的程序划分为若干个功能独立的模块,对各个模块进行独立开发,然后再将这些模块统一合并为一个完整的程序。这是C语言面向过程的编程方法,可以缩短开发周期,提高程序的可读性和可维护性。
在单片机程序里,程序比较小或者功能比较简单的时候,我们不需要采用模块化编程,但是,当程序功能复杂、涉及的资源较多的时候,模块化编程就能体现它的优越性了。如前面我们写过的HT1380驱动程序、独立按键扫描程序和12864程序,每一个程序都是只用一个源文件编写就能完成,但是,当您制作一个12864液晶日历的时候,需要用到HT1380驱动程序、独立按键扫描程序和12864显示程序,如果把这三个程序全部集中在一个源文件里,将导致主体程序臃肿且杂乱,这样做并非不可取,只是降低了程序可读性、可维护性和代码的重用率。如果把这三个程序当做三个独立的模块放到你的主体工程进行模块化编程,效果就不一样了。
实际上,模块化编程就是模块合并的过程,就是建立每个模块的头文件和源文件并将其加入到主体程序的过程。主体程序调用模块的函数是通过包含模块的头文件来实现,模块的头文件和源文件是模块密不可分的两个部分,缺一不可。所以,模块化编程必须提供每个模块的头文件和源文件。下面我们以一个简单的例子(主体程序实现指示灯闪烁,延时模块实现延时功能)来演示模块化编程。
一.建立模块的源文件
在模块化编程里,模块的源文件是实现该模块功能的的变量定义和函数定义,不能定义main函数。建立模块源文件方法很多,直接在主体工程里新建一个文件,把代码添加进去,保存为.c的文件,然后将该文件加入到主体工程;或者是在工程外建立一个记事本文件,把代码添加进去,保存为.c的文件,然后把文件添加到主体工程里。在这里,我们把延时代码放在一个新建的记事本文件里并保存为“delay.c”,延时模块的源文件就生成了。当然了,这个源文件很简单,里面只有两个延时函数。
//delay.c文件内容:
//延时微妙函数:
void Delayus(unsigned char t)
{ //此函数精确计算:18+6*(t-1)=延时时间(us)
while(t--);
}
// 延时毫秒函数:
void Delayms(unsigned int t)
{
unsigned int i,j;
for(i=t;i>0;i--)
for(j=0;j<120;j++);
}
二.建立模块的头文件
模块的头文件就是模块和主体程序的接口,里面是模块源文件的函数声明。建立头文件的方法也和建立源文件类似,只是在保存的时候把文件保存为.h的文件。在这里,我们新建一个文本文件,把延时模块的源文件里的两个函数声明放到文件里并保存为“delay.h”。延时模块的头文件也就生成了。每个模块的头文件最终都要被包含在主体程序里,而且不能重复包含,否则编译器报错,所以在每个模块的头文件里要做一些处理,以防止头文件重复包含。
//delay.h文件内容:
#ifndef _DELAY_H_
#define _DELAY_H_
extern void Delayus(unsigned int t); //延时微妙函数
extern void Delayms(unsigned int t); //延时毫秒函数
#endif
在头文件“delay.h”里,#ifndef …#endif为预编译指令,作用是避免重复包含。头文件的开头两句:“#ifndef _DELAY_H_ ”和“#define _DELAY_H_”,意思是:如果没有定义“_DELAY_H_”,就定义“_DELAY_H_”。“_DELAY_H_”是这个头文件的标识符,名字可以任意取,但通常的写法是将头文件名改成大写,把点改成下划线,首尾再加下划线组成,一看就知道是头文件的标示符。接下来就是定义头文件的内容,也就是列出函数声明,在函数声明的前面最好加上extern 关键字,有些C编译器需要加上这个关键字才能通过编译,而在keil软件的C编译器,extern关键字加与不加都一样。头文件的最后应该是结束编译指令#endif。
三.在主体程序里包含模块的头文件
建立一个指示灯闪烁的主体工程,把上面的两个文件“delay.h”和“delay.c”分别添加到工程里。为了体现“主体”和区别于模块,把主体工程的源文件命名为“main.c”,
//main.c文件内容:
#include<reg52.h>
#include”delay.h” //包含延时模块的头文件
sbit led=P2^0;//定义指示灯
int main(void)
{
while(1)
{
led=0;//点亮led
delayms(500);//调用延时模块的函数
led=1;//熄灭led
delayms(500);//调用延时模块的函数
}
return 0;
}
在包含模块头文件时,可以写成#include”delay.h”也可以写成#include<delay.h>。这里使用双引号和尖括号的区别是程序编译时编译器查找头文件的开始位置不同。用双引号括起来的头文件,编译时从工程文件夹开始查找;用尖括号括起来的头文件,编译时从编译器安装文件夹开始查找。如果头文件是放在工程文件夹里,我们通常写成双引号的形式;如果头文件是在编译器安装文件夹里,我们通常写成尖括号的形式。这就能保证编译速度。
有些程序员为了省事,只建立模块的源文件,不建立头文件,在主体工程里直接将源文件包含进来,笔者建议大家不要采取这样的做法,这是一种不符合C语言标准的做法,是一种冒险方法。只包含一个源文件可能没事,同时包含多个源文件的时候问题就会出现 |