基于C语言的单片机、arm相关的工程开发时,C语言的模块化特点体现的非常明显。试想一下:你的一个工程中需要用到AD采样模块、液晶显示模块、串口发送模块、DA控制模块等。你肯定不会选择在一个.c文件中进行,必须是分模块的,这样才有利于团队开发,提高效率。
那么模块化设计遵循着怎样的原则呢,应该怎么写.c,.h文件呢。
1. .c和.h文件的区别
通常意义上的说法,.c是源文件,.h是头文件。通常创作者为了保护其代码,而把.c文件封装起来,不公开,而将.h文件提供出来。此时.h相当于接口,供程序员调用。
但是实际上我们自己编写.h,.c文件时会发现.c与.h文件里面的内容其实并没有什么区别。在.c中写的代码同样可以在.h文件写。
2.工程中使用.c,.h文件
总的来说,.h文件有两种使用方法。
方法一:
正是由于1中蓝色部分的描述的原因。我们会发现好多工程中某个模块只有.h文件,而没有.c文件
以上图的DSP开发【瑞泰提供的例程为例】其工程中ICETEK-VC5509-EDU.h文件中包含了对12864液晶屏的寄存器地址定义,相关操作函数定义。并没有写相关的.c文件。然后在lcd.c文件中调用ICETEK-VC5509-EDU.h。这样的做法是可行的,为什么可行,上面1中已经讲得很清楚。
但是我们再来思考一个问题。如果这个工程中我又添加了一个ad.c。在ad.c中我们需要用到液晶显示,那么我们得调用ICETEK-VC5509-EDU.h 。如果我们这么做了,工程编译链接会通过吗?
肯定不会,会报define duplicated //重复定义的错误,为什么呢? include "ICETEK-VC5509-EDU.h"相当于把这句话替换成ICETEK-VC5509-EDU.h中的内容,而ICETEK-VC5509-EDU.h中对液晶的寄存器都进行了定义,初始化,函数都已经定义了。你在两个文件中引用ICETEK-VC5509-EDU.h,肯定会报重复定义的错误。
这种方法适用于单模块,或者某个模块不会被多个.c文件调用
方法二:在.c文件中进行变量,数组,函数的定义,在.h文件中进行变量,数组,函数的声明。
C语言支持的是一处定义,多处声明【这里所讲的声明指的是狭义上的声明,引用声明,即不会进行内存分配】
现在将上述的ICETEK-VC5509-EDU.h分成.c和.h文件
首先在.h文件中开头写上
#ifndef __ICETEK-VC5509-EDU_H
#define __ICETEK-VC5509-EDU_H //防止重复定义,名字与.c相同
#endif
然后就要进行进一步的操作
(1)宏定义,宏定义是遇到此变量即替换
所以放在.h文件中
如:
// McBSP0 ------------------------------------------------------
#define SPCR0 (*(unsigned int *)0x018c0008)
#define PCR0 (*(unsigned int *)0x018c0024)
#define SPCR01 (*(unsigned int *)0x01900008)
#define SPCR02 (*(unsigned int *)0x0)
(2)函数,函数的定义和声明比较清晰
.h文件进行声明
void InitInterrupt(void); // 初始化中断
void InitCTR(); // 初始化ICETEK-CTR
void CloseCTR(); // 关闭ICETEK-CTR上各设备
.c文件进行定义
void CloseCTR()
{
CTRGR=0;
CTRLR=0; CTRLR=0x40;
CTRLR=0x0c0;
LCDCMD(LCDCMDTURNOFF);
dbClearKey=CTRCLKEY;
LBDS=0;
}
这样就可以实现多个.c文件加.h头文件,而不会导致重复定义的问题。
(3)数组等变量
变量的声明和定义在单个文件时好像都不怎么区分的。
变量的声明:指明变量名,变量类型。
变量的定义:指名变量名,变量类型,分配内存空间。
eg int a; //定义
extern int a; //声明
如果进行了赋值,则肯定是定义
extern int a=0; //定义
所以在.h文件中:
extern unsigned char ledbuf[8],ledx[8];
extern unsigned char dbClearKey;
extern unsigned char ledkey[10][8];
extern unsigned int music[nMusicNumber][2];
//变量声明
.c文件:
unsigned int pwm[8]={ 0x86,0x87,0x83,0x8b,0x89,0x8d,0x8c,0x8e };
//变量必须要定义
unsigned char dbClearKey; //必须要定义
unsigned char ledbuf[8];
unsigned char ledx[8];
【
在这地方还是有个疑问的,在.h文件中如果不加extern 编译也是可以通过的,那么unsigned char ledbuf[8]也能表示声明。【.h文件是先被加载进.c文件,所以.h中unsigned char ledbuf[8]是最先执行的。为什么表示的是声明呢】