开场白:
许多人也把多文件编程称作模块化编程,其实我觉得叫多文件编程会愈加符合实际一些。多文件编程有两个最大的优点,一个是给咱们的程序增加了目录,便利咱们查找。别的一个优点是便利移植他人现已做好的功用程序模块,使用这个特色,特别合适团队一同做大型项目。许多初学者刚开端学多文件编程时,会常常遇到重复界说等问题,想知道怎样处理这些问题吗?只需依照以下鸿哥教的规矩来做,这些问题就不存在了。
第一个:每个文件坚持成双成对呈现。每个.c源文件有必要有一个.h头文件跟它对应,每个.h头文件有必要有一个.c源文件跟它对应。比方:main.c与main.h,delay.c与 delay.h。
第二个:.c源文件只担任函数的界说和变量的界说,可是不担任函数的声明和变量的声明。比方:
unsigned char ucLedStep=0; //这个是全局变量的界说
void led_flicker() //这个是函数的界说
{
//…里边是具体代码内容
}
第三个:.h头文件只担任函数的声明和变量的声明,以及常量和IO口的宏界说,可是不担任函数的界说和变量的界说。比方:
#define const_time_level 200//这个是常量的宏界说
sbit led_dr=P3^5; //这个是IO口的宏界说
void led_flicker(); //这个是函数的声明
extern unsigned char ucLedStep; //这个是全局变量的声明,不能赋初始值
第四个:每个.h头文件都有必要固定以#ifndef,#define,#endif句子为模板,此模板是用来防止编译时因为重复包括头文件里边的内容而导致犯错。其间标志变量_XXX_鸿哥主张用它自身的文件名称加前后下划线_。
比方:
#ifndef _LED_ //标志变量_LED_是用它自身的文件名称命名
#define _LED_ //标志变量_LED_是用它自身的文件名称命名
#define const_time_level 200//这个是常量的宏界说
sbit led_dr=P3^5; //这个是IO口的宏界说
void led_flicker(); //这个是函数的声明
extern unsigned char ucLedStep; //这个是全局变量的声明,不能赋初始值
#endif
第五个:每个.h头文件里都有必要声明它对应的.c源文件里的一切界说函数和全局变量,留意:.c源文件里一切的全局变量都要在它所对应的.h头文件里声明一次,不仅仅是函数,这个当地很简单被人疏忽。
比方:在led.h头文件中:
void led_flicker(); //这个是函数的声明,因为在这个函数在led.c文件里界说了。
extern unsigned char ucLedStep; //这个是全局变量的声明,不许赋初值
第六个:每个.c源文件里都有必要包括两个文件,一个是单片机的体系头文件REG52.H,别的一个是它自己自身的头文件比方initial.h.剩余其它的头文件看实际情况来决议是否调用,咱们用到了哪些文件的函数,全局变量或许宏界说,就需要调用对应的头文件。
比方:在initial.c源文件中:
#include”REG52.H”//有必要包括的单片机体系头文件
#include”initial.h”//有必要包括它自身的头文件
/*注释:
因为根源文件顶用到了led_dr的句子,而led_dr是在led.h文件里宏界说的,所以有必要把led.h也包括进来
*/
#include”led.h”//因为根源文件顶用到了led_dr的句子,所以有必要把led.h也包括进来
void initial_myself()//这个是函数界说
{
led_dr=0;//led_dr是在led文件里界说和声明的
}
第七个:声明一个全局变量有必要加extern关键字,一起千万不能在声明全局变量的时分赋初始值,比方:
extern unsigned char ucLedStep=0; //这样是肯定过错的。
extern unsigned char ucLedStep; //这个是全局变量的声明,这个才是正确的
第八个:关于函数与全局变量的声明,编译器都不分配内存空间。关于函数与全局变量的界说,编译器都分配内存空间。函数与全局变量的界说只能在一个.c源文件中呈现一次,而函数与全局变量的声明能够在多个.h文件中呈现。
具体内容,请看源代码解说,本程序例程是直接把前面第四节一个源文件更改成多文件编程方法。
(1)硬件渠道:
根据朱兆祺51单片机学习板。把前面第四节一个源文件更改成多文件编程方法。
(2)完成功用:跟前面第四节的功用如出一辙,让一个LED闪耀。
(3)keil多文件编程的截图预览:
(4)整个源代码解说工程文件下载:
(5)源代码解说如下(留意,以下代码不能直接放到一个源文件里编译):
- /*以下是 main.h 的内容*/
- /* 注释一:
- 每个头文件都是固定以#ifndef,#define,#endif
- 为模板,其间标志变量_XXX_我主张用它自身的文件名称加前后下划线_。
- 此标志变量名称是用来防备屡次包括犯错的,具体解说请看注释二。
- 每个头文件只做函数的声明和变量的声明,以及常量和IO口的宏界说,不做
- 函数的界说与变量的界说。
- */
- #ifndef _MAIN_ //标志变量_MAIN_是用它自身的文件名称命名
- #define _MAIN_ //标志变量_MAIN_是用它自身的文件名称命名
- void main();//这个是函数的声明
- #endif
- /* 注释二:
- 以上句子
- #ifndef
- #define
- 刺进其它内容…
- #endif
- 类似于把_MAIN_看成是一个标志变量
- if(_MAIN_==0)// 相当于#ifndef _MAIN_
- {
- _MAIN_=1;// 相当于#define _MAIN_
- 刺进其它内容…
- } //相当于#endif
- 意图是经过一个标志位变量的赋值,让编译器在编译的时分,只包括一次此头文件,防止屡次包括犯错
- */
- /*——分割线————————————————–*/
- /*以下是 main.c 的内容*/
- /* 注释一:
- 每个源文件都有必要包括两个文件,一个是单片机的体系头文件REG52.H,
- 别的一个是它自己自身的头文件main.h.剩余其它的头文件看实际情况来
- 决议是否调用,咱们用到了哪些文件的函数,全局变量或许宏界说,就需要调用对应的头文件。
- 每个源文件只做函数的界说和变量的界说,不做函数的声明和变量的声明。
- */
- #include “REG52.H”//有必要包括的单片机体系头文件
- #include “main.h”//有必要包括它自身的头文件
- /* 注释二:
- (1)因为根源文件中调用initial_myself()和initial_peripheral()函数,而这两个函数
- 都是在initial文件里界说和声明的,所以有必要把initial.h也包括进来。
- (2)因为根源文件中调用delay_long(100)函数,而这个函数
- 是在delay文件里界说和声明的,所以有必要把delay.h也包括进来。
- (2)因为根源文件中调用led_flicker()函数,而这个函数
- 是在led文件里界说和声明的,所以有必要把led.h也包括进来。
- */
- #include “initial.h”//因为根源文件顶用到了initial_myself()和initial_peripheral()函数,所以有必要把initial.h也包括进来
- #include “delay.h”//因为根源文件顶用到了delay_long(100)函数,所以有必要把delay.h也包括进来
- #include “led.h”//因为根源文件顶用到了led_flicker()函数,所以有必要把led.h也包括进来
- void main()//这个是函数的界说
- {
- initial_myself();
- delay_long(100);
- initial_peripheral();
- while(1)
- {
- led_flicker();
- }
- }
- /*——分割线————————————————–*/
- /*以下是 delay.h 的内容*/
- #ifndef _DELAY_ //标志变量_DELAY_是用它自身的文件名称命名
- #define _DELAY_ //标志变量_DELAY_是用它自身的文件名称命名
- void delay_long(unsigned int uiDelaylong); //这个是函数的声明,每一个源文件里的函数都要在它的头文件里声明
- #endif
- /*——分割线————————————————–*/
- /*以下是 delay.c 的内容*/
- #include “REG52.H”//有必要包括的单片机体系头文件
- #include “delay.h”//有必要包括它自身的头文件
- void delay_long(unsigned int uiDelayLong)//这个是函数的界说
- {
- unsigned int i; //这个是局部变量的界说
- unsigned int j; //这个是局部变量的界说
- for(i=0;i
- {
- for(j=0;j<500;j++)
- {
- ;
- }
- }
- }
- /*——分割线————————————————–*/
- /*以下是 initial.h 的内容*/
- #ifndef _INITIAL_ //标志变量_INITIAL_是用它自身的文件名称命名
- #define _INITIAL_ //标志变量_INITIAL_是用它自身的文件名称命名
- void initial_myself(); //这个是函数声明,每一个源文件里的函数都要在它的头文件里声明
- void initial_peripheral(); //这个是函数声明,每一个源文件里的函数都要在它的头文件里声明
- #endif
- /*——分割线————————————————–*/
- /*以下是 initial.c 的内容*/
- #include “REG52.H”//有必要包括的单片机体系头文件
- #include “initial.h”//有必要包括它自身的头文件
- /* 注释一:
- 因为根源文件顶用到了led_dr的句子,而led_dr是在led文件里宏界说的,所以有必要把led.h也包括进来
- */
- #include “led.h”//因为根源文件顶用到了led_dr的句子,所以有必要把led.h也包括进来
- void initial_myself()//这个是函数界说
- {
- TMOD=0x01;//以下能直接用TMOD,TH0,TL0,EA,ET0,TR0这些寄存器关键字,是因为包括了REG52.H头文件
- TH0=0xf8;
- TL0=0x2f;
- led_dr=0;//led_dr是在led文件里界说和声明的
- }
- void initial_peripheral() //这个是函数界说
- {
- EA=1;
- ET0=1;
- TR0=1;
- }
- /*——分割线————————————————–*/
- /*以下是 interrupt.h 的内容*/
- #ifndef _INTERRUPT_ //标志变量_INTERRUPT_是用它自身的文件名称命名
- #define _INTERRUPT_ //标志变量_INTERRUPT_是用它自身的文件名称命名
- void T0_time();//这个是函数声明,每一个源文件里的函数都要在它的头文件里声明
- /* 注释一:
- 声明一个外部全局变量有必要加extern关键字,一起千万不能在声明全局变量的时分赋初始值,比方:
- extern unsigned int uiTimeCnt=0; 这样是肯定过错的。
- */
- extern unsigned int uiTimeCnt; //这个是全局变量的声明,不能赋初始值
- #endif
- /*——分割线————————————————–*/
- /*以下是 interrupt.c 的内容*/
- #include “REG52.H”//有必要包括的单片机体系头文件
- #include “interrupt.h”//有必要包括它自身的头文件
- unsigned int uiTimeCnt=0; //这个是全局变量的界说,能够赋初值
- void T0_time() interrupt 1//这个是函数界说
- {
- TF0=0; //以下能直接用TF0,TR0,TH0,TL0这些寄存器关键字,是因为包括了REG52.H头文件
- TR0=0;
- if(uiTimeCnt<0xffff)
- {
- uiTimeCnt++;
- }
- TH0=0xf8;
- TL0=0x2f;
- TR0=1;
- }
- /*——分割线————————————————–*/
- /*以下是 led.h 的内容*/
- #ifndef _LED_ //标志变量_LED_是用它自身的文件名称命名
- #define _LED_ //标志变量_LED_是用它自身的文件名称命名
- #define const_time_level 200 //宏界说都放在头文件里
- /* 注释一:
- IO口的宏界说也放在头文件里,
- 如果是PIC单片机,以下IO口界说相当于宏界说 #defineled_dr LATBbits.LATB4等句子
- */
- sbit led_dr=P3^5; //如果是P%&&&&&%单片机,相当于宏界说 #defineled_dr LATBbits.LATB4等句子
- void led_flicker(); //这个是函数的声明,每一个源文件里的函数都要在它的头文件里声明
- /* 注释三:
- 声明一个全局变量有必要加extern关键字,一起千万不能在声明全局变量的时分赋初始值,比方:
- extern unsigned char ucLedStep=0; 这样是肯定过错的。
- */
- extern unsigned char ucLedStep; //这个是全局变量的声明
- #endif
- /*——分割线————————————————–*/
- /*以下是 led.c 的内容*/
- #include “REG52.H”//有必要包括的单片机体系头文件
- #include “led.h”//有必要包括它自身的头文件
- /* 注释一:
- 因为根源文件顶用到了uiTimeCnt全局变量,而uiTimeCnt是在interrupt文件里声明和界说的,
- 所以有必要把interrupt.h也包括进来
- */
- #include “interrupt.h”//有必要包括它自身的头文件
- unsigned char ucLedStep=0; //这个是全局变量的界说,能够赋初值
- void led_flicker() //这个是函数的界说
- {
- switch(ucLedStep)
- {
- case 0:
- if(uiTimeCnt>=const_time_level)
- {
- ET0=0;//以下能直接用ET0寄存器关键字,是因为包括了REG52.H头文件
- uiTimeCnt=0; //uiTimeCnt此变量是在interrupt文件里声明和界说的,所以有必要把interrupt.h也包括进来
- ET0=1;
- led_dr=1;//此IO口界说现已在led.h头文件中界说了
- ucLedStep=1; //切换到下一个过程
- }
- break;
- case 1:
- if(uiTimeCnt>=const_time_level)
- {
- ET0=0;
- uiTimeCnt=0;
- ET0=1;
- led_dr=0;
- ucLedStep=0; //回来到上一个过程
- }
- break;
- }
- }
- /*——分割线————————————————–*/
总结陈词:
下一节开端讲液晶屏显现方面的内容。欲知概况,请听下回分解—-带字库12864液晶屏的常用点阵字体程序。