5. 单片机编程规范-排版
5.1 缩进
代码的每一级均往右缩进4个空格的位置。不使用Tab键
5.2 分行
每行语句(?????超过80个字符)要分成多行书写;长表达式要在低优先级操作符处划分新行,操作符放在新行之首,划分出的新行要进适当的缩进,使排版整齐,语句可读。避免把注释插入分行中。
5.3 空行
1) 文件注释区、头文件引用区、函数间应该有且只有一行空行。
2) 相邻函数之间应该有且只有一行空行。
3) 函数体内相对独立的程序块之间可以用一行空行或注释来分隔。
4) 函数注释和对应的函数体之间不应该有空行。
5) 文件末尾有且只有一行空行。
5.4 空格
1) 函数语句尾部或者注释之后不能有空格。
2) 括号内侧(即左括号后面和右括号前面)不加空格,多重括号间不加空格。
3) 函数形参之间应该有且只有一个空格(形参逗号后面加空格)。
4) 同一行中定义的多个变量间应该有且只有一个空格(变量逗号后面加空格)。
5) 表达式中,若有多个操作符连写的情况,应使用空格对它们分隔:
6) 在两个以上的关键字、变量、常量进行对等操作时,它们之间的操作符前后均加一个空格;在两个以上的关键字、变量、常量进行非对等操作时,其前后均不应加空格;
7) 逗号只在后面加空格;
8) 双目操作符,如比较操作符, 赋值操作符"="、"+=",算术操作符"+"、"%",逻辑操作符"&&"、"&",位操作符"<<"、"^"等,前后均加一个空格;
9) 单目操作符,如"!"、"~"、"++"、"-"、"&"(地址运算符)等,前后不加空格;
10) "->"、"."前后不加空格;
11) if、for、while、switch等关键字与后面的括号间加一个空格;
5.5 花括号
1) if、else if、else、for、while语句无论其执行体是一条语句还是多条语句都必须加花括号,且左右花括号各独占一行。
2) do{}while()结构中,"do"和"{"均各占一行,"}"和"while();"共同占用一行。
if ( ) do
{ {
} }while( );
else
{
}
嵌套越少越好,{}不准超过3层
5.6 switch语句
1) 每个case和其判据条件独占一行。
2) 每个case程序块需用break结束。特殊情况下需要从一个case块顺序执行到下一个case块的时候除外,但需要花括号在交界处明确注释如此操作的原因,以防止出错。
3) case程序块之间空一行,且只空一行。
4) 每个case程序块的执行语句保持4个空格的缩进。
5) 一般情况下都应该包含default分支。
Switch ( )
{
case x:
break;
case x:
break;
default:
break;
}
6.程序结构 6.1 基本要求
1) 有main()函数的.c文件应将main()放在最前面,并明确用void声明参数和返回值。
2) 对由多个.c文件组成的模块程序或完整监控程序,建立公共引用头文件,将需要引用的库头文件、标准寄存器定义头文件、自定义的头文件、全局变量等均包含在内,供每个文件引用。通常,标准函数库头文件采用尖角号< >标志文件名,自定义头文件采用双撇号″″标志文件名。
3) 每个.c文件有一个对应的.h文件,.c文件的注释之后首先定义一个唯一的文件标志宏,并在对应的.h文件中解析该标志。
在.c文件中:
define FILE_FLAG在.h文件中:&
6.程序结构 6.1 基本要求
1) 有main()函数的.c文件应将main()放在最前面,并明确用void声明参数和返回值。
2) 对由多个.c文件组成的模块程序或完整监控程序,建立公共引用头文件,将需要引用的库头文件、标准寄存器定义头文件、自定义的头文件、全局变量等均包含在内,供每个文件引用。通常,标准函数库头文件采用尖角号< >标志文件名,自定义头文件采用双撇号″″标志文件名。
3) 每个.c文件有一个对应的.h文件,.c文件的注释之后首先定义一个唯一的文件标志宏,并在对应的.h文件中解析该标志。
在.c文件中:
define FILE_FLAG在.h文件中:
ifdef FILE_FLAGdefine XXXelsedefine XXX externendif4) 对于确定只被某个.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 VarTypedefine VarTypetypedef 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; //双精度浮点数
endif2) 对于一个项目中的头文件与芯片相关的寄存器映像文件不可擅自改动,如果的确存在需要改动的地方另外开辟头文件。
3) typedef和#define的用法
① typedef的用法
在C/C++语言中,typedef常用来定义一个标识符及关键字的别名,它是语言编译过程的一部分,但它并不实际分配内存空间,实例像:
typedef int INT;
typedef int ARRAY[10];
typedef (int*) pINT;
typedef可以增强程序的可读性,以及标识符的灵活性,但它也有“非直观性”等缺点。
② #define的用法
define为一宏定义语句,通常用它来定义常量(包括无参量与带参量),以及用来实现那些“表面似和善、背后一长串”的宏,它本身并不在编译过程中进行,而是在这之前(预处理过程)就已经完成了,但也因此难以发现潜在的错误及其它代码维护问题,它的实例像:define INT intdefine TRUE 1define 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]通用函数头文件---------------------------------------------#i nclude"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。
|