痞子衡嵌入式:恩智浦SDK驅動程式碼風格、模板、檢查工具

  • 2020 年 3 月 15 日
  • 筆記

  大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家講的是恩智浦 SDK 驅動的程式碼風格

  上周痞子衡受領導指示,給 SE 同事做了一個關於 SDK 程式碼風格的分享。隨著組內新人的增多,這樣的培訓還是很有必要的。一是可以讓新同事通過程式碼風格來快速了解 SDK 驅動程式碼結構,另一方面也有利於新同事養成良好的編碼習慣。
  痞子衡剛畢業時曾經也整理過一篇程式碼風格 《飛思卡爾軟體開發C語言編碼規範》,如今雖已是恩智浦紀元,但規範大多還是相似的,僅有微小更新。這次痞子衡將新版規範的要點提取了出來,並且還提供了標準模板,這樣大家學習起來更加方便。
  另外鑒於領導指定我作為組內同事程式碼風格人肉審查員(大家寫好的程式碼需要由我人肉審查風格),這樣的工作如果真的完全是人工去做,可想而言有多枯燥和低效,因此痞子衡計劃寫一個配套的自動化檢查工具,暫且叫 MCUXpresso SDK Coding Style Checker,歡迎大家來圍觀這個項目。

mcux_sdk_coding_style

1.命名

1.1 變數

變數命名使用 CamelCase (小駱駝峰法),即第一個單詞以小寫字母開始,第二個單詞以及後面的每一個單詞的首字母大寫,例如 myVariableName

  • 作用域可在文件外的全局變數加 g_ 前綴,如 g_myVariableName
  • 使用 static 修飾的全局變數加 s_ 前綴,如 s_myVariableName
  • 局部變數不加任何前綴,如 myVariableName
  • 其他如 volatile, const 修飾或指針型變數,無需任何特殊表示
  • 命名中的大部分單詞都不要縮寫,除非是相當流行的縮寫,如 init 或 config

1.2 宏

宏命名使用下劃線命名法,單詞全大寫,例如 MY_MACRO_NAME

1.3 枚舉

枚舉類型的命名混合了多種命名法,且加了一些特殊前後綴

  • 枚舉類型名使用下劃線命名法,單詞全小寫,且以下劃線開頭
  • 枚舉元素名使用小駱駝峰法,但統一加 k 前綴
  • 可用 typedef 重命名枚舉類型名,使用下劃線命名法,但需加 _t 後綴
  • 枚舉變數名使用小駱駝峰法
typedef enum _my_enumeration_name  {      kMyEnumerator0     = 0x00U,      kMyEnumerator1     = 0x01U,        kMyEnumeratorEnd   = 0x02U,  } my_enumeration_name_t;    static my_enumeration_name_t s_myEnumVariableName;

1.4 結構體

結構體類型的命名混合了多種命名法,且加了一些特殊前後綴

  • 結構體類型名使用下劃線命名法,單詞全小寫,且以下劃線開頭
  • 結構體成員名使用小駱駝峰法
  • 可用 typedef 重命名結構體類型名,使用下劃線命名法,但需加 _t 後綴
  • 結構體變數名使用小駱駝峰法
typedef struct _my_struct_name  {      uint32_t myStructMember0;      uint32_t myStructMember1;  } my_struct_name_t;    static my_struct_name_t s_myStructVariableName;

1.5 函數

函數命名使用 Pascal (大駱駝峰法),即把變數名稱的第一個字母也大寫,例如 MyFunctionName

  • 函數命名可由 [Action][Module][Feature] 組成,動作在前,特性在後。如 InitDeviceClock()
  • 一系列同類函數,可加 MODULE_ 前綴,前綴單詞全大寫。如 SD 卡操作的系列函數,可為 SD_PowerOnCard()、SD_PowerOffCard()

2.程式碼體

2.1 排版

  • 永遠不要使用 Tab 鍵(使用 4 個空格代替 Tab),需要以 4 個空格為單位的縮進
  • 換行符應使用 "unix"(LF),而不是windows(CR + LF)
  • 文件結尾需空一行

2.2 花括弧

不使用 K&R 風格花括弧,左右括弧都需要獨佔一行

2.3 局部變數定義

局部變數定義應總是放在所在最小作用域(即最近的 {} 內)里的最前面,並且一行程式碼僅定義一個變數

void MyFunctionName(void)  {      uint8_t myVariableName0;      uint8_t myVariableName1;        /* 程式碼體 */        for (;;)      {          uint8_t myVariableName2;            /* 程式碼體 */      }  }

2.4 數字

程式碼中所有無符號整型數字,均應加 "U" 後綴

Hex: 0x1234ABCDU  Dec: 1234U

2.5 注釋

僅使用 /* */ 來注釋

/* 注釋風格1,單獨佔一行 */  uint8_t i = 0;    for (; i < 5;)  {      i++; /* 注釋風格2,與程式碼共享一行 */  }

2.6 條件編譯

#endif 後面需要加如下注釋

#if MY_MACRO_NAME    /* 程式碼體 */    #endif /* MY_MACRO_NAME */

2.7 頭文件保護宏

任何一個 .h 文件都需要包含下面格式的頭文件保護宏程式碼,宏的命名與頭文件名保持一致。如文件名為 hello_world.h,則宏名為 _HELLO_WORLD_H_

#ifndef _HEADER_FILENAME_  #define _HEADER_FILENAME_    /* 頭文件內容 */    #endif /* _HEADER_FILENAME_ */

3.整體模板

3.1 源文件(.c)

/* 包含頭文件程式碼 */    #include "template.h"    /*******************************************************************************   * Definitions   ******************************************************************************/    /* 私有(僅本源文件內使用)宏、枚舉、結構體的定義 */    #define MAX_DEVICES  (128U)    /*******************************************************************************   * Variables   ******************************************************************************/    /* 所有全局變數(外部,靜態,常量,指針)的定義 */    uint32_t g_deviceIndex = 0;    static device_config_t s_deviceConfig;    const uint32_t g_maxDevices = MAX_DEVICES;    static volatile uint8_t *s_deviceData;    /*******************************************************************************   * Prototypes   ******************************************************************************/    /* 內部函數(即 static 修飾)的聲明 */    static uint32_t GetDeviceIndex(void);    /*******************************************************************************   * Code   ******************************************************************************/    /* 所有函數(外部,內部)的定義 */    void InitDevice(void)  {      s_deviceConfig.index = 1;      s_deviceConfig.mode = kDeviceMode1;      memset(s_deviceConfig.data, 5, sizeof(s_deviceConfig.data));      s_deviceConfig.isEnabled = true;  }    static uint32_t GetDeviceIndex(void)  {      return s_deviceConfig.index;  }    int main(void)  {      InitDevice();      g_deviceIndex = GetDeviceIndex();        while (1)      {      }  }  

3.2 頭文件(.h)

/*   * Copyright xxx   * All rights reserved.   *   * xxx License   *   * Revision History:   * v1.0 - Description   */    #ifndef _TEMPLATE_H_  #define _TEMPLATE_H_    /* 包含頭文件程式碼 */    #include <stdbool.h>  #include <stdint.h>  #include <string.h>    /*******************************************************************************   * Definitions   ******************************************************************************/    /* 公共(可被其他源文件使用)宏、枚舉、結構體的定義 */    enum _device_mode  {      kDeviceMode0    = 0x00U,      kDeviceMode1    = 0x01U,        kDeviceModeEnd  = 0x02U,  };    typedef struct _device_config  {      uint32_t index;      uint32_t mode;      uint8_t data[16];      bool isEnabled;  } device_config_t;    /* 外部全局變數的聲明 */    extern uint32_t g_deviceIndex;    extern const uint32_t g_maxDevices;    /*******************************************************************************   * API   ******************************************************************************/    #if defined(__cplusplus)  extern "C" {  #endif /*_cplusplus*/    /* 外部函數(可加 extern 修飾)的聲明 */    void InitDevice(void);    #if defined(__cplusplus)  }  #endif /*_cplusplus*/    #endif /* _TEMPLATE_H_ */  

歡迎訂閱

文章會同時發布到我的 部落格園主頁CSDN主頁微信公眾號 平台上。

微信搜索"痞子衡嵌入式"或者掃描下面二維碼,就可以在手機上第一時間看了哦。