Embedded Software Development Programming Standards and Principles

Embedded Software Development Programming Standards and Principles

Introduction

I

Have you ever felt this way: when you see unstructured (messy) code, you instantly lose the desire to continue reading?

Everyone should understand from the title how important programming standards and principles are for every software development engineer.

Beginners writing test programs or small module programs may not feel its importance; however, those with experience in large project development know how crucial code standardization is for them.

About Programming Standards and Principles

II

Programming standards refer to writing concise, maintainable, reliable, testable, efficient, and portable code, thereby improving the quality of product code.

This article focuses on embedded systems, primarily discussing the standards of C language programming.

1. Header Files

For C language, the design of header files reflects most of the system design; unreasonable header file layout is the reason for long compilation times.

Many people include all header files in a single include.h file and then include include.h in every .c source code file. While this makes the code look neat, it actually ignores compilation efficiency issues and also affects the portability of the code.

Principles:

A. Header files should contain interface declarations, not implementations;

B. Header files should have a single responsibility;

C. Header files should include stable dependencies.

Rules:

A. Each .c file should have a corresponding .h file with the same name for declaring public interfaces;

B. Circular dependencies in header files are prohibited;

C. .c/.h files should not include unnecessary header files;

D. Header files should be self-contained;

E. Always write internal #include guards (using #define protection);

F. Defining variables in header files is prohibited;

G. External function interfaces and variables should only be used through included header files, and using extern in .c files to access external function interfaces and variables is prohibited;

H. Including header files in extern “C” is prohibited.

Suggestions:

A. A module typically contains multiple .c files, and it is recommended to place them in the same directory, with the directory name being the module name. To facilitate external users, it is recommended that each module provide a .h file named after the directory;

B. If a module contains multiple sub-modules, it is recommended that each sub-module provide an external .h file named after the sub-module (to reduce the difficulty for interface users);

C. Header files should not use non-standard extensions, such as .inc;

D. Consistent header file arrangement for the same product.

2. Functions

The key points of function design: write clean functions while effectively organizing the code.

Requirements for clean functions: the code should be simple and direct, not hiding the designer’s intent, and should be organized organically with clean abstractions and straightforward control statements.

Principles:

A. A function should accomplish only one task;

B. Duplicate code should be extracted into functions whenever possible.

Rules:

A. Avoid overly long functions; new functions should not exceed 100 lines (excluding empty lines and comments);

B. Avoid deep nesting of function code blocks; new function code blocks should not exceed 4 levels of nesting;

C. Reentrant functions should avoid using shared variables; if necessary, they should be protected through mutual exclusion (disabling interrupts, semaphores);

D. The responsibility for checking parameter validity should be uniformly defined within the project team/module;

E. All error return codes from functions should be comprehensively handled;

F. Design functions with high fan-in and reasonable fan-out (less than 7);

G. Dead code (unused functions and variables) should be removed promptly.

Suggestions:

A. Use const for parameters that do not change;

B. Functions should avoid using global variables, static local variables, and I/O operations; unavoidable cases should be centralized;

C. Check the validity of all non-parameter inputs to functions, such as data files, public variables, etc.;

D. The number of parameters for functions should not exceed 5;

E. Avoid using variable-length parameter functions except for printing functions;

F. All functions declared and defined within the source file should add the static keyword unless they are externally visible.

3. Identifier Naming and Definition

Program naming is crucial; if naming is not standardized, over time, even the author may not remember what their own code means.

3.1 General Naming Rules

Common naming styles:

A. Use underscores „_‟ to separate words, such as text_mutex;

B. Mixed case letters, such as ReadRFCText.

Rules:

A. Identifiers should be clear and meaningful, using complete words or commonly understood abbreviations to avoid misunderstandings;

B. Avoid using abbreviations for words other than common ones, and do not use Pinyin;

C. Maintain a consistent naming style within the product/project team.

Suggestions:

A. Use correct antonyms to name mutually exclusive variables or functions with opposite actions;

B. Avoid using numeric identifiers in names unless logically necessary;

C. Do not prefix identifiers with module, project, product, or department names;

D. Maintain consistent naming styles for identifiers in platform/driver adaptation code;

E. When refactoring/modifying parts of the code, maintain consistency with the original naming style.

3.2 File Naming Rules

Since different systems handle file name case sensitivity differently, it is recommended to use lowercase characters for file naming.

3.3 Variable Naming Rules

First, global variables are very dangerous; using prefixes makes global variables more noticeable, prompting developers to be more cautious with their use.

Secondly, fundamentally, global variables should be avoided as much as possible. Adding g_ and s_ prefixes makes global variable names look ugly, thus encouraging developers to use them less.

Rules:

A. Global variables should have a “g_” prefix, and static variables should have an “s_” prefix;

B. Single-byte variable names are prohibited, but defining i, j, k as local loop variables is allowed;

C. Use a noun or adjective + noun structure to name variables.

3.4 Function Naming Rules

A. Function names should reflect the action the function performs, generally using a verb or a verb + noun structure;

B. Function pointers should follow the function naming rules except for the prefix.

3.5 Macro Naming Rules

A. For defining constants such as numbers or strings, it is recommended to use all uppercase letters with underscores between words (the same applies to enumerations);

B. Except for special identifiers like header files or compilation switches, macro definitions should not start or end with an underscore.

4. Variables

Principles:

A. A variable should have only one function; it should not be used for multiple purposes;

B. Structures should have a single function; do not design overly complex data structures;

C. Avoid or minimize the use of global variables.

Rules:

A. Prevent local variables from having the same name as global variables;

B. Structures used in communication must pay attention to byte order;

C. It is strictly prohibited to use uninitialized variables as right values;

Suggestions:

A. Construct global variables that can only be modified or created by one module or function, while other related modules or functions only access them, preventing multiple different modules or functions from modifying or creating the same global variable;

B. Use interface-oriented programming ideas to access data through APIs: if data from this module needs to be exposed to external modules, provide interface functions to set and get, while ensuring mutual exclusion for accessing global data;

C. Initialize variables before their first use, with the initialization location as close to the usage location as possible;

D. Clearly define the initialization order of global variables to avoid cross-module initialization dependencies;

E. Minimize unnecessary default type conversions and forced conversions.

5. Macros and Constants

Since macros are just simple code replacements, they do not compute parameters like functions do before passing them.

Rules:

A. When using macros to define expressions, use complete parentheses;

Non-standard:#define RECTANGLE_AREA(a, b) a * b

Standard:#define RECTANGLE_AREA(a, b) ((a) * (b))

B. Place multiple expressions defined by macros within curly braces;

C. When using macros, parameters should not change;

#define SQUARE(a) ((a) * (a))

int a = 5;

int b;

Non-standard:

b = SQUARE(a++);

Standard:

b = SQUARE(a);

a++;

Suggestions:

A. Use functions instead of macros whenever possible unless necessary;

B. It is recommended to use const definitions instead of macros for constants;

C. Avoid using statements that change program flow, such as return, goto, continue, break, etc., in macro definitions.

6. Comments

Principles:

A. Excellent code can self-explain and be easily understood without comments;

B. Comments should be clear, precise, and unambiguous to prevent ambiguity;

C. Comments should explain the functionality and intent of the code, clarifying what the code cannot express directly, rather than repeating what the code does.

Rules:

A. When modifying code, maintain all comments surrounding the code to ensure consistency between comments and code. Remove comments that are no longer useful;

B. The file header should be commented, listing: copyright notice, version number, generation date, author name, employee number, content, function description, relationships with other files, modification logs, etc. The header file comments should also include a brief description of function functionality;

C. Function declarations should comment on function functionality, performance, and usage, including input and output parameters, return values, reentrancy requirements, etc.; definitions should detail function functionality and implementation points, such as brief steps of implementation, reasons for implementation, design constraints, etc.;

D. Global variables should have detailed comments, including their functionality, value ranges, and access considerations;

E. Comments should be placed above or adjacent to their corresponding code, not below. If placed above, they should be separated by a blank line from the code above and indented the same as the code below;

F. Avoid using abbreviations in comments unless they are industry-standard or standardized within the subsystem;

G. Maintain a consistent comment style within the same product or project team.

Suggestions:

A. Avoid inserting comments in the middle of a line of code or expression;

B. The comment format for file headers, function headers, global constants, variables, and type definitions should be in a tool-recognizable format.

7. Formatting and Style

Rules:

A. Program blocks should be written with an indentation style, with each level of indentation being 4 spaces;

B. There must be blank lines between relatively independent program blocks and after variable declarations;

C. A statement should not be too long; if it cannot be split, it should be written on a new line. The product can determine how many characters are appropriate for line breaks;

D. Multiple short statements (including assignment statements) should not be written on the same line; only one statement should be written per line;

E. Statements like if, for, do, while, case, switch, default, etc., should occupy their own line;

F. When performing equal operations on two or more keywords, variables, or constants, spaces should be added before and after the operators; for non-equal operations, if the operators are closely related (e.g., ->), no space should be added after;

G. There should be a space between comment symbols (including „/*‟, „//‟, „*/‟) and the comment content.

Explanation

III

Many excellent engineers abroad have summarized articles related to programming standards and principles:

http://www.artima.com/weblogs/viewpost.jsp?thread=331531

Good programming habits take time to develop. If you are in the learning stage, please pay attention to these details.

Embedded Software Development Programming Standards and Principles

Embedded Software Development Programming Standards and Principles

1. A big shot says he reads the STM32 reference manual this way…

2. The new IAR debugging issues with register viewing and STM8 code size optimization are addressed in this article!

3. Five embedded operating systems suitable for STM32, which one will you choose?

4. Despite high demand, why are domestic MCUs still “overlooked” by investors?

5. Huawei at 22 years old, 49 years old, yet he has always been coding!

6. What is the essence of open source, free or freedom?Embedded Software Development Programming Standards and Principles

This article is published with the authorization of the original author from the personal WeChat public account “ID: strongerHuang”. The original public account is carefully organized and maintained by embedded engineer “strongerHuang”. The content shared focuses on: Keil, IAR, STM8, STM32, μC/OS, FreeRTOS, CANOpen, ModBus…

Leave a Comment