px-fwlib 0.10.0
Cross-platform embedded library and documentation for 8/16/32-bit microcontrollers generated with Doxygen 1.9.2
5.1 The Piconomix C Coding Style

1. Introduction

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.

2. Library Namespace

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.

3. Modules

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:

#include "px_systmr.h"
void wait(void)
{
// Create a timer object
// Start timer with a 250 ms timeout
// Wait until timer has expired
while(!px_systmr_has_expired(&tmr)) {;}
}
void px_systmr_start(px_systmr_t *systmr, const px_systmr_ticks_t delay_in_ticks)
Start a timer.
Definition: px_systmr.c:35
#define PX_SYSTMR_MS_TO_TICKS(delay_in_ms)
Macro used to convert a timeout in milliseconds to timer ticks.
Definition: px_systmr.h:177
bool px_systmr_has_expired(px_systmr_t *systmr)
See if a timer has expired.
Definition: px_systmr.c:57
Structure to track state of a timer.
Definition: px_systmr.h:75

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.

4. Templates

A place for everything and everything in it's place :)

Template files have been created for consistent organisation of each C module. See:

5. Comments

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:

/**
* See if a timer has expired.
*
* @param systmr Pointer to a timer object
*
* @retval true timer expired
* @retval false timer not expired or timer stopped
*/

6. Indentation, Brace style and Spacing

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:

for(i = 0; i < 10; i++)
{
....for(j = 0; j < 10; j++)
....{
........buffer[i][j] = 0;
....}
}

Observe:

  • There is no space between the for keyword and opening bracket (
  • There is a space before and after the = operator, e.g. i = 0
  • There is no spacing for the unary ++ operator, e.g. i++

7. Naming convention

7.1 Functions

Lower case letters with underscores separating words

The name must have the module name prefixed. Example:

// has_expired() function with "px_systmr_" module name prefix

7.2 Preprocessor defines

CAPITAL letters with underscores separating words

The name must have the module name prefixed. Example:

#define PX_CRC16_POLYNOMIAL 0x8408

7.3 Preprocessor macros

CAPITAL letters with underscores separating words

The name must have the module name prefixed. Example:

#define PX_SYSTMR_MS_TO_TICKS(delay_in_ms) PX_UDIV_ROUND((delay_in_ms) * PX_SYSTMR_TICKS_PER_SEC, 1000ul)

do {} while(0) blocks and brackets () around macro parameters are recommended so that the macro will always expand correctly. Example:

#define PX_BIT_SET_HI(var, bit) do { (var) |= (1ul << (bit)); } while(0)
#define PX_BIT_SET_LO(var, bit) do { (var) &= ~(1ul << (bit)); } while(0)

7.4 Function-like macros

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:

#define px_interrupts_enable() __enable_irq()

7.5 Variables

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:

// Declare global counter value; Needs "px_systmr_" module prefix
uint16_t px_systmr_counter;
void px_systmr_test(int test_val)
{
// Variable declared inside function; Does not need module prefix
int nr_of_items;
...
}

7.6 Typedef

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:

/// Size definition of the tick counter
typedef uint32_t px_sysclk_ticks_t;
uint32_t px_sysclk_ticks_t
Size definition of the tick counter.
Definition: px_sysclk.h:74

7.7 Structures

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:

typedef struct px_list_item_s
{
struct px_list_item_s * next;
struct px_list_item_s * prev;
} px_list_item_t;

7.8 Abbreviations

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

8. Standard Types

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:

int i;
printf("sizeof(i) = %d\n", sizeof(i));

It is therefor imperative to use standard types (uint8_t, uint16_t, ...) to keep the code portable. See px_defs.h : Common definitions. Example:

#include "px_defs.h"
typedef struct
{
uint8_t msg_type; // Unsigned 8-bit value (0 to 255)
int8_t data[13]; // Array of signed 8-bit values (-128 to +127)
uint16_t crc; // Unsigned 16-bit value (0 to 65535)
} msg_t;

9. Debug instrumentation

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:

#include "px_log.h"
PX_LOG_NAME("px_uart");
#define PX_LOG_NAME(name)
Macro to declare a log name string once for each file to reduce code size.
Definition: px_log.h:349

Function parameters can be checked with a PX_LOG_ASSERT() macro. Example:

bool px_uart_rd_u8(px_uart_handle_t * handle, uint8_t * data)
{
// Verify that pointer to handle is not NULL
PX_LOG_ASSERT(handle != NULL);
...
}
#define NULL
NULL pointer.
Definition: px_defs.h:49
#define PX_LOG_ASSERT(expression)
Macro that will test an expression, and block indefinitely if false.
Definition: px_log.h:440
bool px_uart_rd_u8(px_uart_handle_t *handle, uint8_t *data)
See if a received byte is available and store it in the specified location.
Definition: px_uart.c:726
Define UART handle.
Definition: px_uart.h:141

Info, warnings and errors can be reported with PX_LOG_I(), PX_LOG_W() and PX_LOG_E() macros. Example:

{
...
// Set pointer to peripheral data
switch(uart_nr)
{
case PX_UART_NR_1: uart_data = &px_uart1_data; break;
case PX_UART_NR_2: uart_data = &px_uart2_data; break;
default: PX_LOG_E("Invalid peripheral specified"); return false;
}
...
}
#define PX_LOG_E(format,...)
Macro to display a formatted ERROR message.
Definition: px_log.h:377
px_uart_nr_t
Specify UART peripheral.
Definition: px_uart.h:108
bool px_uart_open(px_uart_handle_t *handle, px_uart_nr_t uart_nr)
Open UART peripheral using predefined (default) parameters.
Definition: px_uart.c:472