文档库 最新最全的文档下载
当前位置:文档库 › 单片机软件编程规范

单片机软件编程规范

单片机软件编程规范
单片机软件编程规范

1 编辑器 (3)

2 程序文件的结构 (3)

2.1 说明 (3)

2.2 编译宏定义 (4)

2.3 包含文件 (4)

2.4 用户数据类型定义 (4)

2.5 管脚定义 (5)

2.6 常量 (5)

2.7 变量 (5)

2.8 正文 (6)

2.8.1 正文顺序[推荐] (6)

2.8.2 正文内容 (6)

2.9 长的常量数组 (6)

3 命名 (7)

3.1 常量、编译宏命名 (7)

3.2 变量命名 (7)

3.3 函数命名 (8)

4 程序的版式 (9)

4.1 缩进:统一使用编辑器的TAB键进行缩进,同时设定其缩进量为系统默认 (9)

4.2 空行:相对独立的程序块之间、变量说明之后必须加空行 (10)

4.3 代码行 (11)

4.3.1 一行代码只做一件事情 (11)

4.3.2 if、for、while、do 等语句自占一行 (11)

4.3.3 程序块的分界符独占一行 (12)

4.3.4 多运算符的代码行,不使用默认优先级 (13)

4.3.5 不要使用难懂的技巧性很高的语句 (13)

4.4 空格 (14)

4.4.1 必要时留空格,使代码更清晰[推荐] (14)

4.4.2 二元操作符前后留空格 (14)

4.4.3 一元操作符前后不留空格 (14)

4.5 长行拆分 (15)

4.5.1 较长的语句(>80字符)要分成多行书写 (15)

4.5.2 循环、判断等语句要在低优先级操作符处划分新行 (16)

4.5.3 函数或过程中的参数较长,则要进行适当的划分 (16)

5 全局变量定义时初始化 (16)

6 注释 (17)

6.1 必须加注释的位置[推荐] (17)

6.2 注释应适量且准确[推荐] (17)

6.3 函数接口的注释格式 (17)

6.4 变量的注释格式[推荐] (18)

6.5 注释与所描述内容进行同样的缩排 (19)

6.6 将注释与其上面的代码用空行隔开 (20)

6.7 程序块结束行加注释[推荐] (20)

7 EEPROM数据保护 (21)

8 程序失效复位处理[推荐] (21)

9 检查 (21)

9.1 数组下标检查 (21)

9.2 指针检查 (22)

9.3 易混淆操作符检查 (22)

9.4 右值变量检查 (22)

9.5 差1错误检查 (22)

9.6 变量溢出检查 (23)

9.7 类型转换检查 (23)

1编辑器

程序统一使用VC的编辑器。

2程序文件的结构

如果没有涉及使用平台化的用户文件,通常只能有一个用户主程序文件。

用户程序文件的内容按说明、编译宏定义、包含文件、用户数据类型定义、管脚定义、常量、变量、正文、长的常量数组的顺序编写。

2.1说明

说明:说明位于用户程序文件的开头,主要内容有:

(1)版权信息。

(2)文件名称。

(3)程序说明:说明程序用途、功能及使用方法等。

(4)版本记录。

(5)开发环境。

示例:

//版权信息

//Copyright (c) 2001, 海湾安全技术有限公司产品开发部

// All rights reserved.

// 文件名称:filename.c

// 程序说明:GST5000控制器主程序……

//版本记录:

//V101:

//1、增加警铃输出延时;

//2、修正****Bug。

//作者:***

//时间:****.**.**

//V100:正式发布。

//作者:***

//时间:****.**.**

//编译工具:Keil C 6.0

2.2编译宏定义

说明:同一产品的不同硬件程序和系列产品的程序,应采用编译宏定义,来减少维护的工作量;

2.3包含文件

说明:包含引用的标准库头文件以及用户头文件。用#include 格式来引用标准库的头文件(编译器将从标准库目录开始搜索)。用#include “filename.h” 格式来引用非标准库的头文件(编译器将从用户的工作目录开始搜索)。

示例:

#include

#include “user_header.h”

2.4用户数据类型定义

说明:定义结构、联合、枚举以及其它非标准的数据类型;

示例:

typedef union MacData //联合

{

unsigned char Buf[16]; //缓冲区

struct //结构

{

unsigned char Mac; //类型

unsigned char Len; //长度

unsigned char Addr; //地址

unsigned char Data[13]; //数据

}Msg;

};

#define uint unsigned int;//自定义数据类型

2.5管脚定义

说明:定义本程序使用的管脚。

2.6常量

说明:

●有特定意义的常数必须进行宏定义;

●同一特定意义的常数如在多个地方应用,必须进行宏定义;

●调试维护需要调整的常数必须进行宏定义;

●无特定意义的循环上、下限,不需进行宏定义。

2.7变量

说明:定义程序所使用的变量。

●有关联的、相同地址空间、初始化、不需初始化的变量要放在一起;

2.8正文

2.8.1正文顺序[推荐]

说明:

●应按主程序和中断程序的共用子程序、各中断相关子程序和中断程序、主程序相

关子程序和主程序;

●关系紧密的子程序应放在一起,并按照从低到高的层次放。尽量取消函数的声明,

可通过注释说明;

●除主程序和中断程序外,在不影响可读性的情况下不应设置只一个地方调用的函

数。

2.8.2正文内容

说明:包含初始化程序、主循环体程序和中断程序。

2.8.2.1初始化程序[推荐]

说明:应按管脚初始化、RAM初始化、内部设置初始化、外设初始化、打开定时器、打开中断的顺序进行初始化。

2.8.2.2主循环程序和中断程序[推荐]

说明:

●定时应采用计时器处理,不能存在长时间的停滞等待;

●不允许多个事件使用一个软件计时器。计时器宜采用倒计时;

●中断事件的响应如果在主循环能够完成响应,应放在主循环。

2.9长的常量数组

说明:在此定义长的常量数组,如字库、图案等。

3命名

命名的风格必须保持一致。

3.1常量、编译宏命名

说明:常量、编译宏全部大写,单词间使用下划线分隔。

示例:

#define MAIN_DELAY_NUM 1

3.2变量命名

说明:通常,在变量中加入前缀以增进人们对程序的理解。其前缀的通用格式为:数据类型缩写+模块名+。同时,为了区分全局和局部变量,也兼顾命名的方便,特采用如下变量命名规则:

●全局变量大小写结合,数据类型缩写后,单词首字母大写;

●局部变量全部小写,数据类型缩写后,使用下划线分隔各单词;局部变量在不影

响可读性的前提下,可以不写数据类型;

●unsigned char类型的缩写不写;

●汇编中,与MPU数据总线(如8位、16位)一致数据类型的缩写不写。

数据类型缩写:

boolean b

char c

long l

float f

double d

int i

unsigned int ui

指针p

结构s

输入管脚bi

输出管脚bo

示例:

//管脚定义

sbit biFire = P1^1;

sbit boFireRelay = P1^2;

//全局变量定义

bit bFault;

unsigned char FireAckNum;

unsigned char DeviceStatus[242];

unsigned char* pDevice;

char cSampleValue;

long lTime;

long* plEntry;

float fRadius;

double dArea;

int iFireRelayDelay;

unsigned int uiFaultRelayDelay;

unsigned int uiSounderDelay[4];

void GetSampleValue(void)

{

unsigned char temp;

bit b_fault;

long l_fire_delay;

}

3.3函数命名

说明:函数命名应当直观且可以拼读,可望文知意,不必进行“解码”。标识符采用英文单词或其组合,便于记忆和阅读,并且每个单词的首字母大写,长单词可以缩写,但缩写要使用大家基本认可的,并且必要是进行注释说明。

下单词的缩写能够被大家基本认可:

temp 可缩写为tmp;

flag 可缩写为flg ;

statistic 可缩写为stat ;

increment 可缩写为inc;

message 可缩写为msg;

4程序的版式

4.1缩进:统一使用编辑器的TAB键进行缩进,同时设定其缩进量为系统默认

说明:程序要采用统一缩进风格编写。在下列情况下,必须采用缩进:函数或过程、类的定义、结构的定义、枚举的定义、判断和循环语句中的程序都要采用如上的缩进方式。但如果同一程序块的缩进级数太多(一般不超过5级),可考虑使用函数减少缩进级数。

示例:

//风格良好的格式

void Function(float x, float y, float z)

{

if (…)

{

while (…)

{

} // end of while

} // end of if

}

//风格很差的格式

void Function(float x, float y, float z)

{

if (…)

{

while (…)

{

} // end of while

} // end of if

}

4.2空行:相对独立的程序块之间、变量说明之后必须加空行

说明:空行起着分隔程序段落的作用。空行得体(不过多也不过少)将使程序的布局更加清晰。空行不会浪费内存,虽然打印含有空行的程序是会多消耗一些纸张,但是值得。所以不要舍不得用空行。

示例:

如下例子不符合规范。

if (!IsValidNI (ni))

{

... // program code

}

repssnInd = ssnData[index].repssnIndex;

repssnNI = ssnData[index].NI;

应如下书写

if (!IsValidNI (ni))

{

... // program code

}

repssnInd = ssnData[index].repssnIndex;

repssnNI = ssnData[index].NI;

4.3代码行

4.3.1一行代码只做一件事情

说明:如只定义一个变量,或只写一条语句。这样的代码容易阅读,并且方便于写注释。

示例:

(a)为风格良好的代码行

int width; // 宽度

int height; // 高度

int depth; // 深度

x = a + b;

y = c + d;

z = e + f;

(b)为风格不良的代码行。

int width, height, depth; // 宽度高度深度

X =a + b; y = c + d; z = e + f;

4.3.2if、for、while、do 等语句自占一行

说明:执行语句不得紧跟其后。不论执行语句有多少都要加{}。这样可以防止书写失误示例:

(a)为风格良好的代码行

if (width < height)

{

DoSomeThing();

}

for (initialization; condition; update)

{

DoSomeThing ();

}

(b)为风格不良的代码行。

if (width < height) DoSomeThing ();

Other();

for (initialization; condition; update)

DoSomeThing ();

Other();

4.3.3程序块的分界符独占一行

说明:程序块的分界符(如C/C++语言的大括号‘{’和‘}’)应各独占一行并且位于同一列,同时与引用它们的语句左对齐。

示例:如下例子不符合规范。

for (...) {

... // program code

}

if (...)

{

... // program code

}

void ExampleFun( void )

{

... // program code

}

应如下书写。

for (...)

{

... // program code

}

if (...)

{

... // program code

}

void ExampleFun ( void )

{

... // program code

}

4.3.4多运算符的代码行,不使用默认优先级

说明:注意运算符的优先级,并用括号明确表达式的操作顺序,防止阅读程序时产生误解,防止因默认的优先级与设计思想不符而导致程序出错。

示例:下列语句中的表达式

word = (high << 8) | low (1)

if ((a | b) && (a & c)) (2)

if ((a | b) < (c & d)) (3)

如果书写为

high << 8 | low

a |

b && a & c

a |

b <

c & d

由于

high << 8 | low = ( high << 8) | low,

a |

b && a &

c = (a | b) && (a & c),

(1)(2)不会出错,但语句不易理解;

a |

b <

c &

d = a | (b < c)& d,(3)造成了判断条件出错。

4.3.5不要使用难懂的技巧性很高的语句

说明:高技巧语句不等于高效率的程序,实际上程序的效率关键在于算法。

示例:如下表达式,考虑不周就可能出问题,也较难理解。

* statPoi ++ += 1;

* ++ statPoi += 1;

应分别改为如下。

*statPoi += 1;

statPoi++; // 此二语句功能相当于“* statPoi ++ += 1; ”

++ statPoi;

* statPoi += 1; // 此二语句功能相当于“* ++ statPoi += 1; ”

4.4空格

4.4.1必要时留空格,使代码更清晰[推荐]

说明:采用这种松散方式编写代码的目的是使代码更加清晰。如果语句已足够清晰则不需要加空格,如多重括号间不必加空格,像const、virtual、inline、case 等关键字之后,为了突出关键字,至少要留一个空格。像if、for、while 等关键字之后应留一个空格再跟左括号‘(’,以突出关键字。

4.4.2二元操作符前后留空格

说明:如“=”、“+=”“>=”、“<=”、“+”、“*”、“%”、“&&”、“||”、“<<”,“^”等二元操作符的前后应当加空格。

4.4.3一元操作符前后不留空格

说明:一元操作符如“!”、“~”、“++”、“--”、“&”(地址运算符)等前后不加空格。“[]”、“.”、“->”这类操作符前后不加空格。

示例:

(1) 逗号、分号只在后面加空格。

int a, b, c; //良好的风格

int a,b,c; //不好的风格

(2)比较操作符, 赋值操作符"="、"+=",算术操作符"+"、"%",逻辑操作符"&&"、"&",

位域操作符"<<"、"^"等双目操作符的前后加空格。

if (currentTime >= MAX_TIME_VALUE)

a =

b + c;

a *= 2;

a =

b ^ 2;

(3)"!"、"~"、"++"、"--"、"&"(地址运算符)等单目操作符前后不加空格。

*p = 'a'; // 内容操作"*"与内容之间

flag = !isEmpty; // 非操作"!"与内容之间

p = &mem; // 地址操作"&" 与内容之间

i++; // "++","--"与内容之间

(4)"->"、"."前后不加空格。

p->id = pid; // "->"指针前后不加空格

(5) if、for、while、switch等与后面的括号间应加空格,使if等关键字更为突出、明显。

if (a >= b && c > d)

4.5长行拆分

4.5.1较长的语句(>80字符)要分成多行书写

说明:较长的语句(>80字符)要分成多行书写,长表达式要在低优先级操作符处划分新行,操作符放在新行之首,划分出的新行要进行适当的缩进,使排版整齐,语句可读。

示例:

permCountMsg.head.len = NO7_TO_STAT_PERM_COUNT_LEN

+ STAT_SIZE_PER_FRAM * sizeof( _UL );

actTaskTable[frameId * STAT_TASK_CHECK_NUMBER + index].occupied

= statPoi[index].occupied;

actTaskTable[taskNo].durationTrueOrFalse

= sysGetSccpStatisticState( statItem );

reportOrNotFlag = ((taskNo < MAX_ACT_TASK_NUMBER)

&& (IsStatItemValid (statItem))

&& (actTaskTable[taskNo].ResultData != 0));

4.5.2循环、判断等语句要在低优先级操作符处划分新行

说明:循环、判断等语句中若有较长的表达式或语句,则要进行适应的划分,长表达式要在低优先级操作符处划分新行,操作符放在新行之首。

示例:

if ((taskNo < maxActTaskNumber)

&& (IsStatItemValid (statItem)))

{

... // program code

}

for (i = 0, j = 0; (i < BufferKeyword[wordIndex]. WordLength)

&& (j < NewKeyword. WordLength); i++, j++)

{

... // program code

}

4.5.3函数或过程中的参数较长,则要进行适当的划分

说明:若函数或过程中的参数较长,则要进行适当的划分。

示例:

StrCompare( (BYTE *) & statObject,

(BYTE *) & (actTaskTable[taskNo].statObject),

sizeof (_STAT_OBJECT));

FlashActDuration( statItem, frameId *STAT_TASK_CHECK_NUMBER

+ index, statObject );

5全局变量定义时初始化

变量定义的同时初始化,未初始化的变量需注释其原因。

6注释

6.1必须加注释的位置[推荐]

说明:注释符有块注释“/*…*/”行注释和“//…”。应使用“//…”进行注释,使用“/*…/*”进行代码屏蔽。注释通常用于:

●用户程序文件开头的说明;

●编译宏定义、结构和联合定义、管脚定义、常量、重要变量;

●重要的代码行或段落提示;

●函数接口说明。

6.2注释应适量且准确[推荐]

说明:虽然注释有助于理解代码,但不必要过多注释,也不可与代码体现的意思不一致。注释是对代码的“提示”,而不是文档,应该“点到为止”。注释不仅要提示代码,还要体现其显示意义。过多的注释,会让人眼花缭乱。如果代码本来就是清楚的,则不必加注释;否则多此一举,令人厌烦。不一致的注释,更让人费解。

应从标识符命名、程序流程上下功夫,让代码本身就能说明自己,进而减少注释。

示例:多余的注释

i++; // i 加1,

6.3函数接口的注释格式

说明:对函数接口进行正确而必要的注释,有助于该函数的使用和维护,甚至有助于在代码级上测试该函数。

示例:函数接口的注释

//函数介绍:

//输入参数:

//输出参数:

//返回值:

//备注:

//过程参数:(汇编中,过程参数也是需要注明的。)

void DrawTriangle(float x, float y, float z)

{

if (…)

{

while (…)

{

} // end of while

} // end of if

}

6.4变量的注释格式[推荐]

说明:全局变量要有较详细的注释,其注释的内容包括:

●功能;

●取值范围;

●存取注意事项;

●存取函数或过程;

●其它说明;

示例:变量的注释。

// active statistic task number

#define MAX_ACT_TASK_NUMBER 1000

#define MAX_ACT_TASK_NUMBER 1000 // active statistic task number

示例:可按如下形式说明枚举/数据/联合结构。

// sccp interface with sccp user primitive message name

enum SCCP_USER_PRIMITIVE

{

N_UNITDATA_IND, // sccp notify sccp user unit data come

N_NOTICE_IND, // sccp notify user the No.7 network can not

// transmission this message

N_UNITDATA_REQ, // sccp user's unit data transmission request };

示例:全局变量要有较详细的注释。

// The ErrorCode when SCCP translate

//Global Title failure, as follows // 变量作用、含义

// 0 -SUCCESS

// 1 -GT Table error

// 2 -GT error Others -no use // 变量取值范围

// only function SCCPTranslate() in

// this modual can modify it, and other

// module can visit it through call

// the function GetGTTransErrorCode() // 使用方法

unsigned char g_uchGTTranErrorCode;

6.5注释与所描述内容进行同样的缩排

说明:可使程序排版整齐,并方便注释的阅读与理解。

示例:

//如下例子,排版不整齐,阅读稍感不方便

void ExampleFun( void )

{

// code one comments

CodeBlock One

// code two comments

CodeBlock Two

}

//应改为如下布局。

void ExampleFun( void )

{

// code one comments

CodeBlock One

// code two comments

CodeBlock Two

}

6.6将注释与其上面的代码用空行隔开

说明:使人能很清晰的看出,注释与被注释的代码。

示例:如下例子,显得代码过于紧凑。

// code one comments

program code one

// code two comments

program code two

应如下书写

// code one comments

program code one

// code two comments

program code two

6.7程序块结束行加注释[推荐]

说明:当代码段较长,特别是多重嵌套时,在程序块的结束行右方加注释标记,以表明某程序块的结束,这样做可以使代码更清晰,更便于阅读。

示例:参见如下例子。

if (...)

{

// program code

相关文档