A living document of best practices, style guidelines, and tips for writing embedded software.
The malloc() function is not permitted to be used and any dynamic memory allocation should be avoided after initialization. It is incredibly easy to mishandle memory and free routines, and they are unpredictable as to how much time they take to execute. Therefore, they are not safe for safety-critical applications.
All arrays must be statically declared using a fixed number of elements.
Do not allow the possibility of infinite loops. All loops must have a fixed upper bound that can be statically proven and determined. A separate timeout should always be added for code waiting for a hardware event so that the MCU does not stall.
Here is a good example taken from the HERON Style Guide, which illustrates a loop that waits for the PB0 pin to go low.
// good example of a timeout
// built-in UINT16_MAX constant starts at 2^16 - 1
uint16_t timeout = UINT16_MAX;
while (bit_is_set(PINB, PB0) && timeout > 0) {
timeout--
}
For any variables that are modified in an interrupt handler, they must be declared using the volatile keyword.
Variables should be declared at the lowest scope possible. Variable names used in a larger scope should never be repeated for separate variables in a smaller scope in order to avoid variable eclipsing bugs that can be subtle but nasty and hard to detect.
// AVOID
uint16_t counter = 0;
void func(void) {
uint16_t counter = 0;
}
// better example
uint16_t counter = 0;
void func(void) {
uint16_t counter_func = 0;
}
Never use the int type. All integer variables must be declared with explicit sizes to be clear about the maximum value the integer can hold and support. Instead, use types such as uint8_t
, uint16_t
, and uint32_t
so that the size of the integer value is clear and obvious. If you are unsure as to how large an integer variable should be, use uint32_t
by default since the MCU architecture is 32-bit.
It should be noted that integer overflows must be avoided at all times, so make sure that the integer types used are large enough to hold the values they contain.