嵌入式系统已经在各行各业中得到了广泛的应用,随着人们的生活向信息化,智能化的发展,嵌入式技术将彻底融入到我们的生活,在我们的生活当中扮演越来越重要的角色。对于嵌入式系统来讲,嵌入式软件相当于嵌入式系统的灵魂,整个嵌入式系统如何工作,都是由嵌入式软件来控制的。如何编写高质量,高效率的嵌入式软件在实际项目开发过程中变的越来越重要。本文针对嵌入式软件的特点,从嵌入式软件编程规范和注意事项2个方面来阐述如何编写高质量的嵌入式软件。
一、 嵌入式编程规范
我们在公司进行嵌入式项目开发的时候,并不是你一个人在单打独斗,通常是一个团队在一起战斗。很多人在一起共同完成一个嵌入式项目,通常是每个成员,每个小组完成整个项目中的一个或几个模块。我们编写的代码首先是给人看的,其次才是给机器执行的,这就要求我们团队中的每个人在编写软件的时候,要遵循统一的编程规范和编码风格,提高代码的可读性和可维护性,方便团队成员之间的沟通和交流。在实际项目开发过程中,遵循统一的编程规范相当重要,同学们一定要引起足够的重视,下面我就从代码排版,代码注释,标识符命名,代码可读性和函数设计几个方面来讲解比较通用的嵌入式软件编程规范。
1. 代码排版
1) 程序块要采用缩进风格编写, 缩进的空格数为4个或一个TAB键,设置TAB键为
4个空格.
例如:
int main(int argc, char *argv[])
{
int a=900; //缩进4个空格
}
2) 相对独立的程序块之间、变量说明之后必须加空行
例如:
if (!valid_ni(ni))
{
... // program code
}
//相对独立的程序块之间加空行
repssn_ind = ssn_data[index].repssn_index;
repssn_ni = ssn_data[index].ni;
3) 较长的语句( >80字符)要分成多行书写, 长表达式要在低优先级操作符处划
分新行,操作符放在新行之首, 划分出的新行要进行适当的缩进, 使排版整齐,
语句可读。
例如:
perm_count_msg.head.len = NO7_TO_STAT_PERM_COUNT_LEN
+ STAT_SIZE_PER_FRAM * sizeof( _UL );
4) 不允许把多个短语句写在一行中,即一行只写一条语句。
例如:
rect.length = 0;
rect.width = 0;
5) if、 for、 do、 while、 case、 switch、 default等语句自占一行,且if、 for、do、
while等语句的执行语句部分无论多少都要加括号{}。
例如:
if (pUserCR == NULL) //if语句单独占一行
{ //执行语句只有1条也要加{}
return;
}
6) 程序块的分界符(如C/C++语言的大括号‘ {’和‘ }’)应各独占一行并且位
于同一列, 同时与引用它们的语句左对齐。 在函数体的开始、 类的定义、 结
构的定义、 枚举的定义以及if、 for、 do、 while、 switch、 case语句中的
程序都要采用如上的缩进方式.
例如:
for (...)
{ //{}单独占一行,与for左对齐
... // program code
}
void example_fun(void)
{
... // program code
}
2. 代码注释
1) 注释的原则是有助于对程序的阅读理解,在该加的地方都加了,注释不宜太多也不
能太少, 注释语言必须准确、易懂、简洁,防止注释的二义性.
2) 说明性文件(如头文件.h文件)头部应进行注释, 注释必须列出:版权说明、版本
号、生成日期、作者、内容、功能、 修改日志等, 头文件的注释中还应有函数功能
简要说明。
例如:
Copyright (C), 2004-2018, 华清远见教育科技集团.
File name: // 文件名
Author: Version: Date: // 作者、版本及完成日期
Description: // 用于详细说明此程序文件完成的主要功能,与其他模块
// 或函数的接口,输出值、取值范围、含义及参数间的控
// 制、顺序、独立或依赖等关系
Function List: // 主要函数列表,每条记录应包括函数名及功能简要说明
1. ....
History: // 修改历史记录列表,每条修改记录应包括修改日期、修改
// 者及修改内容简述
1. Date:
Author:
Modification:
2. ...
3) 源文件头部应进行注释, 列出: 版权说明、 版本号、 生成日期、 作者、 模块目的/功能、主要函数及其功能、 修改日志等.
例如:
Copyright (C), 1988-2018, 华清远见教育科技集团.
FileName: test.cpp
Author: Version : Date:
Description: // 模块描述
Function List: // 主要函数及其功能
1. -------
History: // 历史修改记录
David 96/10/12 1.0 build this moudle
4) 函数头部应进行注释,列出函数的功能、输入参数、输出参数、返回值等。
例如:
Function: // 函数名称
Description: // 函数功能、性能等的描述
Input: // 输入参数说明,包括每个参数的作用、取值说明及参数间关系。
Output: // 对输出参数的说明。
Return: // 函数返回值的说明
Others: // 其它说明
5) 边写代码边注释, 修改代码同时修改相应的注释, 以保证注释与代码的一致
性。 不再有用的注释要删除。
6) 注释应与其描述的代码相近, 对代码的注释应放在其上方或右方(对单条语句
的注释)相邻位置, 不可放在下面,如放于上方则需与其上面的代码用空行隔开。
3. 标识符命名
1) 标识符的命名要清晰、 明了, 有明确含义, 同时使用完整的单词或大家基本
可以理解的缩写,避免使人产生误解.
2) 对于变量命名,禁止取单个字符(如i、 j、 k...) ,建议除了要有具体含义外,
还能表明其变量类型。建议在变量前面加前缀 g表示全局变量,m表示形式参数.
例如: int gCount = 0;
3) 命名规范必须与所使用的系统风格保持一致,并在同一项目中统一,比如采用
UNIX的全小写加下划线的风格或大小写混排的方式, 不要同时使用大小写与下
划线混排的方式。
例如: char total_score =0;
4) 函数名应准确描述函数的功能,使用动宾词组为执行某操作的函数命名。
例如:
int input_record( void )
unsigned char get_current_color( void )
5) 除非必要,不要用数字或较奇怪的字符来定义标识符
6) 用正确的反义词组命名具有互斥意义的变量或相反动作的函数等
4. 代码可读性
1) 注意运算符的优先级,并用括号明确表达式的操作顺序,避免使用默认优先级
例如:不要这样写: word = high << 8 | low
而应该写成如下这样: word = (high << 8) | low
2) 避免使用不易理解的数字, 用有意义的标识来替代。 涉及物理状态或者含有物理意
义的常量,不应直接使用数字,必须用有意义的枚举或宏来代替.
例如:
#define TRUNK_IDLE 0
#define TRUNK_BUSY 1
if (Trunk[index].trunk_state == TRUNK_IDLE)
{
Trunk[index].trunk_state = TRUNK_BUSY;
... // program code
}
3) 不要使用难懂的技巧性很高的语句,除非很有必要时.
例如: 不要使用类似这样的难懂的语句 *stat_poi++ += 1; 应该分成多个语句
书写,增强代码可读性.
二、嵌入式编程中的注意事项
嵌入式软件开发和普通软件编程相比,有一些自己的特点,下面从嵌入式软件架构,中断编程,寄存器配置,浮点运算等几个方面来讲解嵌入式编程中的注意事项.
1. 嵌入式系统的软件架构
一个大型的嵌入式软件往往需要根据功能的不同划分成多个软件功能模块。
1) 模块即是一个.c文件和一个.h文件的结合,头文件(.h)中是对于该模块接口的声明;
2) 某模块提供给其它模块调用的外部函数及数据需在.h中文件中冠以extern关键字
声明;
3) 模块内的函数和全局变量需在.c文件开头冠以static关键字声明;
4) 永远不要在.h文件中定义变量!定义变量和声明变量的区别在于定义会产生内存分
配的操作,是汇编阶段的概念;而声明则只是告诉包含该声明的模块在连接阶段从
其它模块寻找外部函数和变量
2. 中断编程
中断是嵌入式系统中重要的组成部分,但是在标准C中不包含中断。 许多编译开发商在标准C上增加了对中断的支持,提供新的关键字用于标示中断服务程序. 类似于__interrupt、#program interrupt等。当一个函数被定义为中断服务处理程序的时候,编译器会自动为该函数增加中断服务程序所需要的中断现场入栈和出栈代码。
中断服务程序需要满足如下要求:
1) 不能有返回值;
2) 不能向中断服务处理程序传递参数;
3) 中断服务处理程序应该尽可能的短小精悍,不要包含耗时的代码
3. 寄存器配置
嵌入式软件是面向硬件底层的软件,我们在对硬件进行编程时,通常是通过配置硬件相关的寄存器来实现的。在配置寄存器时,通常我们只需要配置寄存器的1位或几位,对于其他不需要配置的位,我们要保持不变,不要更改我们不需要配置的位。
例如:我们希望配置寄存器的 GPIOADAT 的第 1位 为 1
我们不能这样写成这样:
GPIOADAT = 0x02; //将其他位设置为 0
而应该写成这样:
GPIOADAT |= 0x02; //保证其他位不变
4. 浮点运算
大多数低档次的单片机都是不支持浮点运算的,因此在实际使用过程中也很少用到,因此为了降低成本,一般都去掉了浮点运算模块,这就带来了一个问题,如果万一要用到浮点运算怎么办?我们可以采用的是“定点”的方法来解决这个问题,就是直接放大10的N次方倍进行整数的计算,可以得出近似值,因此为了不增加不必要的麻烦,应该总是尽量避免使用浮点运算,一般情况也是可以避免的。
5. volatile 关键字的使用
嵌入式开发过程中,在定义硬件寄存器的时候,需要使用volatile关键字。 volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。 如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值。
例如: #define GPIO_DATA (*(volatile unsigned int *)0x90002000)
以上就是我今天要给同学们讲解的嵌入式软件编程规范和注意事项,希望同学们在实际的嵌入式项目开发过程中严格遵循嵌入式软件编程规范和注意事项,开发出高质量,稳定,可靠,维护性高的嵌入式软件。