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

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. CF1291A Even But Not Even 题解

    Content 有 \(t\) 组数据,每组数据给定一个整数 \(n\),接着给出一个长度为 \(n\) 的数字串.请从中删除一些数,使得剩下的数字串不是偶数,但是其和为偶数,或者不存在这样的方案. ...

  2. js 让小数四舍五入保留两位小数的函数是?

    js 让小数四舍五入保留两位小数的函数是? 例子:data.relations[i].data[j].toFixed(2) toFixed(2)这个函数就是保留两位小数的作用

  3. 直接在filter过滤器代码里加org.apache.struts2.ServletActionContext.getRequest()会出现空指针情况

    直接在filter过滤器代码里加org.apache.struts2.ServletActionContext.getRequest()获得request对象请注意啦,会出现空指针情况 请关注此处:

  4. 可以通过外键的.id直接传值

    可以通过外键的.id直接传值 如<input type="text" name="user.department.id" value="1&qu ...

  5. SpringBoot整合quartz实现动态启动,停止定时任务功能

    注意:这个方法当程序重启之后会失效,所以必须将定时任务持久化到数据库,然后程序启动的时候重新把数据库的定时任务加载到quartz中 springboot程序启动初始化代码参考:https://www. ...

  6. 【LeetCode】1023. Camelcase Matching 解题报告(Python)

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

  7. 【LeetCode】388. Longest Absolute File Path 解题报告(Python)

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

  8. 本原串(hdu 2197)

    本原串 题目链接 思路: 反向想将总的个数减去不符合要求的个数.我们枚举n的约数,然后把n平均分,就可以构成不符合要求的串,\(g[i]\)表示循环节长为i约数的个数\(2^i\),我们要求循环节为\ ...

  9. 难搞的偏向锁终于被 Java 移除了

    背景 在 JDK1.5 之前,面对 Java 并发问题, synchronized 是一招鲜的解决方案: 普通同步方法,锁上当前实例对象 静态同步方法,锁上当前类 Class 对象 同步块,锁上括号里 ...

  10. Capstone CS5268DEMOBOARD原理图|TYPEC转HDMI+VGA+PD3.0+USB3.0扩展坞方案

    Capstone CS5268DEMOBOARD原理图|TYPEC转HDMI+VGA+PD3.0+USB3.0四合一设计参考 CS5268 是typec转HDMI+VGA+pd3.0+U3四合一拓展坞 ...