6
.程序结构
6.1
基本要求
1)
有
main()
函数的
.c
文件应将
main()
放在最前面,并明确用
void
声明参数和返回值。
2)
对由多个
.c
文件组成的模块程序或完整监控程序,建立公共引用头文件,将需要引用的库头文件、标准寄存器定义头文件、自定义的头文件、全局变量等均包含在内,供每个文件引用。通常,标准函数库头文件采用尖角号
< >
标志文件名,自定义头文件采用双撇号″″标志文件名。
3)
每个
.c
文件有一个对应的
.h
文件,
.c
文件的注释之后首先定义一个唯一的文件标志宏,并在对应的
.h
文件中解析该标志。
在
.c
文件中:
#define FILE_FLAG
在
.h
文件中:
#ifdef FILE_FLAG
#define XXX
#else
#define XXX extern
#endif
4)
对于确定只被某个
.c
文件调用的定义可以单独列在一个头文件中、单独调用。
6.2
可重入函数
可重入函数中若使用了全局变量,应通过关中断、信号量等操作手段对其加以保护。
6.3
函数的形参
1)
由函数调用者负责检查形参的合法性。
2)
尽量避免将形参作为工作变量使用。
6.4
循环
1)
尽量减少循环嵌套层数
2)
在多重循环中,应将最忙的循环放在最内层
3)
循环体内工作量最小
4)
尽量避免循环体内含有判断语句
7
.工程中所包含的文件
7.1
头文件
7.1.1 头文件的形式
MCU
程序中的头文件包括面向硬件对象头文件、公共头文件和总头文件。
MCU C
工程编程是面向硬件对象的。例如,要用
MCU
控制电机
(Motor)
,在这样一个系统中,“面向硬件对象”概念体现在,工程中会创建“
Motor.c
”的源程序文件专门用于电机控制。相应的,也要创建一个同名头文件“
Motor.h
”
,
用于控制电机的
MCU
引脚定义、相关宏定义和电机控制函数声明等。像这样的头文件,就是面向硬件对象头文件。与之同名的“
*.c
”文件可以包含它,来完成控制此硬件对象的
MCU
引脚定义和相关宏定义;调用该硬件对象控制函数的文件也可以通过调用它来进行函数声明。
还有一类头文件不是专门针对于特定的硬件对象的,而是有一定的通用性。这类头文件被称为公共头文件。如工程中包含的“
Type.h
”文件,该文件用于
C
语言中类型的别名定义,用户还可以根据自己的需要,随时在该文件中添加条目。在工程的任一文件中,需要用到这些别名时,都要包含“
Type.h
”。可见公共头文件并不拘泥于具体的硬件对象,它是为整个工程的和谐运作而建立的。
总头文件
(includes.h)
是一个较特殊的头文件。它只被主函数文件包含,用于包含主函数文件中需要的头文件,宏定义,函数声明等。它使得主函数文件能够尽量避免改动,结构更加清晰。
7.1.2 头文件的命名
总的来说头文件的命名应尽量做到简短易懂,见名知意。
面向硬件对象头文件的名称一定要与相应的硬件对象驱动文件同名。例???
公共头文件,如果对应于相应的源程序文件而建立,必须与之同名。如,“
GeneralFun.c
”
是工程中的通用函数定义文件,(像内存数据移动函数,延时函数都属于通用函数),其他文件在用到这些函数之前,必须进行函数原型声明,从而建立与之同名的“
GeneralFun.h
”文件,专门用于相应的函数声明。其它的公共头文件没有同名要求,只要表清文件含义即可,如“
Type.h
”
,
“
GP32C.h
”等。
总头文件在一个工程中只有一个,它的名称较为固定,一般取为“
Includes.h
”。
7.1.3 头文件注意事项
1)
为了防止重复定义需要使用伪指令
#ifndef VarType
……
例:
#ifndef VarType
#define VarType
typedef unsigned char INT8U; //
无符号
8
位数
typedef signed char INT8S; //
有符号
8
位数
typedef unsigned int INT16U; //
无符号
16
位数
typedef signed int INT16S; //
有符号
16
位数
typedef unsigned long INT32U; //
无符号
32
位数
typedef signed long INT32S; //
有符号
32
位数
typedef float FP32; //
单精度浮点数
typedef double FP64; //
双精度浮点数
#endif
2)
对于一个项目中的头文件与芯片相关的寄存器映像文件不可擅自改动,如果的确存在需要改动的地方另外开辟头文件。
3) typedef
和
#define
的用法
①
typedef
的用法
在
C/C++
语言中,
typedef
常用来定义一个标识符及关键字的别名,它是语言编译过程的一部分,但它并不实际分配内存空间,实例像:
typedef int INT;
typedef int ARRAY[10];
typedef (int*) pINT;
typedef
可以增强程序的可读性,以及标识符的灵活性,但它也有“非直观性”等缺点。
②
#define
的用法
#define
为一宏定义语句,通常用它来定义常量
(
包括无参量与带参量
)
,以及用来实现那些“表面似和善、背后一长串”的宏,它本身并不在编译过程中进行,而是在这之前
(
预处理过程
)
就已经完成了,但也因此难以发现潜在的错误及其它代码维护问题,它的实例像:
#define INT int
#define TRUE 1
#define Add(a,b) ((a)+(b));
#define Loop_10 for (int i=0; i<10; i++)
③
typedef
与
#define
的区别
从以上的概念便也能基本清楚,
typedef
只是为了增加可读性而为标识符另起的新名称
(
仅仅只是个别名
)
,而
#define
原本在
C
中是为了定义常量,到了
C++
,
const
、
enum
、
inline
的出现使它也渐渐成为了起别名的工具。为了尽可能地兼容,一般都遵循
#define
定义“可读”的常量以及一些宏语句的任务,而
typedef
则常用来定义关键字、冗长的类型的别名。
宏定义只是简单的字符串代换
(
原地扩展
)
,而
typedef
则不是原地扩展,它的新名字具有一定的封装性,以致于新命名的标识符具有更易定义变量的功能。请看上面第一大点代码的第三行:
typedef (int*) pINT;
以及下面这行
:
#define pINT2 int*
效果相同?实则不同!实践中见差别:
pINT a,b;
的效果同
int *a; int *b;
表示定义了两个整型指针变量。
而
pINT2 a,b;
的效果同
int *a, b;
表示定义了一个整型指针变量
a
和整型变量
b
。
注意:两者还有一个行尾
;
号的区别哦!(???)
7.2
源程序文件
源程序文件包括主函数文件、通用函数文件、硬件对象控制文件、芯片初始化文件、中断向量定义文件和中断使能文件。
源程序文件的分类和命名类同于头文件,但也有它自己的特点。
7.2.1 主程序文件
(Main.s
或
Main.c)
(
?????
)
工程中有且仅有一个主程序文件,它包含了工程的主处理流程。
主函数文件中包含:
(
1
)工程描述
①工程名
工程名中每个意义单词(或单词缩写)的首字母大写,后缀为
.prj
。
②硬件连接索引
工程所要控制的硬件对象索引,详细描述在相应的硬件对象控制文件中给出。
③工程的功能、目的和说明
④注意要点
可以注明编程要点和心得
⑤日期
注明工程完成日期
(
2
)总头文件
(
3
)主函数
如:
7.2.2 芯片初始化文件(“SetUp.c”或 “SetUp.s”)
该文件与具体的芯片型号有关,并且只包含一个芯片初始化函数,若想由编译器自动调用芯片初始化函数,其函数名必须为
"_HC08Setup"
,否则编译器会自动建立并调用一个空的
"__HC08Setup"
汇编子程序,而不理会用户创建的芯片初始化函数。为了统一,将该函数起名为
"MCUInit",
并在主函数中调用该函数。
7.2.3 通用函数头文件和通用函数文件
通用函数头文件和通用函数文件,“
GenneralFun.h
”和“
GeneralFun.c
”。
//[GenneralFun.h]
通用函数头文件
---------------------------------------------#include"Type.h" //
类型别名定义
void Delay(INT16U); //
延时函数声明
“
GenneralFun.h
”中包含:
(
1
)文件名
(
2
)通用函数所需用到的头文件
(
3
)通用函数用到的宏定义
(
4
)通用函数声明
外部函数要用到通用函数时,可包含这个头文件进行函数声明。
7.2.4 对象控制文件
7.2.5 中断处理函数和中断向量表文件
9
.
硬件封装的思想
1)
与硬件相关的程序文件
与某个硬件相关的子程序放到
1
个程序文件中,该硬件的头文件放到一个文件中。
程序文件的开始处是有关说明:本文件所包含的子程序及简要的功能说明,子程序分为内部调用和外部调用;硬件的连接说明。
2)
中断的开放和禁止
使用宏定义方式开放或禁止中断,宏定义语句放在
EnDisInt.h
头文件中。宏名的定义方法:
开放中断以
Enable
标识,宏名中包含中断名,宏名最后以
Int
结束。如:开放串行接收中断的宏名为:
EnableSCIReInt
。
禁止中断以
Disable
标识,宏名中包含中断名,宏名最后以
Int
结束。如:禁止串行接收中断的宏名为:
DisableSCIReInt
。
开放所有中断宏名:
EnableMCUInt
。
禁止所有中断宏名:
DisableMCUInt
。