|
px-fwlib 0.10.0
Cross-platform embedded library and documentation for 8/16/32-bit microcontrollers generated with Doxygen 1.9.2
|
A crystal clear, consistent and concise coding style improves readibility, reduces errors and improves maintainability. This page provides a short guide to the C coding style used.
All filenames, defines, typedefs, variables, functions, etc. are prefixed with px_ or PX_
C lacks the ability of C++ to put a collection of typedefs, variables, functions, etc. in a separate namespace so that their names do not clash with existing names.
Consider including this library and another library and both contain and use a generic filename called "defs.h" or a macro called MAX(a, b) or a function called uart_init(). The compiler or linker will generate an error to indicate that there is more than one symbol with the same name.
To decrease the likelihood of a name clash, all file names, functions, typedefs, defines, etc. are prefixed with px_ or PX_ (in the case of #defines). Unfortunately this increases the size of the source code and makes it harder to fit a source line into 80 columns, but it is the pragmatic solution to avoid a possible clash.
All defines, typedefs, variables, functions, etc. are prefixed with the module name
An object-like programming approach is recommended. This means that the data and functionality of an object is encapsulated in an H and C file. A module name must be chosen that is the same or consistent with the filename. This module name must be prefixed to all function names, structures, defines, enumerations, etc.
A good example of this approach is px_systmr.h : Polled software timers. Example usage:
The module name is px_systmr. The file names are "px_systmr.h" and "px_systmr.c". Each function name starts with "px_systmr_" e.g. px_systmr_start(). The px_systmr object's data is encapsulated in a structure called px_systmr_t and each function is passed a pointer to the object, e.g. &tmr.
A place for everything and everything in it's place :)
Template files have been created for consistent organisation of each C module. See:
Doxygen is used to document each module. For more info see 5.2 Using Doxygen source documentation system.
Here is an an example showing how a function is documented:
4 spaces must be used for each indentation level, not tabs. Curly brackets {} must be used, even for single lines
The Allman style (a.k.a. ANSI C or BSD style) is followed. Example:
Observe:
for keyword and opening bracket (= operator, e.g. i = 0++ operator, e.g. i++Lower case letters with underscores separating words
The name must have the module name prefixed. Example:
CAPITAL letters with underscores separating words
The name must have the module name prefixed. Example:
CAPITAL letters with underscores separating words
The name must have the module name prefixed. Example:
do {} while(0) blocks and brackets () around macro parameters are recommended so that the macro will always expand correctly. Example:
lower case letters with underscores separating words are acceptable but all CAPITALS are preferred
If a macro is used with the appearance of a function it is acceptable to use the function naming convention. Example:
Lower case letters with underscores separating words
If the variable is declared outside function or structure scope, then it must have the module name prefixed. Example:
Lower case letters with underscores separating words and "_t" suffix
The name must have the module name prefixed. An "_t" suffix must be added to indicate that it is a typedef. Example:
Lower case letters with underscores separating words and "_s" suffix
The name must have the module name prefixed. An "_s" suffix must be added to indicate that it is a struct. Example:
Often used words have shorter abreviations to reduce the length of names. The abbreviation must be obvious and not obscure. Here is a non-exhaustive list:
| Abbreviation | Full version |
|---|---|
| adr | address |
| buf | buffer |
| cal | calibration |
| cfg | configuration |
| cmd | command |
| def(s) | definition(s) |
| deg_c | Degrees Celcius |
| dev | device |
| en / dis | enable / disable |
| fn | function |
| gfx | graphics |
| idx | index |
| info / warn / err | information / warning / error |
| init | initialize |
| img | image |
| lo / hi | low / high |
| lsb / msb | least significant byte / most significant byte |
| rd / wr / xc | read / write / exchange |
| min / max | minimum / maximum |
| meas | measure |
| nr | number |
| prev | previous |
| pwr | power |
| reg | register |
| res | resource |
| resp | response |
| rst | reset |
| u8 / u16 / u32 | unsigned 8/16/32 bit value |
| s8 / s16 / s32 | signed 8/16/32 bit value |
| sec / min / hr | second / minute / hour |
| str | string (zero terminated) |
| tmr | timer |
| tx / rx | transmit / receive |
| temp | temperature |
| tmp | temporary |
| val | value |
Use standard types (uint8_t, int16_t, bool, true, false, etc.) defined via "px-fwlib/common/px_defs.h" which includes <stdbool.h>, <stddef.h> and <stdint.h>
The size of a variable declared as an int depends on the architecture that it is compiled for. For the 8-bit Microchip AVR architecture, the size of an int will be 16-bits, but for the 32-bit ARM Cortex M0 architecture, the size of an int will be 32-bits. This example will return a different answer depending on which architecture it is compiled for:
It is therefor imperative to use standard types (uint8_t, uint16_t, ...) to keep the code portable. See px_defs.h : Common definitions. Example:
A convenient debug module (px_log.h : Debug log module) has been created to report the flow of an embedded program as well as warnings and errors. The debug output can easily be removed from the final release (by defining PX_LOG=0) with no impact on the execution or code size.
To conserve space, the module name string is declared once at the top of the C file. Example:
Function parameters can be checked with a PX_LOG_ASSERT() macro. Example:
Info, warnings and errors can be reported with PX_LOG_I(), PX_LOG_W() and PX_LOG_E() macros. Example: