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

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. CF94A Restoring Password 题解

    Content 有一个 \(80\) 位的 \(01\) 字符串,由 \(8\) 个长度为 \(10\) 的 \(01\) 字符串组成,每个小字符串分别对应一个数字.现在,给出这个字符串和 \(0\) ...

  2. CF656C Without Text 题解

    Content 输入一个字符串 \(s\),遍历每一个字符,如果这个字符是小写字母,那么答案就加上这个字母的字母表序:否则,如果这个字符是小写字母,那么答案就减去这个字母的字母表序.求最后的答案. 字 ...

  3. AT5341 [ABC156D] Bouquet 题解

    Content 有一个人有 \(n\) 种不同的话可供选择,TA 可以选择至少一种花做花束,但是 TA 不喜欢花的种数为 \(a\) 或者 \(b\) 的花束.求选花的方案数对 \(10^9+7\) ...

  4. LuoguP7375 [COCI2018-2019#5] Jarvis 题解

    Content 有 \(n\) 架无人机,每架无人机都有一个当前属性值 \(a_i\) 和出战属性值 \(b_i\).你可以给每架无人机的当前属性值同时加一个数 \(x\)(但只能做一次),使得能够出 ...

  5. 【进阶】uniapp复现微信相册功能之【图视频编辑 + 压缩】

    基于uniapp + vue实现微信相册,在实现了微信相册的基础上增加以下功能 1: 图片编辑 2: 视频编辑 3: 文件压缩 技术实现 开发环境:HbuilderX + nodejs 技术框架:un ...

  6. JS使用html()获取html代码获取不到input、textarea控件填写的值

    我们可以重写一个方法 (function ($) { var oldHTML = $.fn.html; $.fn.formhtml = function () { if (arguments.leng ...

  7. windows10下vscode+cmake编译Qt5代码

    概述 本文演示环境: windows10 cmake version:3.18 qt version: 5.14 vscode version: 1.54.1 本文演示环境基于在线模式, 离线使用VS ...

  8. software engineer's resume(帮助你写程序员简历)

    关键词 参考 简历模板 参考 下面开始是正文(关键词原文) 介绍 本项目由海外兔 (https://osjobs.net) 维护,海外兔团队由一线互联网面试官组成,提供海内外公司一对一入职套餐以及算法 ...

  9. 【LeetCode】378. Kth Smallest Element in a Sorted Matrix 解题报告(Python)

    [LeetCode]378. Kth Smallest Element in a Sorted Matrix 解题报告(Python) 标签: LeetCode 题目地址:https://leetco ...

  10. codeforces626D . Jerry's Protest

    Andrew and Jerry are playing a game with Harry as the scorekeeper. The game consists of three rounds ...