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

    参考:

    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

    《完》

    如果转载,请注明出处。https://www.cnblogs.com/ssdq/
  • 相关阅读:
    DIY 作品 及 维修 不定时更新
    置顶,博客中所有源码 github
    openwrt PandoraBox PBR-M1 极路由4 HC5962 更新固件
    使用 squid 共享 虚拟专用网至局域网
    第一次参加日语能力测试 N5
    libx264 libfdk_aac 编码 解码 详解
    开发RTSP 直播软件 H264 AAC 编码 live555 ffmpeg
    MFC Camera 摄像头预览 拍照
    http2 技术整理 nginx 搭建 http2 wireshark 抓包分析 server push 服务端推送
    plist 图集 php 批量提取 PS 一个个切
  • 原文地址:https://www.cnblogs.com/ssdq/p/13071975.html
Copyright © 2011-2022 走看看