zoukankan      html  css  js  c++  java
  • 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);
    }

    复制代码

  • 相关阅读:
    Linux下常用的3种软件安装方式
    解决navicate 连接mysql数据库中文乱码的问题
    Lua 遍历Linux目录下的文件夹
    ubuntu 更改源
    ubuntu 下安装配置LAMP
    简述configure、pkg-config、pkg_config_path三者的关系
    linux 下库的深入调研
    Linux下的库操作工具-nm、ar、ldd、ldconfig和ld.so
    linux命令strings
    c++隐式类型转换和explicit
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13309224.html
Copyright © 2011-2022 走看看