相当于嵌入式C/代码组织中的接口

相当于嵌入式C/代码组织中的接口,c,architecture,c-preprocessor,C,Architecture,C Preprocessor,我正在EFM32 Cortex M3处理器上开发嵌入式C代码,几个月后,代码开始变得疯狂。。。我的意思是,我们改变了硬件,所以我们得到了不同的版本,在这些版本上,我们改变了一些组件,移动了一些IOs,在启动时有不同的状态 所以我想把它清理一下: 在那里,我有一些非常大的文件,组织方式如下: #ifdef HW_v1 #include "BSP_gpio_HW1.h" #elif defined HW_v2 #include "BSP_gpio_HW2.h" #endif

我正在EFM32 Cortex M3处理器上开发嵌入式C代码,几个月后,代码开始变得疯狂。。。我的意思是,我们改变了硬件,所以我们得到了不同的版本,在这些版本上,我们改变了一些组件,移动了一些IOs,在启动时有不同的状态

所以我想把它清理一下:

在那里,我有一些非常大的文件,组织方式如下:

#ifdef HW_v1

    #include "BSP_gpio_HW1.h"

#elif defined HW_v2

    #include "BSP_gpio_HW2.h"

#endif 
#ifndef DRIVER_SIGNAL_H
#define DRIVER_SIGNAL_H

typedef enum
{
  SIGNAL_STARTED,
  SIGNAL_ERROR,
  SIGNAL_WARNING,
} signal_id_t;

void signal_show(signal_id_t signal_id);

#endif
/*============================================================================*/
/*文件:BSP_gpio.h*/
/*处理器:EFM32GG980F1024*/
/*----------------------------------------------------------------------------*/
/*描述:gpio驱动程序*/
/*----------------------------------------------------------------------------*/
#ifndef BSP\U GPIO\U H
#定义BSP_GPIO_H
#ifdef外部
#未定义外部
#恩迪夫
#ifdef BSP_GPIO_C
#定义外部
#否则
#定义外部
#恩迪夫
类型定义枚举
{
GPIO_INIT=0,
GPIO_唤醒=1,
GPIO_SLEEP=2,
}GPIO_模式;
/*定义/续*/
#定义PIO_引脚_高1
#定义PIO_引脚_低0
#ifdef HW_v1
... 数百行。。。
#elif定义的硬件单元v2
... 数百行。。。
#恩迪夫
#恩迪夫
我尝试在分离的文件中分离不同的版本,并尝试如下操作:

#ifdef HW_v1

    #include "BSP_gpio_HW1.h"

#elif defined HW_v2

    #include "BSP_gpio_HW2.h"

#endif 
#ifndef DRIVER_SIGNAL_H
#define DRIVER_SIGNAL_H

typedef enum
{
  SIGNAL_STARTED,
  SIGNAL_ERROR,
  SIGNAL_WARNING,
} signal_id_t;

void signal_show(signal_id_t signal_id);

#endif
为每个“子文件”使用相同类型的头(直到枚举)。其目的是在每个其他的“.c”文件中包含“BSP_gpio.h”,并自动包含与所用硬件对应的文件

第一个问题是编译取决于我在哪里包含子文件。例如,我有一个函数“voidBSP_GPIO_集(GPIO_模式)”,它使用枚举“GPIO_模式”,并且在两个硬件版本中不同(因为两个硬件上的IOs状态不同)。如果我在声明之前包含子文件,则它不知道类型“GPIO_MODE”,并且即使我在子文件中包含“BSP_GPIO.h”,也会产生编译错误。 所以我只是把它放在文件的末尾,它可以工作,即使我真的不喜欢它

第二个问题出现在我将一个变量声明为extern时,我希望在子文件和其他C文件中使用该变量。假设我把这行放在“#ifdef HW_v1”前面:

“EXTERN”一词在我的“BSP_gpio.c”文件中不算什么,因为我在文件开头定义了BSP_gpio_c,它是包含“BSP_gpio.h”的每个其他文件中的关键字EXTERN。当我构建项目时,它会编译,但我有一个链接器错误:“BSP_gpio.o和BSP_gpio_HW2.o中numberOfToggles的定义重复”,我找不到解决方案

我准备改变我的项目架构,如果有人有一个适当的解决方案

第一个问题是编译取决于我在哪里包含子文件。例如,我有一个函数“void BSP_GPIO_set(GPIO_MODE MODE)”,它使用枚举“GPIO_MODE”,并且在两个硬件版本中不同[…]。如果我在声明之前包含子文件,则它不知道类型“GPIO_MODE”,并且即使我在子文件中包含“BSP_GPIO.h”,也会产生编译错误。所以我只是把它放在文件的末尾,它可以工作,即使我真的不喜欢它

我不确定“第一个问题是[…]”与“它起作用”是如何搭配的。我猜你的抱怨是关于编码风格,这是从你的重构中掉出来的

无论如何,是的,C对声明的出现顺序很敏感,因此将
#include
指令放在哪里很重要。就我个人而言,我更倾向于采用这样的方法,即每个头文件
H
应该
#包括提供宏和声明的所有其他头文件
H
需要而且本身不提供宏和声明。有了标准的防止多重包含的措施,这就减轻了围绕标题顺序和位置的许多(但不是全部)问题

可能需要将更多或更精细的标题分解出来,才能将
#include
指令仅放在每个标题的顶部

第二个问题出现在我将一个变量声明为extern时,我希望在子文件和其他c文件中使用该变量。[…]当我构建我的项目时,它会编译,但我有一个链接器错误:“BSP_gpio.o和BSP_gpio_HW2.o中numberOfToggles的定义重复”,我找不到解决方案


一个全局变量可以在任意数量的编译单元中有任意数量的兼容声明,但它必须只有一个定义。头中的
extern
声明非常好,只要它们没有全局变量的初始值设定项。然后,每个全局文件都应该在一个
.c
源文件中有一个非外部声明作为定义。在定义中使用初始值设定项的好处。

问题似乎100%与缺乏版本控制有关,而不是真正与编程有关

不要忘记使用编译器开关,只需更改最新版本硬件的代码即可。在进行时,在版本控制系统中为每个硬件版本更改创建一个“标记”。如果您需要使用旧硬件,只需返回到您当时使用的版本即可


或者创建当前最新文件的分支,从旧标记中获取“硬件路由”文件,并将其转储到最新文件中。

在我看来,您似乎没有明确区分接口和实现。在头文件中存在硬件依赖项是否有充分的理由?是否有充分的理由使用
GPIO
驱动程序而不是特定于功能的驱动程序

在我看来,硬件依赖应该隐藏在t中
/*============================================================================*/
/* File       : BSP_gpio.c                                                       */
/* Processor  : EFM32GG980F1024                                               */
/*----------------------------------------------------------------------------*/

#define BSP_GPIO_C

/*----------------------------------------------------------------------------*/
/*          Includes                                                          */
/*----------------------------------------------------------------------------*/
#include "BSP_type.h"
#include "BSP_gpio.h"

void BSP_GPIO_set_interupt(UINT8 port, UINT8 pin, BOOL falling)
{
    //implementation
}
#ifdef HW_v1(/2)
#include "BSP_gpio.h"

/*============================================================================*/
/*                           BSP_gpio_set                                     */
/*============================================================================*/
void BSP_GPIO_set(GPIO_MODE mode)
{
//Implementation which depends on the hardware
}
#endif