找回密码
 立即注册
Qt开源社区 门户 查看内容

嵌入式代码编写规范

2019-4-12 15:35| 发布者: admin| 查看: 771| 评论: 0

摘要: 吴鉴鹰单片机开发板地址淘宝店铺:【吴鉴鹰的小铺】地址:【https://item.taobao.com/item.htm?_u=ukgdp5a7629id=524088004171】该文章是小单在网上搜集的有关嵌入式代码编码规范,用于规范自己的代码,增强可读性, ...
吴鉴鹰单片机开发板地址

淘宝店铺:【吴鉴鹰的小铺】

地址:【https://item.taobao.com/item.htm?_u=ukgdp5a7629&id=524088004171】



该文章是小单在网上搜集的有关嵌入式代码编码规范,用于规范自己的代码,增强可读性,非标准规范。小单希望同学们能强制自己形成良好的编码风格,有利于开发大规模程序而不显得杂乱。 参考STM32固件库编码风格和FreeRTOS编码风格。

一、工程文件组织结构

    新建工程文件应包含以下全部或部分文件夹:


  • usrSrc:用户源文件,用来存放.c文件和其他的源文件。main.c应放在这里。

  • usrInc:用户头文件,用来存放.h文件。

  • usrDoc:用户说明文档,用来存放用户在开发过程中书写的文档,一般为.txt格式。例如Readme.txt,指令说明等。

  • Src:引用库的源文件。

  • Inc:应用库的头文件。

  • Lib:引用的库文件。



    一个工程一定要包含一个main.c文件,只用来存放main函数。其余函数的定义应在相应的.c文件中,声明在相应的.h文件中。

二、源文件

    文件头,文件的简介

/*************************************************************************
* Copyright (c) 2018, Xiaodan
* All rights reserved.
*
 * File name    :   USB_Ctrl.c
 * Brief        :   USB API source code.
*               Introduce the main function or content of this document briefly.
 * Revision     :   1.01
 * Author       :   Xiaodan
 * Date         :   2018.11.06
 * Update       :   Introduce the difference from previous version.
*************************************************************************/

  •  必要的注释和说明


    源文件应该只包含它自己的头文件,其他的头文件在他自己的头文件中包含。

    源文件中只声明局部函数,全局函数在头文件中声明。

    全局变量在相应的源文件中定义,在头文件中用extern声明。

    类型定义在头文件中定义。

/* Includes -----------------------------------------------------------*/
/*only include it's own header file, the others header file included by USB_Ctrl.h*/
#include "USB_Ctrl.h"
     
/* Declaration --------------------------------------------------------*/
/*here are the local function declare, global fuction declare in header file.*/
void delay( uint32_t n);
/* Global variable ----------------------------------------------------*/

  • 函数头


/******************************************************
 * Brief     : Delay n ms
 * Parameter :
 *           n: the number of delay microsecond.
 * Return    : None.
*******************************************************/
void delay( uint32_t n)
{
for( i=0; i<110; i++)
;
}

三、头文件


  • 文件头,文件的简介

/*************************************************************************
- Copyright (c) 2018, Xiaodan
- All rights reserved.
-
 - File name    :   USB_Ctrl.h
 - Brief        :   The header file of USB_Ctrl.c.
 - Revision     :   1.01
 - Author       :   Xiaodan
 - Date         :   2018.11.06
 - Update       :   Introduce the difference from previous version.
*************************************************************************/

  • 必要的注释和声明

/* Includes -----------------------------------------------------------*/
/* Define -------------------------------------------------------------*/
/* Typedef ------------------------------------------------------------*/
typedef unsigned int apiStatus;     //api return code
/* Enume --------------------------------------------------------------*/
/* Extern -------------------------------------------------------------*/
/* Declaration --------------------------------------------------------*/

四、命名规则


  • 参考FreeRTOS命名规则,MISRA C规范。

  • 定义变量时尽量使用uint8_t 、uint16_t 、uint32_t等。头文件为stdint.h。


typedef signed char        int8_t;
typedef short              int16_t;
typedef int                int32_t;
typedef long long          int64_t;
typedef unsigned char      uint8_t;
typedef unsigned short     uint16_t;
typedef unsigned int       uint32_t;
typedef unsigned long long uint64_t;

  • uint32_t类型的变量使用前缀ul,这里’u’表示’unsigned’,’l’表示’long’


  • uint16_t类型的变量使用前缀us,这里’u’表示’unsigned’,’s’表示’short’

  • uint8_t类型的变量使用前缀uc,这里’u’表示’unsigned’,’c’表示’char’

  • 枚举类型变量使用前缀e

  • 指针类型变量在类型基础上附加前缀p,比如指向uint16_t的指针变量前缀为pus

  • 与MISRA指南一致,char类型变量仅被允许保存ASCII字符,前缀为c

  • 与MISRA指南一致,char *类型变量仅允许指向ASCII字符串,前缀为pc

  • 宏定义全部使用大写,两个单词之间用下划线隔开。

  • 具有文件作用域的对象尽量声名为static的。

  • 全局变量加前缀’g_’。整个工程都可以用的变量,不局限于文件作用域。

五、代码风格

  • 缩进:缩进使用制表符,一个制表符等于4个空格。

  • 注释:注释单行不超过80列,特殊情况除外。

  • 布局:源代码应被设计成尽可能的易于查看和阅读。

    下面的代码片中,第一部分展示文件布局,第二部分展示C代码设计格式。

/* #defines, 在合理的位置添加括号. */  
#define A_DEFINITION ( 1 )

/*
* 随后是Static (文件内部的)函数原型,
* 如果注释有多行,参照本条注释风格---每一行都以’*’起始.
*/
static void prvAFunction( uint32_t ulParameter );
/* 文件作用域变量(本文件内部使用),要在函数体定义之前. */
static BaseType_t xMyVariable.

/* 每一个函数的结束都有一行破折号,破折号与下面的第一个函数之间留一行空白。*/
/*-----------------------------------------------------------*/
void vAFunction( void )
{
/* 函数体在此定义,注意要用大括号括住 */
}
/*-----------------------------------------------------------*/
static UBaseType_t prvNextFunction( void )
{
/* 函数体在此定义. */
}

/*-----------------------------------------------------------*/
     
/*
 * 函数名字总是占一行,包括返回类型。 左括号之前没有空格左括号之后有一个空格,
* 每个参数后面有一个空格,参数的命名应该具有一定的描述性.
*/
void vAnExampleFunction( long lParameter1, unsigned short usParameter2 )
{
/* 变量声明没有缩进. */
uint8_t ucByte;
   /* 代码要对齐.  大括号占独自一行. */
   for( ucByte = 0U; ucByte < fileBUFFER_LENGTH; ucByte++ )
{
/* 这里再次缩进. */
   }
}

/*
 * for、while、do、if结构具有相似的模式。这些关键字和左括号之间没有空格。
 * 左括号之后有一个空格,右括号前面也有一个空格,每个分号后面有一个空格。
 * 每个运算符的前后各一个空格。使用圆括号明确运算符的优先级。不允许有0
* 以外的数字(魔鬼数)出现,必要时将这些数字换成能表示出数字含义的常量或
 * 宏定义。
*/
for( ucByte = 0U; ucByte < fileBUFFER_LENGTH; ucByte++ )
{
}

while( ucByte < fileBUFFER_LENGTH )
{
}

/*
* 由于运算符优先级的复杂性,我们不能相信自己对运算符优先级时刻保持警惕
* 并能正确的使用,因此对于多个表达式运算时,使用括号明确优先级顺序
*/
if( ( ucByte < fileBUFFER_LENGTH ) && ( ucByte != 0U ) )
{
ulResult = ( ( ulValue1 + ulValue2 ) - ulValue3 ) * ulValue4;
}

/* 条件表达式也要像其它代码那样对齐。 */
#if( configUSE_TRACE_FACILITY == 1 )
{
/* 向TCB增加一个用于跟踪的计数器. */
   pxNewTCB->uxTCBNumber = uxTaskNumber;
}
#endif

/*方括号前后各留一个空格*/
ucBuffer[ 0 ] = 0U;
ucBuffer[ fileBUFFER_LENGTH - 1U ] = 0U;
   

六、编程思想


  •  将特定功能的代码封装成函数。

七、C语言编程规则

参考MISRA

Rule1:不得使用三元操作符(? : )。

Rule2:不得残留被注释掉的废代码。

Rule3:所有标识符不超过31字符。

Rule4:不同名空间中的变量名不得相同。

例如:

typedef struct MyStruct {... } MyStruct; (违规)

struct Person {

char* name;
       ...

};
char   name[32];     (违规)

Rule5: 不得使用char, int, float, double, long等基本类型,应该用stdint.h中定义的类型显示表示类型的大小,如uint16_t、int32_t等。

Rule6:禁止使用八进制数。(因为086U这样的常数很容易引起误解)。

Rule7:不得定义与外部作用域中某个标识符同名的对象,以避免遮盖外部作用域中的标识符。

Rule8:具有文件作用域的对象尽量声名为static的。

Rule9:自动对象(栈对象)使用前必须赋初值。

Rule10:操作符&&和||的右侧表达式不得具有副作用(side-effect)。

也就是说,象 if (x == 20 && ++y == 19)这样的表达式被禁止。

Rule11:不得对有符号数施加位操作,例如 1 << 4 将被禁止,必须写 1UL << 4。

Rule12:不得对有副作用的表达式施加sizeof操作符。

Rule13: 除了循环控制语句,不得使用逗号表达式。

Rule14:不得显式判断浮点数的相等性和不等性。

Rule15:不得遗留“永远不会用到”的代码。

Rule16:除了switch语句,不得使用标号(label)。

Rule17:不得使用goto。

Rule18:不得使用continue。

Rule19:除了switch语句,不得使用break

Rule20:if, else if, else, while, do..while, for语句块必须使用{}括起。

Rule21:循环计数器的值不得在循环体内修改。

Rule22:禁止任何直接和间接的递归函数调用。

Rule23:不应该使用#undef。

Rule24:不得将宏作为参数传给宏函数。

Rule25:在一个宏定义中,#或##符号只能出现一次。

Rule26:禁止指针运算(代之以数组下标运算)。

Rule27:禁止超过两级的指针。

Rule28:禁止使用指向函数的非常量指针。

Rule29:禁止使用setjmp, longjmp。

Rule30:禁止使用atoi, atof, atol。(这个我很赞成,建议使用strtol, strtod等函数)

Rule31:禁止使用abort, exit, getenv。

本文章来源网络,如果原作者不支持咱们转发,请联系删除,谢谢!

喜欢本文的亲们,欢迎点赞







 技术源于积累,成功来自执着

——单片机精讲吴鉴鹰

----------------------------------------------------------------------------------------------------------------------
我们尊重原创,也注重分享,文章来源于微信公众号:单片机精讲吴鉴鹰,建议关注公众号查看原文。如若侵权请联系qter@qter.org。
----------------------------------------------------------------------------------------------------------------------

鲜花

握手

雷人

路过

鸡蛋

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了