嵌入式常用宏定义

一、什么是宏?

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

宏定义的三种形式

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

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

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

基本语法示例

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
相关推荐
h_a_o777oah9 小时前
【树论】树链剖分:将树形结构转化为线性结构以处理树上路径问题(洛谷 P3384 附C++代码模板)
数据结构·算法·线段树·acm·树链剖分·树论·重链剖分
HEJOO910 小时前
指针的算术运算详解
java·数据结构·算法
Coder-magician10 小时前
《代码随想录》刷题打卡day47:并查集-part01
数据结构·算法·图论
垆边人似月.10 小时前
VLAN 资源池(100~200 分档)
数据结构·算法
程序员清风11 小时前
算法与数据结构入门:复杂度、数组与链表
数据结构·算法·链表
Logic10115 小时前
C语言/数据结构排序题解:三个数的最大乘积——排序后比较两种候选情况
c语言·数据结构·数组·排序·时间复杂度·算法题·负数处理
殷色玫瑰16 小时前
C++ string类详解:常用接口、字符串操作与模拟实现
java·linux·c语言·开发语言·数据结构·c++
写后端的胖头鱼17 小时前
时间复杂度 & 空间复杂度
java·数据结构·算法·时间复杂度·空间复杂度
多弗朗皮卡丘17 小时前
数据结构番外:环形队列
数据结构·c
50万马克的面包18 小时前
顺序表和链表_精华知识点_CSDN博客
数据结构·链表