首页 > 其他分享 >C语言之不常用的预编译命令

C语言之不常用的预编译命令

时间:2022-10-14 10:55:49浏览次数:64  
标签:LED0 常用 PIN C语言 编译 warning GPIO define

C语言之不常用的预编译命令

最近在做单片机的开发,我发现一些功能需要反复使用(个人习惯),所以今天决定写一些常用的函数作为自己的库。然后发现了一个自己忽略了很久的问题,那就是怎么合理使用预编译命令来实现接口的保留。

我个人在单片机开发有个小习惯就是上电先闪一下LED灯,说明系统正常上电了,同时也是对芯片的一个简单检查吧。然后有以下两个文件:led.h和led.c

#ifndef _USER_LED_H
#define _USER_LED_H

#include "stm32f1xx_hal.h"
#include "main.h"

// 拉低点亮
#define LIGHT_UP_LOW

#ifdef LIGHT_UP_LOW
    #define GPIO_PIN_LIGHT_UP GPIO_PIN_RESET
    #define GPIO_PIN_LIGHT_DOWN GPIO_PIN_SET
#else
    #define GPIO_PIN_LIGHT_UP GPIO_PIN_SET
    #define GPIO_PIN_LIGHT_DOWN GPIO_PIN_RESET
#endif

#define LED0_LIGHTUP HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_LIGHT_UP)  // 点亮LED0
#define LED0_LIGHTDOWN HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_LIGHT_DOWN)   // 熄灭LED0
#define LED0_TOGGLE HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin)    // 切换状态

#define LED1_LIGHTUP HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_LIGHT_UP)
#define LED1_LIGHTDOWN HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_LIGHT_DOWN)
#define LED1_TOGGLE HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin)

void initLed(void);

#endif

#include "USER/LED/include/led.h"
#include "USER/tool/include/tool.h"

/**
 * @brief 
 *  板子上的三个led灯点亮一秒后关闭
 */
void initLed(void){
    LED0_LIGHTUP;
    LED1_LIGHTUP;
    SLEEP(1000);
    LED0_LIGHTDOWN;
    LED1_LIGHTDOWN;
}

在这里,用到了最基础的也是大家最常用的预编译命令:

  1. #include
  2. #define
  3. #ifndef
  4. #ifdef
  5. #else
  6. #endif

这些命令来说都是我们比较熟悉的,也足够应对大多数的情况,但我遇到了一个不好处理的情况:由于板子没有显示屏,实时数据不方便,我就日常使用串口将数据发送到Pc,通过出串口助手进行数据的检查,但是,也不是所有的板子都是有串口芯片的,有的是通过USB进行模拟,也就是stm32的usb虚拟串口来实现串口发送的功能。

由于在使用时,可能不是同样的芯片,并且可能是直接使用USART进行数据发送,也可能是USB进行模拟发送,所以我打算于预留这个接口,但时候用的时候再来实现。

于是,我在tool.h文件中,定义了函数 sendDataToPc,在源文件中进行了函数体的预留。

#ifndef _USER_TOOL_H
#define _USER_TOOL_H

#define SLEEP(n) HAL_Delay(n)

#define SEND_MSG(msg) sendDataToPc((unsigned char *)msg, strlen(msg))

void sendDataToPc(unsigned char *data, int len);
void sendDigit(char *name, long long num);

#endif

#include "USER/tool/include/tool.h"
#include <stdio.h>
#include <string.h>

#define _UNDEFINE_SEND_MSG_TO_PC 1

void sendDataToPc(unsigned char *data, int len)
{
// 如果为定义这个函数,则报错,定义后修改宏 _UNDEFINE_SEND_MSG_TO_PC 为0
#if _UNDEFINE_SEND_MSG_TO_PC
#warning "The function 'sendDataToPc' need to define"
#endif

    return;
}

void sendDigit(char *name, long long num)
{
    if (name == NULL)
    {
        return;
    }
    char msg[256] = {0};
    sprintf(msg, "Parameter\"%s\": %d / %X", name, num, num);
    SEND_MSG(msg);
}

为了不影响编译时这里的一个错误导致其他地方报了N个错误,又不希望到时候导入后忘了这里没有实现,我使用了警告。

同时呢,我设置了宏 _UNDEFINE_SEND_MSG_TO_PC 为1,在实现时就可以选择实现后修改这个宏来取消这个warning或者是注释掉这一段的代码。通过预编译命令 #if 和 #warning的组合来达到既不影响其他地方引用是编译报错,又提醒自己这里还有个函数没有现实的目的。

在代码中,我们可能不会经常用到#warnign#error,比起其他的预编译,我们使用的频率会低很多,但如果是接口设计,个人感觉在需要的位置做好处理,对于后期他人的使用和代码调试,意义重大。

但同样来说,用不好也是挺头大的。我想大家都遇到过编译后error 3个,然后修改完最后一个以为可以完事的时候,error 90个的这种情况,比如某个宏未定义,然后在几个文件中被用到就会有一大堆的error,看着都心烦。

从我个人经验来看,我们应该尽量少的去使用error,而是多使用warning,并在设计时候,注意接口的耦合性,要尽可能的解耦。比如我上述的代码中,其实串口发不出数据,并不会影响我板子控制设备。比如我的功能是控制电机正转10圈,如果我编译后电机正常工作,那么这里的warning自然可以不用理会。而如果出现了问题,我需要输出信息的时候,再去实现这个函数也是可以的。

很多人可能认为warning其实是可以不用理会的,但我觉得还是应该稍微的重视一下warning,他可能在现在没有对你的程序造成影响,但未必以后不会,我们没法保证自己的测试就测完了所有的可能性,可能在某些情况下这里的warning会是致命。

除了 #warning 和 #error,我还想说一下 #pragma这个预编译命令。

预编译命令 #pragma 是功能最多的一个预编译命令了,但很多人甚至从来没有使用过。这个命令在不同的编译器下,功能也是不一样的,比如我们想在编译时输出一个信息:

#pragma message("消息文本")

虽然我们有error和warning,但是不是这样更加合理呢?但同样,这个编译命令可能在一些编译器下就会无效。

使用VS的使用,我其实常用 #pragma once, 这个命令是说这个文件只会导入一次,优点是比使用 #ifndef + #define + #endif 要简单,缺点就是如果这个命令不生效,我们还是需要去用后者来避免头文件的多次导入。

除此之外,如果使用VS开发,有些人可能就会遇到 warning 4996,虽然是warning,但是确实不能运行,其中一个方法就是在文件中使用 #pragma warning(disable:4996) 来指定关闭一个警告,这也说明了这个命令的强大。但是,我更倾向于于直接去VS的项目属性中禁用这个警告,毕竟更加的操作简单,而且一劳永逸,同时也证明这个命令其实是有点鸡肋的。

标签:LED0,常用,PIN,C语言,编译,warning,GPIO,define
From: https://www.cnblogs.com/joke-dream/p/16790704.html

相关文章

  • python3 批量编译pyc文件
    compile.pyimportos,shutilimportcompileallimportsysimportredefcopy_to_up(path):forfinos.listdir(path):iff=='__pycache__':......
  • Java程序员必备:查看日志常用的linux命令
    前言趁周末,复习一下鸟哥的linux私房菜,看了文件内容查阅部分,做个笔记,哈哈,希望对你有帮助哦。catcat:由第一行开始显示文件所有内容参数说明​​cat[-AbEnTv]​​​​参数:......
  • 实战!工作中常用到哪些设计模式
    前言大家好,我是捡田螺的小男孩。平时我们写代码呢,多数情况都是流水线式写代码,基本就可以实现业务逻辑了。如何在写代码中找到乐趣呢,我觉得,最好的方式就是:使用设计模式优化......
  • C语言操作符大全和详解(上)
    ......
  • 常用的Jmeter参数化技巧总结,总有一个你不知道
    每天进步一点点,关注我们哦,每天分享测试技术文章本文章出自【码同学软件测试】码同学公众号:自动化软件测试,领取资料可加:magetest码同学抖音号:小码哥聊软件测试说起接口......
  • excel表格常用函数技巧大全 excel中最常用的30个函数分享
    excel中最常用的30个函数:一、数字处理1、取绝对值=ABS(数字)2、取整=INT(数字)3、四舍五入=ROUND(数字,小数位数)二、判断公式1、把公式产生的错误值显示为空公......
  • Git常用命令
    Git中的常用概念工作目录:是一个目录,用于保存仓库项目中所有的文件暂存区:是内存中的一个区域,用于临时存储项目中文件的变化Git仓库:是一个特殊的目录,保存项目中所有的......
  • 实验一 C语言开发环境使用和编程初体验
    实验任务一//实验任务一#include<stdio.h>intmain(){printf("O\n");printf("<H>\n");printf("II\n");return0;}task1_1.c//实验任......
  • 初探c语言第四天
    循环结构程序设计while语句循环变量初始化在此语句之前完成while(条件){         }条件为真,执行下面的语句。dowhile语句do{       }while(条......
  • excel常用公式
    条件查找IFNA(VLOOKUP(TBL_NAME!A1,TBL_NAME!$A$1:$C$10,3,0)&"","")vlookup()后加&“”可将空值0显示为空白ifna(vlookup(),"")可在vlookup函数显示#N/A时,自定义显示内......