嵌入式常用宏定义

一、什么是宏?

预处理阶段的文本替换机制

宏定义的三种形式

① 对象式宏:简单的常量定义和文本替换

② 函数式宏:接收参数的可复用代码片段

③ 条件编译宏:根据条件编译不同代码

基本语法示例

cpp 复制代码
#define PI 3.14159
#define MAX(a, b)((a)>(b)?(a):(b))
#define SWAP(x,y) do {          /
    int temp =x;                /
    x = y;                      /
    y = temp;                   /
}while(0)

#ifdef DEBUG
    #define DPRINT(x)printf(x)
#else
    #define DPRINT(x)
#endif

核心特点:

  1. 编译前处理:宏在预处理阶段展开,不占用运行时开销
  2. 文本替换:直接进行字符串替换,无函数调用开销
  3. 灵活性高:可以生成各种代码变体和优化方案

二、寄存器操作宏

嵌入式开发中最常见的宏类型:寄存器读写宏

cpp 复制代码
/*读寄存器*/
#define READ_REG(reg) (*(volatile uint32_t*)(reg))
/*写寄存器*/
#define WRITE_REG(reg,val)     \
    (*(volatile uint32_t*)(reg)=(val))
/*实际使用 */
uint32_t data= READ_REG(GPIO_IDR_ADDR);
WRITE_REG(GPIO_ODR_ADDR, 0x0001);

三、位操作宏

cpp 复制代码
/*位操作*/
#define SET_BIT(reg, bit)    \
    ( (reg) |= (1<<(bit)) )   //1左移bit位,也就是bit位置1
#define CR_BIT(reg, bit)    \
    ( (reg) &= ~(1 <<(bit)) )  //1左移bit位后取反,也就是bit位置0
#define GET_BIT(reg, bit)    \
    (( (reg)>>(bit) ) & 1)     //取bit位的值,1或0
/*实际使用 */
/*设置第5位*/
SET_BIT(GPIO_PORT, 5);

/*清除第3位 */
CLR_BIT(GPIO_PORT, 3);

/*检査第7位 */
if(GET_BIT(GPIO_PORT, 7)){
    // do something
}

关键点:

使用 volatile防止编译器优化,确保每次都读写寄存器。加括号提高安全性。

四、实用工具宏

提高编码效率的常见模式

1、常用计算宏

cpp 复制代码
/*获取数组元素个数*/
#define ARRAY_SIZE(arr)     \
    (sizeof(arr) / sizeof((arr)[0]))
/*获取结构体成员偏移量 */
#define OFFSET_OF(type, member)    \
    ((size_t)&((type *)0)->member)
/*容器向后指针获取*/
#define CONTAINER_OF(ptr, type, member)    \
    ((type *)((char *)(ptr) - OFFSET_OF(type, member)))
/*实际使用*/
int array[]={1,2,3,4,5};
int size = ARRAY SIZE(array);/* size = 5 */

2、调试和断言宏

cpp 复制代码
/*调试打印 */
#define DEBUG_PRINT(fmt, ...)    \
    printf("[%s:%d]" fmt "\n",\
    __FILE__, __LINE__, ##__VA_ARGS__)
/*断言宏 */
#define AsSERT(condition)    \
    if(!(condition)){    \
        printf("Assert failed: %s\n", #condition);    \
        while(1);\
    }
/*实际使用 */
DEBUG_PRINT("Value:%d", 42);
ASSERT(ptr != NULL);

do-while(0):确保宏在任何上下文中都是单一语句

##VA_ARGS :处理可变参数宏,支持灵活参数

#运算符:将参数转换为字符串用于调试

五、宏的优缺点

1、优势详解

  • 零运行时开销:编译时展开,无函数调用开销
  • 代码膨胀可控:只在需要的地方展开
  • 类型灵活性:不受类型限制,支持通用编程区
  • 编译时检查:条件编译实现平台特定代码
  • 可维护的常量:集中管理配置参数
  • 性能关键路径优化:内联展开替代函数调用

典型应用场景

  • 硬件寄存器操作、嵌入式系统配置、实时系统的关键路径优化、跨平台代码编译控制。

2、宏的5大劣势

  • 调试困难:展开后难以追踪,堆栈信息混乱
  • 代码膨胀:过度使用导致可执行文件变大
  • 易出错:参数未加括号引发优先级错误
  • 难以维护:宏定义散落各处,难以追踪
  • 名字冲突:全局命名空间污染,易产生冲突

优先使用 const 替代常量;使用 static inline替代简单宏;宏参数必须加括号;宏名使用大写;集中管理宏定义。

六、常用宏

cpp 复制代码
寄存器读写宏 - 这是硬件交互的基础
#define READ_REG(reg) (*(volatile uint32_t *)(reg))
#define WRITE_REG(reg, val) (*(volatile uint32_t *)(reg) = (val))

位操作宏 - 单片机控制的杀手锏
#define SET_BIT(reg, bit) ((reg) |= (1 << (bit)))
#define CLR_BIT(reg, bit) ((reg) &= ~(1 << (bit)))

数组长度宏 - 避免手动计算
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

调试打印宏 - 开发效率倍增
#define DEBUG_PRINT(fmt, ...) printf("[%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)

断言宏 - 及时发现问题
#define ASSERT(condition) if(!(condition)) { printf("Assert failed\n"); while(1); }

条件编译宏 - 跨平台开发必备
#ifdef DEBUG
    #define LOG(x) printf(x)
#else
    #define LOG(x)
#endif
相关推荐
sin_hielo18 小时前
leetcode 3432
数据结构·算法·leetcode
代码游侠18 小时前
数据结构——树
数据结构·算法
天骄t18 小时前
树与哈希:数据结构核心解析
数据结构·算法
ShineLeong18 小时前
C的第一次
数据结构·算法
咖丨喱19 小时前
【对端发送的invitation req中channel list和operating channel的operating class不对应】
数据结构·list·asp.net
傻小胖19 小时前
第3讲:BTC-数据结构-北大肖臻老师客堂笔记
数据结构
xlq2232220 小时前
23.二叉树搜索树(下)
数据结构·c++·算法
lzh2004091920 小时前
【数据结构】二叉搜索树
数据结构·算法
古月居GYH20 小时前
数据结构算法——排序算法解析
数据结构·算法·排序算法