π Home | π Embedded Systems | π§° Development Toolbox | π Training Courses | π Documents
π― As you may already know, macros in C are commonly used to replace constant numbers during preprocessing. In this article, I will recap the basic usage of macros and dive deeper into their applications in embedded development to answer the questions below:
β What is a macro in C?
#define
and performs simple text substitution.β Which compilation step are macros used in?
π‘ In this demonstration, I simulate a system tick that counts up 1000 times per second. When 500ms have elapsed, the software sends a notification to the user by turning on the on-board LEDs on the development board.
π‘ I would like to make the high-level software module compatible with 2 types of boards: the STM32F103 Blue Pill Board and the Tiva C Launchpad EK-TM4C123GXL with the help of Board Support Package (BSP).
There are some goals I would like to achieve:
π½ Source: Demonstration Without Macro
In the first implementation approach, I use constants, variables and functions to implement the software without using any macros. I have the software running as I expect but it has failed to achieve the goals.
Some problems you can figure out when first looking at the implementation:
π½ You can find the final source here: Demonstration With Macro
π¨ Development Boards
π§ Development Tools
π Here I see an opportunity to use macros to replace meaningless constants such as 4294967295U
, 0U
, 1000U
.
/* Macro for maximum value of ticks */
#define TICKS_MAX_VALUE (4294967295U)
/* Macro for reset value of ticks */
#define TICKS_RESET_VALUE (0U)
/* Macro for ticks per second */
#define TICKS_PER_SECOND (1000U)
Benefits of using macros:
β If the code doesnβt have any comments, you may not know what the exact purpose of this line of code is:
uint32_t interval = (500U) * (1000U) / (1000U);
π I replace it with meaningful code using macros, which makes it easy to understand:
/* Macro to convert milliseconds to ticks */
#define MS_TO_TICKS(ms) ((ms) * TICKS_PER_SECOND / 1000U)
uint32_t interval = MS_TO_TICKS(500U);
Benefits of using macros for expressions:
π The function Ticks_elapsed()
is quite simple, so I replace it with a function-like macro. There is no function call overhead because the code from the macro is inserted directly for faster execution.
Benefits of using function-like macros:
π I can use macros for conditional compilation with #ifdef
or #if defined
to include/exclude features from the program. The unused code is completely removed from the program.
#if defined (BOARD_STM32F103C6_BLUEPILL)
#include "bsp_stm32f103_bluepill.h"
#elif defined (BOARD_EK_TM4C123GXL)
#include "bsp_ektm4c123gxl.h"
#else
#error "Development Board is not specified"
#endif
BOARD_STM32F103C6_BLUEPILL
macro to include files and BSP functions for the Blue Pill board and exclude files and BSP functions for the EK-TM4C123GXL
board.
#define BOARD_STM32F103C6_BLUEPILL
DEBUG_ENABLED
macro to enable/disable debug code whenever needed.
#define DEBUG_ENABLED
Benefits of using macros for conditional compilation:
β Multiple inclusion happens when the file is included more than one time. The variable, constant, data type is redefined causing this error.
π To prevent the multiple inclusion error and help the compilation faster, we can also use the header guard technique.
βοΈ Example: Before including, the complier will check if bsp_stm32f103_bluepill.h
file is already included or not by __BSP_STM32F103_BLUEPILL_H__
macro. If yes, the complier will skip processing this file content.
#ifndef __BSP_STM32F103_BLUEPILL_H__
#define __BSP_STM32F103_BLUEPILL_H__
typedef enum
{
BAD = 0U,
GOOD
} BoardStatus;
void BSP_Stm32f103BluePill_turnOnBoardLedsOn(void);
#endif
Benefits of using macros for header guards:
π You can use the macro to manipulate the string as below:
#define TO_STRING(x) #x
#define CONCAT(a, b) a##b
Benefits of stringizing and token pasting macros:
βοΈ Here is a summary of some best practices you should follow when using macros.
/* Bad */
#define SQUARE(x) x * x
/* Good */
#define SQUARE(x) ((x) * (x))
#define MAX_BUFFER_SIZE 1024U
#define LED_PORT GPIOC
#define TIMEOUT_MS 5000U
#define MAX_COUNT 100U
#ifndef MY_HEADER_H
#define MY_HEADER_H
/* content here */
#endif
/* Bad - evaluates 'x' multiple times */
#define MAX(x, y) ((x) > (y) ? (x) : (y))
/* Better - use inline functions for complex logic */
static inline int max(int x, int y) {
return (x > y) ? x : y;
}
/* Bad */
#define N 10
/* Good */
#define MAX_USERS 10
/**
* @brief Convert milliseconds to system ticks
* @param ms Milliseconds to convert
* @return Number of system ticks
*/
#define MS_TO_TICKS(ms) ((ms) * TICKS_PER_SECOND / 1000U)
[!TIP] Use inline functions for complex logic instead of macros.
π My Repositories
If you have any thing would like to discuss or cooperate with me, please donβt hesitate to contact me via:
π Home