如何在C预处理器中基于宏定义的定时器名称生成关联的中断处理相关宏?
如何在C预处理器中基于宏定义的定时器名称生成关联的中断处理相关宏?
嘿,这个问题我太熟了!你遇到的坑其实是C预处理器的展开顺序规则在搞鬼——当你直接用##拼接MY_TIMER的时候,预处理器会先把MY_TIMER(也就是TC0)展开成那个地址宏((Tc *)0x42003000UL),然后再和_Handler拼接,结果就变成了一堆乱码,完全不是你想要的TC0_Handler。
别担心,咱们用两层辅助宏就能完美解决这个问题,核心思路就是先保留定时器的符号名,再做拼接,而不是先展开成地址。
解决方法:用间接拼接宏调整展开顺序
- 第一步:先定义定时器的符号名宏,专门存定时器的名字(比如
TC0),而不是直接用已经展开的地址宏:
#define MY_TIMER_NAME TC0
- 第二步:写两个辅助宏,用来调整预处理器的展开顺序:
// 直接拼接的宏(用了##,参数不会提前展开) #define CONCAT(a, b) a ## b // 间接拼接的宏(先展开参数,再传递给CONCAT) #define CONCAT_INDIRECT(a, b) CONCAT(a, b)
- 第三步:用间接拼接宏生成我们需要的中断相关宏:
// 生成中断处理函数名 #define MY_TIMER_HANDLER CONCAT_INDIRECT(MY_TIMER_NAME, _Handler) // 生成中断号宏 #define MY_TIMER_IRQ CONCAT_INDIRECT(MY_TIMER_NAME, _IRQn) // 如果需要定时器的地址,单独定义 #define MY_TIMER TC0
为什么这样有效?
这里的关键是C预处理器的展开规则:
- 当宏参数是另一个宏时,如果当前宏没有用
#或##操作符,预处理器会先展开参数,再代入宏体。 CONCAT用了##,所以它的参数不会提前展开;但CONCAT_INDIRECT没有直接用##,所以它会先把MY_TIMER_NAME展开成TC0,再传递给CONCAT进行拼接,最终得到TC0_Handler,完全符合你的需求!
完整代码示例
// 平台头文件里的固定定义(你无法修改的部分) #define TC0 ((Tc *)0x42003000UL) #define TC0_Handler TC0_IRQHandler #define TC0_IRQn 8 // 咱们自己的宏定义 #define MY_TIMER_NAME TC0 // 辅助拼接宏 #define CONCAT(a, b) a ## b #define CONCAT_INDIRECT(a, b) CONCAT(a, b) // 生成需要的所有宏 #define MY_TIMER TC0 #define MY_TIMER_HANDLER CONCAT_INDIRECT(MY_TIMER_NAME, _Handler) #define MY_TIMER_IRQ CONCAT_INDIRECT(MY_TIMER_NAME, _IRQn) // 测试:中断处理函数 void MY_TIMER_HANDLER(void) { // 这里MY_TIMER_HANDLER会被正确展开成TC0_IRQHandler MY_TIMER->COUNT = 0; // 这里MY_TIMER会展开成地址宏,正常操作定时器寄存器 }
这样一来,不管TC0的地址宏怎么定义,我们都能正确生成TC0_Handler和TC0_IRQn,同时也能正常使用MY_TIMER来操作定时器寄存器。
备注:内容来源于stack exchange,提问作者Alex Kiselev
相关产品推荐
相关产品推荐

