Skip to content

Instantly share code, notes, and snippets.

@hraftery
Last active February 4, 2025 03:09
Show Gist options
  • Save hraftery/902504b51316d6c4871ddcfbcf4825f3 to your computer and use it in GitHub Desktop.
Save hraftery/902504b51316d6c4871ddcfbcf4825f3 to your computer and use it in GitHub Desktop.
Project wide includes, macros and types.
/**
* \file
*
* \brief General utility functions
*
* \author Heath Raftery
* \date January, 2025
*/
#ifndef __COMMON_H__
#define __COMMON_H__
//=====================
// INCLUDES
//=====================
/** All modules should be using these utils. */
#include "util/assert.h"
#include "util/fault.h"
//=====================
// TYPES
//=====================
/** Use the std bool and ints (uint32_t et al.) liberally */
#include <stdbool.h>
#include <stdint.h>
/**
* A date/time value, as the number of seconds since 00:00 hours, Jan 1, 1970 UTC.
* Compatible with the Unix timestamp, and most time_t implementations.
*/
typedef uint32_t tDatetime;
/** A three-valued logic type. */
typedef enum {
TernaryUnknown = 0, //Can mean other, undecidable, don't care, irrelevant, both, etc.
TernaryTrue = 1, //Values picked from here: https://en.wikipedia.org/wiki/Three-valued_logic#Representation_of_values
TernaryFalse = 2 //careful, both TernaryTrue == true and TernaryFalse == true
} tTernary;
/** Remove ambiguity around the signedness of a char */
typedef unsigned char byte;
/** Like UINT8_MIN/MAX and friends, but for sub byte-sized limits. */
#define UINT2_MIN 0b00
#define UINT2_MAX 0b11
#define UINT3_MIN 0b000
#define UINT3_MAX 0b111
#define UINT4_MIN 0b0000
#define UINT4_MAX 0b1111
#define UINT5_MIN 0b00000
#define UINT5_MAX 0b11111
#define UINT6_MIN 0b000000
#define UINT6_MAX 0b111111
#define UINT7_MIN 0b0000000
#define UINT7_MAX 0b1111111
/** Fill out the unsigned mins, missing from stdint */
#define UINT8_MIN 0
#define UINT16_MIN 0
#define UINT32_MIN 0
//=====================
// DEFINES
//=====================
/** Fiddle with bits */
#define GET_BIT_IDX(val, bit) (((val) >> (bit)) & 0x01)
#define GET_3_BITS(val, lsb) (((val) >> (lsb)) & 0x07)
#define SET_BIT_IDX(val, bit) ((val) | (0x01 << (bit))) //Not the same as stm32 hal's "SET_BIT"
#define SET_4_BITS(val, lsb) ((val) | (0x0F << (lsb)))
#define CLEAR_BIT_IDX(val, bit) ((val) & ~(1 << (bit)))
#define LO8(x) ((x) & 0xFF)
#define HI8(x) (((x)>>8) & 0xFF)
#define TOHI8(x) (((x)<<8) & 0xFF00)
#define SWAP16(x) __builtin_bswap16(x) //use built-in over DIY: (HI8(x)|TOHI8(x))
/** Convert the integer n (representing a nybble) to a single uppercase hex digit */
#define NYBBLE_TO_HEX(n) ((n) < 10 ? '0' + (n) : 'A' + (n) - 10)
/** Do the opposite of NYBBLE_TO_HEX */
#define HEX_TO_NYBBLE(n) ((n) <= '9' ? (n) - '0' : 10 + (n) - 'A')
/** Useful for ignoring unused function parameters, eg. void foo(UNUSED(int notNeeded)) */
#define UNUSED_PARAM(x) x __attribute__ ((unused))
/** Get the number of elements in a statically defined array */
#define NUM_ELEMS(x) (sizeof(x) / sizeof((x)[0]))
/** Return the largest/smallest of two numbers */
#define MIN( a, b ) ( (a) < (b) ? (a) : (b) )
#define MAX( a, b ) ( (a) > (b) ? (a) : (b) )
/** Return absolute value */
#define ABS(x) ((x)<0 ? -(x) : (x))
/** Return absolute difference. Works with unsigned values. */
#define ABS_DIFF(x,y) ((x)>(y) ? (x)-(y) : (y)-(x))
/**
* Get n rounded up to a multiple of m. Eg ROUND_UP(12,5) = 15, ROUND_UP(4,4) = 4.
* Only intended for positive integers.
* \see http://stackoverflow.com/questions/3407012/
*/
#define ROUND_UP(n,m) ( (n)==0 ? (n) : ( ((n)+(m)-1) - (((n)-1)%(m)) ) )
/**
* Get n rounded down to a multiple of m. Eg ROUND_DOWN(12,5) = 10, ROUND_DOWN(4,4) = 4, ROUND_DOWN(-1,4) = -4.
* Works with negative integers for the first argument.
* \see https://gist.github.com/aslakhellesoy/1134482
*/
#define ROUND_DOWN(n,m) ( (n) >= 0 ? ((n) / (m)) * (m) : (((n) - (m) + 1) / (m)) * (m) )
/**
* Perform integer division n/d but round to nearest integer instead of truncating by adding d/2 first.
* Only intended for positive integers. See \ref DIV_ROUND_INT for signed integers support.
* \see http://stackoverflow.com/questions/2422712/c-rounding-integer-division-instead-of-truncating
*/
#define DIV_ROUND(n,d) ((((n) + (d)/2)/(d)))
/**
* Perform integer division n/d but round to nearest integer instead of truncating.
* Works with positive or negative integers.
* \see http://stackoverflow.com/questions/2422712/c-rounding-integer-division-instead-of-truncating
*/
#define DIV_ROUND_INT(n,d) ((((n) < 0) ^ ((d) < 0)) ? (((n) - (d)/2)/(d)) : (((n) + (d)/2)/(d)))
/**
* Return -1, 0 or 1 depending on whether x is negative, zero or positive.
* \see http://stackoverflow.com/questions/1903954/is-there-a-standard-sign-function-signum-sgn-in-c-c
*/
#define SIGN(x) ((x > 0) - (x < 0))
/** Return true if x is not zero and is an integer power of 2 */
#define IS_POWER_OF_TWO(x) (x && (!(x & (x - 1))))
/**
* Get the number of wordSize words required to wholly contain numBytes.
* Eg. NUM_WORDS_FOR_BYTES(12,5) = 3, NUM_WORDS_FOR_BYTES(4,4) = 1.
* Only intended for positive integers. Could do with a better name.
*/
#define NUM_WORDS_FOR_BYTES(numBytes, wordSize) (((numBytes)+(wordSize)-1)/(wordSize))
/** Get the sizeof a member of a struct type. */
#define SIZEOF_MEMBER(type, member) sizeof(((type *)0)->member)
/** Turn a define into a string literal by wrapping it in double quotes */
#define STRINGIFY(s) STRINGIFY_HELPER(s)
#define STRINGIFY_HELPER(s) #s /* two macro levels required, as per https://gcc.gnu.org/onlinedocs/cpp/Stringification.html */
/** Turn a define into a char rvalue by dereferencing a string */
#define CHARIFY(c) CHARIFY_HELPER(c)
#define CHARIFY_HELPER(c) *#c /* Ref: https://stackoverflow.com/a/79187995/3697870 */
/** Simple linear interpolation macro, without the overhead of std::lerp
* Returns the linear interpolation of x on the line (x0,y0) to (x1,y1) */
#define LERP(x, x0, x1, y0, y1) (y0 + ((y1-y0)/(x1-x0)) * (x-x0))
/** Stop the current task. Interrupts can still fire so context switches can still occur. */
#define HALT() while(1) __asm volatile( "NOP" );
/**
* Allow macro names to be overloaded with different numbers of arguments.
* Ref: https://stackoverflow.com/a/11763277/3697870
*
* To overload a macro with two instances, one with one argument and one with two, use:
* #MY_MACRO(...) OVERLOAD_MACRO2(__VA_ARGS__, MY_MACRO2, MY_MACRO1)(__VA_ARGS__)
* then MY_MACRO1() will handle calls to MY_MACRO() with a single argument
* and MY_MACRO2() will handle calls to MY_MACRO() with two arguments.
*
* To overload a macro with three instances (one, two and three arguments), use OVERLOAD_MACRO3.
*/
#define OVERLOAD_MACRO2(_1, _2, macro, ...) macro
#define OVERLOAD_MACRO3(_1, _2, _3, macro, ...) ma
#include "config/FreeRTOSConfig.h"
/**
* All interrupts that we enable should have priority >= configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
* so we are free to call *FromISR FreeRTOS functions from the ISR. See the "Inverse Relationship Between
* Numeric Priority Value and the Logical Priority Setting" section of
* http://www.freertos.org/RTOS-Cortex-M3-M4.html for details.
*/
#define INTERRUPT_PRIORITY configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
/**
* This is a replacement for the retarded irq_register_handler in interrupt_sam_nvic.h. The documentation
* for irq_register_handler is so bad as to be considered dangerous. This version does what it says.
* In SAM4E, IRQ handlers are already assigned in the vector table (see startup_sam4e.c) so all one needs
* to do to enable an interrupt is call this define and provide a definition of the corresponding
* handler (see exceptions.c). irq_num must be of type IRQn_Type (see sam4e16e.h).
*/
#define irq_enable(irq_num) \
do { \
NVIC_ClearPendingIRQ(irq_num); \
NVIC_SetPriority(irq_num, INTERRUPT_PRIORITY); \
NVIC_EnableIRQ(irq_num); \
}while(0) /* user to supply ';' */
//=====================
// DEBUG
//=====================
#ifdef _DEBUG
//In debug mode, try to print out the error message to stdout. Note printf is not thread safe, isn't guaranteed
//to be available and occasionally hard faults, but generally if you have a terminal session attached to the
//USB port the error message is useful debugging information. See serial_comms for the stdio setup.
#include <stdio_serial.h>
#define DEBUG_PRINT(...) printf(__VA_ARGS__)
#else
#define DEBUG_PRINT(...)
#endif
//=====================
// PROFILING
//=====================
/** #include this at top of the file of a function you wish to profile. */
#define PROFILE_INCLUDE <stdio.h>
/** Place at the start of a function you wish to profile. */
#define PROFILE_START \
TickType_t endTick, startTick = xTaskGetTickCount();
/** Place any number of times between \ref PROFILE_START and \ref PROFILE_END to get a profiling update. */
#define PROFILE_PROGRESS \
endTick = xTaskGetTickCount(); \
DEBUG_PRINT("time = %lu, ", endTick-startTick); \
startTick = xTaskGetTickCount();
/** Place at the end of a function you wish to profile. */
#define PROFILE_END \
endTick = xTaskGetTickCount(); \
DEBUG_PRINT("time = %lu.\n", endTick-startTick);
//=====================
// CALIBRATION
//=====================
/**
* Uncomment to enable calibration mode, where various calibration values are printed to the console,
* displayed on the home screen and configurable in the settings screen. See TT-PRO-0001 Assembly
* Instructions for details.
*
* Note that the preferred way to enable CALIBRATION_MODE is to build using the "Calibration" configuration.
* It is the same as "Debug", but defines CALIBRATION_MODE as well. It was created so we can have a normal
* Debug build without having to manually edit this file back and forth.
*/
//#define CALIBRATION_MODE
#ifdef CALIBRATION_MODE
#ifdef NDEBUG
#error Cannot use calibration mode in Release build because Release build requires a Flash erase to overwrite, which deletes the calibration.
#endif
/** In cal mode, start with a fresh set of settings on boot so we don't launch with invalid values. */
#define RESET_TO_FACTORY_DEFAULTS
#endif
#endif //__COMMON_H__
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment