by Saheblal Bagwan | March 29, 2021

    Naming conventions for embedded C projects help in several ways. They enable code to be written consistently by everyone on the team. Implementing new functionality is simply following the pattern. Reading code becomes easier as proper names give more context. All of this also aids in debugging.

    Data Types

    The custom data types shall use the following convention

    Data Type Range Description
    myStruct_st NA User defined structure suffixed with st
    myEnum_et NA User defined Enum suffixed with et
    myUnion_ut NA User defined Union suffixed with ut

    Structure declaration:

    
    typedef  struct{
        uint8_t  maxLine_u8;
        uint8_t  maxChars_u8;
        uint8_t  mode_u8;
    }lcdConfig_st;

    Explicitly declare the variables for holes in the structure

    
    typedef  struct{
        uint32_t  maxLine_u32;
        uint16_t  maxChars_u16;
        uint8_t   mode_u8;
        uint8_t   dummyHole_u8;  // dummy variable to fill the hole
    }lcdConfig_st;

     

    Enum declaration:

    
    typedef  enum{
        E_COLOR_RED,
        E_COLOR_BLUE,
        E_COLOR_GREEN,
        E_COLOR_MAX      // always declare _MAX for an enum
    }color_et;

     

    Union declaration:

    
    typedef  union{
        uint16_t  word_u16;
        uint8_t   byteZero_u8;
        uint8_t   byteOne_u8;
    }myUnion_ut;

     

    Identifier Naming Convention

    All the identifiers shall follow the below-naming convention. The naming convention consists of three fields separated by underscores (_) as shown below.

    • Identifier Short Form:
    Identifier Short Form Identifier type and scope
    s Structures
    PORT GPIO Port
    PIN GPIO Pin
    SFR Special Function register
    SBIT Bit of a SFR

     

    • Identifier Name
      • Identifier names should not exceed 32 chars with the first 24 characters being unique.
      • It is recommended to prefix the global identifiers with its module name.
      • The global variables shall follow the Pascal case whereas local variables shall follow the Camel case for identifier naming.
      • Ex: uartReceivedData_u8 (variable following the camel case)
    • Identifier Type
    Sign Size
    u 8,16,32
    s 8,16,32

    Ex:

    
    uartReceivedData_u8 (unsigned 8-bit variable)
    adcTempValue_s8 (signed 8-bit variable to store both +ve and –ve values)
    

    Exception: The structures and Enums will not have the third field. But still, the members will have the third field depending on the type of the identifiers.

    Below are some of the examples for naming the identifiers

    Variables:

    
    adcValue_u16 (variable to store unsigned 16-bit adc value )
    adcTempValue_s8 (signed 8-bit variable to store both +ve and –ve temperature values)

    Arrays/Strings:

    
    glcdFontLookUp_a8 [ ]            (array of GLCD fonts)
    uartReceived_string [ ] (array/string to store the received string from UART)

    Pointers:

    
    lcdDisplay_string                (pointer to a string to be displayed on the LCD)
    adcValue_p16                     (pointer to a 16-bit unsigned variable)
    glcdFontLookUp_pa                (pointer to array of GLCD fonts)
    
    
    lcdConfig_st* lcd_ps;
    lcd_ps->maxLines_u8= 2;
    lcd_ps->maxChars_u8= 16;
    lcd_ps->mode_u8 = 2;

    Constants/Macros:

    
    #define LCD_TIMEOUT_VALUE_U8    (uint8_t)50u
    #define c_lcdTimeoutValue_u8    (uint8_t)50u

    Note: Both of the above conventions can be used for defining a macro/constant.

    PORT/PIN/SFR:

    
    #define PORT_LcdDataBus       PORTB               /* Lcd data bus is connected to PORTB */
    #define PIN_LcdEnable              7       /* Lcd Enable signal is connected to 7th bit of a port */
    #define SFR_SerialBuffer           SBUF                /* SFR holds the received serial data */
    

    Structure/Enums/Unions:

    
    /* Structure and Enum Declaration*/
    lcdConfig_st   s_lcd;
    colors_et e_myCloros; 
    /* Structure and Enum usage */
    s_lcd.maxLines_u8= 2;
    s_lcd.maxChars_u8= 16;
    s_lcd.lcdMode_u8 = 2;
    e_myColors = E_COLOR_RED;

    Function naming convention

    Below naming convention shall be followed for the Function/APIs/Interfaces

    Module: Name of the module to which the interface/function belongs. For the global functions, the module name shall be capital and for the local function, it shall be small.

    The global function prototype (declaration) shall be in its respective .h file. The local functions prototype (declaration) shall be in a .c file prefixed with the static keyword so as limit the scope of the function to a particular .c file.
    Ex:

    
    lcd_init();        /* function to initialize the lcd */
    adc_getAdcValue();   /* function to read the adc value */
    • The function name shall be descriptive of its purpose.
    • No function shall have a name that is a keyword of C.
    • Efforts shall be taken to limit the length of functions to 100 lines.
    • All functions shall have one exit point at the end of the function. ie, the return keyword shall appear only once.
    • Parameters shall be explicitly declared and meaningfully named.

     

    Braces/Parenthesis

    • Braces shall always surround blocks of code, following if, else, switch, while, do and for statements. Single and empty statements shall also be surrounded by braces.
    • The left brace shall appear by itself on the line below the start of the block of code. The right brace shall appear in the same position later in the file.
    • Do not rely on C operator precedence. Use parentheses to explicitly specify the operation.

     

    Comments

    • Use C-style(/* —– */) for multiple line comments instead of // on each line.
    • Do not use nested comments
    • Do not use C-style comments /*……*/ to disable a block of code.
    • Use conditional compilation to disable a block of code: #if (0) ending with #endif.
    • The most useful comments generally precede a block of code. The comments should be at the same indentation level as the block of code. A blank line shall follow the code block.

     

    White Spaces

    • If, else, while, for, switch and return shall be followed by one space.
    • Assignment operators =, +=, -=, /=, %=, &=, |=, ^=, ~=, and != shall be preceded and followed by one white space.
    • Binary operators +, -, *, /, %, <, <=, >, >=, ==, !=, <<, >>, &, &&, and || shall be preceded and followed by one white space.
    • Unary operators +, -, ++, –, ! and ~, shall be written without a space on the operand side and with one space on the other side.
    • The pointer operators * and & shall be written without any space.
    • The left and right brackets of the array subscript operator [ ] shall be without surrounding spaces.
    • The left and right parentheses of the function call operator shall be without surrounding spaces, except the function declaration which shall have one space between the function name and left parenthesis to allow it to be easily located.
    • Each comma separating function parameters shall be followed by one space.
    • Each semicolon separating the elements of a for statement shall be followed by one space.
    • Each semicolon shall follow the statement it terminates without a preceding space.

     

    Indentation/Tabs:

    • Tabs shall not be used. (Tabs size vary by editor)
    • Each indentation level within a module should consist of 3/4 spaces as per programmer preference.
    • Do not use tabs for indentation. (Editors provide the feature to replace the tabs with a specified number of spaces)
    • Within a switch statement, each case statement should be indented and the contents of the case block should be indented once more.

     

    Modules

    • Module names shall consist of lowercase letters, numbers and underscores.
    • All module names shall be unique in their first eight characters, with .h,.c and .cpp used as the suffixes.
    • No module name shall share the name of a standard library header file. For example “stdio” or “math”.
    • Any module containing a main() function shall have the word “main” in its filename.
    • There shall be one header file for each source file and they shall have the same root name.
    • The header file shall identify only the functions, constants and data types about which it is strictly necessary for other modules to know about.
    • No storage for variables shall be declared in a header file.
    • Each source file shall #include the header file of the same name.
    • Source files shall be free of unused include files.
    • No source file shall include another source file.

     

    If-Else and Switch statements.

    • Preferably place the shortest of the if and else-if clauses first.
    • Nested if-else clauses should not be deeper than four levels.
    • Any if statement with an else-if clause shall end with an else clause.
    • When evaluating a variable and a constant, place the constant on the left side of the comparison.
    • The break for each case shall align with the associated case.
    • All switch statements shall contain a default block.

    It is often advised to follow a coding standard than to invent one. So most of our adoption is from the MISRA C standard that is widely used in safety-critical medical and automotive industries. We will cover this in another post