嵌入式常用宏定义

一、什么是宏?

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

宏定义的三种形式

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

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

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

基本语法示例

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
相关推荐
Kuo-Teng3 小时前
LeetCode 19: Remove Nth Node From End of List
java·数据结构·算法·leetcode·链表·职场和发展·list
洛_尘5 小时前
数据结构--7:排序(Sort)
java·数据结构
2401_841495645 小时前
【LeetCode刷题】找到字符串中所有字母异位词
数据结构·python·算法·leetcode·数组·滑动窗口·找到字符串中所有字母异位词
元亓亓亓7 小时前
考研408--数据结构--day2--顺序表及其增删改查
数据结构·考研·顺序表·408
dllxhcjla7 小时前
数据结构与算法 第一天
数据结构·算法
洛_尘7 小时前
数据结构--9:反射、枚举以及lambda表达式(了解即可)
java·开发语言·数据结构
njxiejing8 小时前
Python pandas基础:Series数据操作详解
数据结构·pandas
Xiaochen_128 小时前
有边数限制的最短路:Bellman-Ford 算法
c语言·数据结构·c++·程序人生·算法·学习方法·最简单的算法理解
君不见,青丝成雪14 小时前
网关整合验签
大数据·数据结构·docker·微服务·系统架构