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);
}

複製代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章