Using Macros in Embedded C Language Effectively

Embedded engineers commonly use macros when coding in C language and debugging. Through continuous engineering experience, I have summarized many useful macros that can be applied to most embedded C language projects, ensuring high portability and readability. Below are some commonly used macros that engineers can save for future use as needed!

1. Preventing a header file from being included multiple times
#ifndef COMDEF_H
#define COMDEF_H
// Header file content
#endif

2. Redefining some types to avoid differences in type byte sizes due to various platforms and compilers, facilitating portability.
typedef unsigned char boolean; /* Boolean value type. */
typedef unsigned long int uint32; /* Unsigned 32 bit value */
typedef unsigned short uint16; /* Unsigned 16 bit value */
typedef unsigned char uint8; /* Unsigned 8 bit value */
typedef signed long int int32; /* Signed 32 bit value */
typedef signed short int16; /* Signed 16 bit value */
typedef signed char int8; /* Signed 8 bit value */

Not recommended:
typedef unsigned char byte; /* Unsigned 8 bit value type. */
typedef unsigned short word; /* Unsigned 16 bit value type. */
typedef unsigned long dword; /* Unsigned 32 bit value type. */
typedef unsigned char uint1; /* Unsigned 8 bit value type. */
typedef unsigned short uint2; /* Unsigned 16 bit value type. */
typedef unsigned long uint4; /* Unsigned 32 bit value type. */
typedef signed char int1; /* Signed 8 bit value type. */
typedef signed short int2; /* Signed 16 bit value type. */
typedef long int int4; /* Signed 32 bit value type. */
typedef signed long sint31; /* Signed 32 bit value */
typedef signed short sint15; /* Signed 16 bit value */
typedef signed char sint7; /* Signed 8 bit value */

3. Getting a byte or word at a specified address
#define MEM_B( x ) ( *( (byte *) (x) ) )
#define MEM_W( x ) ( *( (word *) (x) ) )

4. Finding maximum and minimum values
#define MAX( x, y ) ( ((x) > (y)) ? (x) : (y) )
#define MIN( x, y ) ( ((x) < (y)) ? (x) : (y) )

5. Getting the offset of a field in a structure (struct)
#define FPOS( type, field ) \
/*lint -e545 */ ( (dword) &(( type *) 0)-> field ) /*lint +e545 */

6. Getting the size in bytes of a field in a structure
#define FSIZ( type, field ) sizeof( ((type *) 0)->field )

7. Converting two bytes to a Word in LSB format
#define FLIPW( ray ) ( (((word) (ray)[0]) * 256) + (ray)[1] )

8. Converting a Word to two bytes in LSB format
#define FLOPW( ray, val ) \
(ray)[0] = ((val) / 256); \
(ray)[1] = ((val) & 0xFF)

9. Getting the address of a variable (word width)
#define B_PTR( var ) ( (byte *) (void *) & (var) )
#define W_PTR( var ) ( (word *) (void *) & (var) )

10. Getting the high and low bytes of a word
#define WORD_LO(xxx) ((byte) ((word)(xxx) & 255))
#define WORD_HI(xxx) ((byte) ((word)(xxx) >> 8))

11. Returning the nearest multiple of 8 greater than X
#define RND8( x ) ((((x) + 7) / 8 ) * 8 )

12. Converting a letter to uppercase
#define UPCASE( c ) ( ((c) >= 'a' && (c) <= 'z') ? ((c) - 0x20) : (c) )

13. Checking if a character is a decimal digit
#define DECCHK( c ) ((c) >= '0' && (c) <= '9')

14. Checking if a character is a hexadecimal digit
#define HEXCHK( c ) ( ((c) >= '0' && (c) <= '9') ||\
((c) >= 'A' && (c) <= 'F') ||\
((c) >= 'a' && (c) <= 'f') )

15. A method to prevent overflow
#define INC_SAT( val ) (val = ((val)+1 > (val)) ? (val)+1 : (val))

16. Returning the number of elements in an array
#define ARR_SIZE( a ) ( sizeof( (a) ) / sizeof( (a[0]) ) )

17. Returning the tail value of an unsigned number n
MOD_BY_POWER_OF_TWO(X,n)=X%(2^n)
#define MOD_BY_POWER_OF_TWO( val, mod_by ) \
( (dword)(val) & (dword)((mod_by)-1) )

18. For IO space mapping in memory structure, input-output processing:
#define inp(port) (*((volatile byte *) (port)))
#define inpw(port) (*((volatile word *) (port)))
#define inpdw(port) (*((volatile dword *)(port)))
#define outp(port, val) (*((volatile byte *) (port)) = ((byte) (val)))
#define outpw(port, val) (*((volatile word *) (port)) = ((word) (val)))
#define outpdw(port, val) (*((volatile dword *) (port)) = ((dword) (val)))

19. Using some macros for debugging
The ANSI standard specifies five predefined macro names, which are:
__LINE__
__FILE__
__DATE__
__TIME__
__STDC__

If the compiler is not standard, it may only support a few of the above macro names or not support them at all. Remember that the compiler may also provide other predefined macro names.
The __LINE__ and __FILE__ macros have been discussed in the section on #line; here we discuss the remaining macro names.
The __DATE__ macro contains a string in the form of month/day/year, indicating the date when the source file was translated into code.
The time when the source code is translated into target code is contained in __TIME__, in the form of hour:minute:second.
If the implementation is standard, the __STDC__ macro contains the decimal constant 1. If it contains any other number, the implementation is non-standard.
You can define macros, for example: when _DEBUG is defined, output data information and the line number in the file.
#ifdef _DEBUG
#define DEBUGMSG(msg,date) printf(msg);printf("%d%d%d",date,__LINE__,__FILE__)
#else
#define DEBUGMSG(msg,date)
#endif

20. Macro definitions to prevent errors when used should be enclosed in parentheses. For example:
#define ADD(a,b) (a+b)

Using do{}while(0) statement to enclose multiple statements to prevent errors, for example:
#define DO(a,b) a+b;\
a++;

Usage:
if(…)
DO(a,b); // This will cause an error
else

Solution:
#define DO(a,b) do{a+b;\
a++;}while(0)

Source: Some Awesome Engineer
‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧ END ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧

Follow my WeChat public account, reply "planet" to join the knowledge planet, and get answers to your questions.

Click "Read the original" to see the details of the knowledge planet, and feel free to share, save, like, and view.

Leave a Comment