[C 语言基础] 如何调用不同文件中的函数

很多时候需要将实现不同功能的函数或者与某个模块有关的函数写在一个文件里。这样有两个好处:

1. 方便以后调用:以后需要用到这个模块,或者这类函数,直接将相关文件复制过去,再稍微修改一下就能应用于不同场合。

2. 使整个程序或者说工程条理清晰,利于Debug。

 

刚接触单片机编程的人都喜欢把所有函数都写一个main.c里面,其实也不能说喜欢,或许是对C语言不太熟练,不知道如何将函数写在不同的文件中罢了。

现在,我以1602 LCD模块为例,来说明如何将程序写在不同的文件里。

 

Step 1:新建3个文件,分别命名为:

             1602.c     : 和1602 LCD有关的函数都写在这里。

             _1602.h   : 这是1602.C的头文件,里面主要是1602.C里面定义的全局变量的声明,函数声明

             main.c     : main 函数,会在这里调用1602.c里面的某些函数。

 

Step 2:编写1602.c。这里只以两个函数为例说明。

 1 //###########################################################################
 2 //
 3 //  File     : 1602.c   
 4 //                                  
 5 //  Contents : 1602 LCM display driver. 
 6 //                   
 7 //               
 8 // 
 9 //  Powered by Vincent, 2014
10 //
11 //###########################################################################
12 //
13 //  Ver | dd mmm yyyy | Who     | Description of changes
14 // =====|=============|=========|=============================================
15 //  0.1 | 02 Jan 2014 | Vincent | Builded the file
16 //
17 //###########################################################################
18 
19 //**************************************************************************
20 //
21 // 1602 read busy(bit7) and address(bit6~bit0)
22 //
23 // Parameter None
24 //
25 // Return status value
26 //
27 //**************************************************************************
28 static
29 unsigned char StatusGet(void)
30 {
31     unsigned char Val;
32     RS = 0; RW = 1;
33     DATA_MODE_IN();
34 
35     do
36     {
37         E = 0;
38         Delay(40);
39         E = 1;
40         Delay(40);
41     }while(DATA_BUS & 0x80);
42 
43     Val = DATA_BUS;
44     DATA_MODE_OUT();
45 
46     return Val;
47 }
48 
49 //**************************************************************************
50 //
51 // 1602 write string 
52 //
53 // Parameter Addr is start address 
54 //           Val is string pointer.
55 //
56 // Return None.
57 //
58 //**************************************************************************
59 void _1602_Printf(unsigned char Addr, unsigned char *Val)
60 {
61     /* Set start address */
62     CMDSet(_1602_DDRAM_AC);
63 
64     while(*Val)
65     {
66         RegSet(*Val);
67         Val++;
68     } 
69 } 

上面程序清单中,第1行到第27行(简称L1~L17,下同),是对这个文件的简单描述(文件名,内容,版本,作者等)。

L28~L47是读取1602 状态字的子函数,修饰符static将这个函数的作用域限定在当前文件里,换句话说该函数只在当前文件中有效,在其他文件中,即使有相同函数名的子函数,他们也是互不干扰的,如果你写的子函数只在当前文件中调用,添加个修饰符是很有必要的。

L59~L69是让1602输出字符的子函数,因为外部文件要调用到本函数,没有用static修饰符。

 

Step 3. 编写_1602.h文件。上面提到,这个头文件的作用是给全局变量,全局函数做声明。清单如下:

 1 //###########################################################################
 2 //
 3 //  File     : _1602.h   
 4 //                                  
 5 //  Contents : Header for 1602 LCM.
 6 //                   
 7 //               
 8 // 
 9 //  Powered by Vincent, 2014
10 //
11 //###########################################################################
12 //
13 //  Ver | dd mmm yyyy | Who     | Description of changes
14 // =====|=============|=========|=============================================
15 //  0.1 | 02 Jan 2014 | Vincent | Builded the file
16 //
17 //###########################################################################
18 
19 
20 #ifndef _1602_H
21 #define _1602_H
22 
23 //*****************************************************************************
24 //
25 // Constants
26 //
27 //*****************************************************************************
28 #define _1602_LINE_1                      0x00
29 #define _1602_LINE_2                      0x40
30 
31 //*****************************************************************************
32 //
33 // Macro
34 //
35 //*****************************************************************************
36 #define _1602_CLEAR_DISPLY()             CMDSet(0x01)
37 #define _1602_RETURN_HOME()              CMDSet(0x02)
38 
39 //*****************************************************************************
40 //
41 // Function prototypes.
42 //
43 //*****************************************************************************
44 extern void _1602_Printf(unsigned char Addr, unsigned char *Val);
45 extern void _1602_Init(void);
46 
47 #endif

L1~L17同样是描述该文件的简要信息。L20, L21, L47是一对预处理模块,作用是防止头文件的重复包含和编译。

再看L44, L45,这四个函数的定义在1602.C文件里,为了使它们能被其他文件里的函数调用,需要先进行声明,修饰符extern表示该函数是外部函数。

 

把1602相关的程序写好后,现在就可以开始使用它了。我们要在main.c文件里面的main()函数里调用_1602_Printf()函数。代码见下面:

 1 //###########################################################################
 2 //
 3 //  File     :   main.c   
 4 //                                  
 5 //  Contents :   main file     
 6 //               
 7 // 
 8 //  Powered by Vincent, 2014
 9 //
10 //###########################################################################
11 //
12 //  Ver | dd mmm yyyy | Who    | Description of changes
13 // =====|=============|========|=============================================
14 //  0.1| 02 Jan 2014 | Vincent | Builded the file
15 //
16 //###########################################################################
17 
18 
19 
20 #include "_1602.h"
21 
22 int main(void)
23 {
24     _1602_Printf(0x80, "Hello, ZL_M4");
25 }

 函数_1602_Printf()的声明在_1602.h里,所以开头要将头文件包含进去,见L20,然后就可以在当前文件中调用该函数了。

 

 

结论:

     1. 在一个函数中调用另一个函数(即被调用函数)需要具备的以下条件:

1.1 被调函数必须是已经定义的函数(库函数或用户自己定义的函数)

1.2 如果使用的函数在另一个文件中定义,还应在文件开头用#include命令将调用的相关函数时所需用到的信息“包含到”本件中,如上面提到的1602 LCD模块相关      函数

1.3 如果定义的函数在调用它的函数(即主调函数)的后面(在同一文件中),应该在主调函数中对被调的函数作声明

2. 函数有内部函数和外部函数之分。函数本质上是外部的,可以供本文件或其他文件中的函数调用,但是在其他文件调用时要用extern对函数进行声明(如本例中的         _1602_Printf()函数;如果在定义函数时用static声明,表示其他文件不得调用此函数,如本例中的StatusGet函数。

 

/×××××××××××××××××××××××××××××××××××××××× THE END ×××××××××××××××××××××××××××××××××××××××××××××××××/

 

 

 

 

 

posted @ 2014-01-04 15:20  Mr.Bike  阅读(2240)  评论(0编辑  收藏  举报