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

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. JDK ThreadPoolExecutor核心原理与实践

    一.内容概括 本文内容主要围绕JDK中的ThreadPoolExecutor展开,首先描述了ThreadPoolExecutor的构造流程以及内部状态管理的机理,随后用大量篇幅深入源码探究了Threa ...

  2. CF78B Easter Eggs 题解

    Content 有一个有 \(n\) 个点的环,你可以将其染成一种颜色.一共有 \(7\) 种颜色(R,O,Y,G,B,I,V)可以选择.你的染色方案应该满足下面的要求: 每一个点都要被染色,且 \( ...

  3. 利用struts2上传文件时,如果文件名中含有-符号,那么会出错

    利用struts2上传文件时,如果文件名中含有-符号,那么会出错 报错如下: HTTP Status 500 - C:\Program Files\Apache Software Foundation ...

  4. ☕【Java实战系列】「技术盲区」Double与Float的坑与解决办法以及BigDecimal的取而代之!

    探究背景 涉及诸如float或者double这两种浮点型数据的处理时,偶尔总会有一些怪怪的现象,不知道大家注意过没,举几个常见的栗子: 条件判断超预期 System.out.println( 1f = ...

  5. Solon 1.6.10 重要发布,现在有官网喽!

    关于官网 千呼万唤始出来: https://solon.noear.org .整了一个月多了,总体样子有了...还得不断接着整! 关于 Solon Solon 是一个轻量级应用开发框架.支持 Web. ...

  6. SpringBoot整合Elasticsearch启动报错处理 nested exception is java.lang.IllegalStateException: availableProcessors is already set to [8], rejecting [8]

    Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean wit ...

  7. 【九度OJ】题目1109:连通图 解题报告

    [九度OJ]题目1109:连通图 解题报告 标签(空格分隔): 九度OJ 原题地址:http://ac.jobdu.com/problem.php?pid=1109 题目描述: 给定一个无向图和其中的 ...

  8. 【LeetCode】90. Subsets II 解题报告(Python & C++)

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

  9. Laravel 使用 maatwebsite/Excel 3.1 实现导入导出的简单方法

    官方文档 https://docs.laravel-excel.com/3.1/getting-started git地址 https://github.com/maatwebsite/Laravel ...

  10. Adversarial Defense by Restricting the Hidden Space of Deep Neural Networks

    目录 概 主要内容 Mustafa A., Khan S., Hayat M., Goecke R., Shen J., Shao L., Adversarial Defense by Restric ...