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

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 ×××××××××××××××××××××××××××××××××××××××××××××××××/

[C 语言基础] 如何调用不同文件中的函数的更多相关文章

  1. Python 调用JS文件中的函数

    Python 调用JS文件中的函数 1.安装PyExecJS第三方库 2.导入库:import execjs 3.调用JS文件中的方法 Passwd = execjs.compile(open(r&q ...

  2. C++学习——在C文件中调用C++文件中的函数

    1.CPP文件中的内容 #include "mytest.h" #include <iostream> using namespace std; int add(con ...

  3. c#.net前台调用JS文件中的函数[.net与JavaScript的应用]

    原文发布时间为:2008-10-10 -- 来源于本人的百度文章 [由搬家工具导入] <%@ Page Language="C#" AutoEventWireup=" ...

  4. VS2013 VC++的.cpp文件调用CUDA的.cu文件中的函数

    CUDA 8.0在函数的调用中方便的让人感动.以下是从网上学到的VC++的.cpp文件调用CUDA的.cu文件中的函数方法,和一般的VC++函数调用的方法基本没差别. 使用的CUDA版本为CUDA 8 ...

  5. VS开发】C中调用C++文件中定义的function函数

    [VS开发]C中调用C++文件中定义的function函数 标签(空格分隔): [VS开发] 声明:引用请注明出处http://blog.csdn.net/lg1259156776/ 精要一揽 C调用 ...

  6. CUDA常见问题之无法在c文件中调用cu文件中定义的函数

    当在C源文件中调用cu文件中定义的函数时,会出现undefined reference的问题,而在C++源文件中调用cu文件中定义的函数时则不会出现这个问题. 出现上述问题的原因是,nvcc编译器采用 ...

  7. vb6如何调用delphi DLL中的函数并返回字符串?

    1,问题描述 最近发现vb6调用delphi DLL中的函数并返回字符串时出现问题,有时正常,有时出现?号,有时干脆导致VB程序退出 -- :: 将金额数字转化为可读的语音文字:1转化为1元 ???? ...

  8. 在.bashrc文件中定义函数

    在命令行上直接定义shell函数的明显缺点是当退出shell时,函数就消失了,对于复杂的函数来说,这可能会是个问题. 一个简单的方法就是每次启动新shell的时候都会自动加载所需要的函数. 最好的办法 ...

  9. [转]html页面调用js文件里的函数报错onclick is not defined处理方法

    原文地址:http://blog.csdn.net/ywl570717586/article/details/53130863 今天处理html标签里的onclick功能的时候总是报错:Uncaugh ...

随机推荐

  1. AT4811 [ABC160D] Line++ 题解

    Content 给定一个 \(n\) 个点.\(n\) 条边的无向图.对于所有的 \(1\leqslant i<n\),在点 \(i,i+1\) 之间连一条无向边.另外在给定两个点 \(x,y\ ...

  2. windows(Linux)创建”内网穿透“工具(通过自定义域名访问部署于内网的 web 服务,可以用于调试微信支付,支付宝支付,微信公众号等开发项目)

    此方法需要自有服务器和域名,如果没有这些的开发者, 可以参考钉钉提供的内网穿透方式:https://www.cnblogs.com/pxblog/p/13862376.html 一.准备工作 1.域名 ...

  3. JAVA判断URL地址是否非法

    /** * 判断请求url是否非法 * @param url * @return */ public static boolean isValidRequestUri(String url) { if ...

  4. 反解ios静态库

    p.p1 { margin: 0; font: 12px "Helvetica Neue" } p.p2 { margin: 0; font: 12px "Helveti ...

  5. c++内存分布之虚函数(多继承)

    系列 c++内存分布之虚函数(单一继承) c++内存分布之虚函数(多继承) [本文] 结论 1.虚函数表指针 和 虚函数表 1.1 影响虚函数表指针个数的因素只和派生类的父类个数有关.多一个父类,派生 ...

  6. 【LeetCode】1013. Pairs of Songs With Total Durations Divisible by 60 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...

  7. 【LeetCode】1003. Check If Word Is Valid After Substitutions 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 循环 日期 题目地址:https://leetcod ...

  8. 【LeetCode】844. Backspace String Compare 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 字符串切片 栈 日期 题目地址:https://le ...

  9. 定义制造业操作(定义 MES/MOM 系统)

    定义制造业操作(定义 MES/MOM 系统) 制造业操作包含众多工厂级活动,涉及设备(定义.使用.时间表和维护).材料(识别.属性.位置和状态).人员(资格.可用性和时间表),以及这些资源与包含其信息 ...

  10. JWT+SpringBoot实战

    往期内容:JWT - 炒焖煎糖板栗 - 博客园 (cnblogs.com) JWT可以理解为一个加密的字符串,里面由三部分组成:头部(Header).负载(Payload).签名(signature) ...