C语言宏定义使用记录
我在STM上,想写一个利用串口打印LOG的函数trace,根据我手上两块板子的硬件设计,他们的串口不一致,但是我想把这个trace函数写成一个统一的源文件,分别放到两个不同的项目里面编译,需要做的仅仅是修改下头文件中定义的串口号而已。
于是我就想到了用宏定义来展开相关的代码。
我定义了一个串口号的宏
// 日志打印的串口号,可以取值1/2/3 #define TRACE_UART_PORT 1
接着我构思了需要动态修改的几个地方分别有:
// 串口的API函数操作需要串口号 USART* USART_Init(USARTxN, &usartInitData); USART_ClearFlag(USARTxN, USART_IT_RXNE | USART_IT_ORE); USART_ITConfig(USARTxN, USART_IT_RXNE, ENABLE); USART_Cmd(USARTxN, ENABLE); // 中断配置的操作 USART*_IRQn nvicInitData.NVIC_IRQChannel = USARTIRQxN; nvicInitData.NVIC_IRQChannelCmd = ENABLE; nvicInitData.NVIC_IRQChannelPreemptionPriority = 2; nvicInitData.NVIC_IRQChannelSubPriority = 0; NVIC_Init(&nvicInitData); // 中断服务函数 void USART*_IRQHandler(void);
这里的 USARTxN 可能的值有 USART1,USART2,USART3三者之一,USART*_IRQHandler可能是 USART1_IRQHandler,USART2_IRQHandler,USART3_IRQHandler三者之一,USARTIRQxN也可以是USART1_IRQn,USART2_IRQn,USART3_IRQn三者之一。他们的使用代码在很多地方都是一样的,只是具体的串口名称不一样,我不想用
#if defined(TRACE_UART_PORT) && (TRACE_UART_PORT == 1) // 串口一的各类初始化操作 #elif defined(TRACE_UART_PORT) && (TRACE_UART_PORT == 2) // 串口二的各类初始化操作 #elif defined(TRACE_UART_PORT) && (TRACE_UART_PORT == 3) // 串口三的各类初始化操作 #else #error "编译错误提示" #endif
这样的方式去 “复制-黏贴-修改” ,于是便想到了C语言的预处理操作。在C语言里面只有 # 和 ## 两个可以这么做,其中 # 是 字符串化,##是字符拼接。
实现的代码如下:
#if defined(TRACE_UART_PORT) #define USARTxN2(n) USART##n #define USARTxN1(n) USARTxN2(n) #define USARTxN USARTxN1(TRACE_UART_PORT) #define USARTxN_IRQHandler2(n) USART##n##_IRQHandler #define USARTxN_IRQHandler1(n) USARTxN_IRQHandler2(n) #define USARTxN_IRQHandler USARTxN_IRQHandler1(TRACE_UART_PORT) #define USARTIRQxN2(n) USART##n##_IRQn #define USARTIRQxN1(n) USARTIRQxN2(n) #define USARTIRQxN USARTIRQxN1(TRACE_UART_PORT) #endif
为什么要使用三重的宏定义呢?我只知道##是拼接的意思,于是我不断的修改尝试,最后试出来了,可惜我那是还是没搞明白到底是为什么。
后来就特地去百度找资料看,直到我看了 https://www.cnblogs.com/9sheng/archive/2011/03/22/2684247.html 中的介绍之后才顿悟。这篇文章中的这一段话十分重要:
“规则可简单总结如下:在展开当前宏函数时,如果形参有#(字符串化操作)或##(记号连接操作)则不进行宏参数的展开,否则先展开宏参数,再展开当前宏(就像先计算函数中的参数,然后调用函数一样)。”
关键就是这里了,搞明白这里才知道为什么要多重展开才能达到目的。
初始化代码如下:
extern void trace_initialize(void) { GPIO_InitTypeDef gpioInitData; USART_InitTypeDef usartInitData; NVIC_InitTypeDef nvicInitData; usartInitData.USART_BaudRate = TRACE_BAUDRATE; usartInitData.USART_WordLength = USART_WordLength_8b; usartInitData.USART_StopBits = USART_StopBits_1; usartInitData.USART_Parity = USART_Parity_No; usartInitData.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; usartInitData.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 如下GPIO初始化是针对STM32F103的,尚未在实物上验证,编译已经通过 #if defined(TRACE_UART_PORT) #if (TRACE_UART_PORT == 1) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO | RCC_APB2Periph_USART1, ENABLE); // Alternate: TX=PA9/RX=PA10, default alternate functions. // remap to TX=PB6/RX=PB7 gpioInitData.GPIO_Mode = GPIO_Mode_AF_PP; gpioInitData.GPIO_Speed = GPIO_Speed_50MHz; gpioInitData.GPIO_Pin = GPIO_Pin_9; GPIO_Init(GPIOA, &gpioInitData); gpioInitData.GPIO_Mode = GPIO_Mode_IN_FLOATING; gpioInitData.GPIO_Speed = GPIO_Speed_50MHz; gpioInitData.GPIO_Pin = GPIO_Pin_10; GPIO_Init(GPIOA, &gpioInitData); #elif (TRACE_UART_PORT == 2) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB2Periph_USART2, ENABLE); // Alternate: TX=PA2/RX=PA3. // remap to TX=PD5/RX=PD6 gpioInitData.GPIO_Mode = GPIO_Mode_AF_PP; gpioInitData.GPIO_Speed = GPIO_Speed_50MHz; gpioInitData.GPIO_Pin = GPIO_Pin_2; GPIO_Init(GPIOA, &gpioInitData); gpioInitData.GPIO_Mode = GPIO_Mode_IN_FLOATING; gpioInitData.GPIO_Speed = GPIO_Speed_50MHz; gpioInitData.GPIO_Pin = GPIO_Pin_3; GPIO_Init(GPIOA, &gpioInitData); #elif (TRACE_UART_PORT == 3) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB2Periph_USART3, ENABLE); // Alternate: TX=PB10/RX=PB11. // remap to TX=PD8/RX=PD9 // remap to TX=PC10/RX=PC11 gpioInitData.GPIO_Mode = GPIO_Mode_AF_PP; gpioInitData.GPIO_Speed = GPIO_Speed_50MHz; gpioInitData.GPIO_Pin = GPIO_Pin_10; GPIO_Init(GPIOB, &gpioInitData); gpioInitData.GPIO_Mode = GPIO_Mode_IN_FLOATING; gpioInitData.GPIO_Speed = GPIO_Speed_50MHz; gpioInitData.GPIO_Pin = GPIO_Pin_11; GPIO_Init(GPIOB, &gpioInitData); #else #error "Not supported TRACE_UART_PORT value." #endif #error "You are not define TRACE_UART_PORT." #endif nvicInitData.NVIC_IRQChannel = USARTIRQxN; nvicInitData.NVIC_IRQChannelCmd = ENABLE; //nvicInitData.NVIC_IRQChannelPreemptionPriority = 2; //nvicInitData.NVIC_IRQChannelSubPriority = 0; nvic_load_priority(&nvicInitData); // 专门的函数用来加载各类中断的优先级 NVIC_Init(&nvicInitData); USART_Init(USARTxN, &usartInitData); USART_ClearFlag(USARTxN, USART_IT_RXNE | USART_IT_ORE); USART_ITConfig(USARTxN, USART_IT_RXNE, ENABLE); //开启串口接受中断 USART_Cmd(USARTxN, ENABLE); }
参考:
https://www.cnblogs.com/9sheng/archive/2011/03/22/2684247.html
https://blog.csdn.net/huyansoft/article/details/2484297
https://www.mikeash.com/pyblog/friday-qa-2010-12-31-c-macro-tips-and-tricks.html
《完》