相当于嵌入式C/代码组织中的接口
Equivalent to Interfaces in Embedded C / Code organization
我正在 EFM32 Cortex M3 处理器上开发嵌入式 C 代码,几个月后代码开始变得疯狂......我的意思是我们改变了硬件所以我们得到了不同的版本,我们改变了一些组件,移动了一些 IOs,在启动时有不同的论文状态...
所以我想稍微清理一下:
我有一些非常大的文件是这样组织的:
/*============================================================================*/
/* File : BSP_gpio.h */
/* Processor : EFM32GG980F1024 */
/*----------------------------------------------------------------------------*/
/* Description : gpio driver */
/*----------------------------------------------------------------------------*/
#ifndef BSP_GPIO_H
#define BSP_GPIO_H
#ifdef EXTERN
#undef EXTERN
#endif
#ifdef BSP_GPIO_C
#define EXTERN
#else
#define EXTERN extern
#endif
typedef enum
{
GPIO_INIT = 0,
GPIO_WAKEUP = 1,
GPIO_SLEEP = 2,
} GPIO_MODE;
/* Definition / conts */
#define PIO_PIN_HIGH 1
#define PIO_PIN_LOW 0
#ifdef HW_v1
... hundreds of lines...
#elif defined HW_v2
... hundreds of lines...
#endif
#endif
我尝试将不同版本的文件分开并尝试这样的操作:
#ifdef HW_v1
#include "BSP_gpio_HW1.h"
#elif defined HW_v2
#include "BSP_gpio_HW2.h"
#endif
每个 "subfile" 具有相同类型的 header(直到枚举)。目的是在每个其他“.c”文件中包含 "BSP_gpio.h",它会自动包含与所用硬件相对应的文件。
第一个问题是编译取决于我包含子文件的位置。例如,我有一个函数“void BSP_GPIO_set(GPIO_MODE mode)”,它使用枚举 "GPIO_MODE" 并且在两个硬件版本中不同(因为 IOs 的状态在两个硬件上是不一样的)。如果我在声明之前包含子文件,它不知道类型 "GPIO_MODE" 并产生编译错误,即使我在子文件中 #include "BSP_gpio.h" 也是如此。
所以我只是把它放在文件的末尾并且它有效,即使我真的不喜欢它......
当我有一个声明为 extern 的变量时出现第二个问题,我想在子文件和其他 C 文件中使用它。可以说我把这一行放在“#ifdef HW_v1”之前:
EXTERN int numberOfToggles;
"EXTERN" 这个词在我的 "BSP_gpio.c" 文件中什么都没有,因为我在它的开头定义了 BSP_GPIO_C,并且是关键字 extern 在我包含 "BSP_gpio.h" 的所有其他文件中。当我构建我的项目时,它可以编译,但我有一个链接器错误:"Duplicate definitions for numberOfToggles in BSP_gpio.o and BSP_gpio_HW2.o",我找不到解决方案。
如果有人对此有合适的解决方案,我准备更改我的项目的架构!
The first problem is that the compilation depends on where I include the subfile. For example, I have a function "void BSP_GPIO_set(GPIO_MODE mode)" which uses the enum "GPIO_MODE" and is different in the two hardware versions [...]. If I include the subfile before the declaration, it doesn't know the type "GPIO_MODE" and makes a compilation error, even if I #include "BSP_gpio.h" in the subfiles. So I just put this at the end of the file and it works, even if I don't really like it...
我不确定 "The first problem is [...]" 与 "it works" 的搭配如何。我猜你的抱怨是关于编码风格从你的重构中掉下来的。
无论如何,是的,C 对声明出现的顺序很敏感,因此放置 #include
指令的位置很重要。就个人而言,我更喜欢采用这样的方法,即每个 header 文件 H
应该 #include
所有其他 header 提供 H
需要和不需要的宏和声明本身提供。有了防止多重包含的标准防护措施,可以缓解许多(但不是全部)围绕 header 顺序和位置的问题。
可能需要从 header 中分解出更多或更精细的部分,才能将 #include
指令仅放在每个部分的顶部。
The second problem appears when I have a variable declared as extern which I want to use in both subfiles and in others c files. [...] When I build my project, it compiles but I have a linker error : "Duplicate definitions for numberOfToggles in BSP_gpio.o and BSP_gpio_HW2.o" and I can't find a solution for that.
一个全局变量可以在任意数量的编译单元中有任意数量的兼容声明,但它必须恰好有一个 定义。 header(s) 中的 extern
声明完全没问题,只要它们没有全局变量的初始值设定项即可。每个全局变量都应该在一个 .c
源文件中有一个 non-extern 声明作为定义。在定义中使用初始值设定项的奖励积分。
这个问题似乎 100% 与缺乏版本控制有关,而不是真正与编程有关。
不必为编译器开关而烦恼,只需更改代码,使其适用于最新版本的硬件。在进行过程中,为每个硬件修订更改在版本控制系统中创建一个 "tag"。如果您需要使用旧硬件,只需返回到您当时使用的版本即可。
或者创建一个当前最新文件的分支,从旧标签中取出 "hardware routing" 文件并将其转储到最新文件中。
在我看来,您似乎没有明确区分接口和实现。是否有充分的理由在头文件中包含硬件依赖项?使用 GPIO
驱动程序而不是功能特定的驱动程序是否有充分的理由?
在我看来,硬件依赖项应该隐藏在实现中(即 .c
文件),而不是通过头文件 public。例如,用于 LED 的 Signalisation
驱动程序。在那里,头文件可能看起来像这样:
#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
如果您采用这种方法,您几乎只需要更改驱动程序代码的实现,而不需要更改不同硬件版本之间的头文件。
除此之外,我同意 and 的回答。
根据您的回答,我修改了我的代码,使其更清晰。我现在只有一个 BSP_gpio.h 文件,其中包含我的定义和所有原型:
/*============================================================================*/
/* File : BSP_gpio.h */
/* Processor : EFM32GG980F1024 */
/*----------------------------------------------------------------------------*/
/* Description : gpio driver */
/*----------------------------------------------------------------------------*/
#ifndef BSP_GPIO_H
#define BSP_GPIO_H
#ifdef EXTERN
#undef EXTERN
#endif
#ifdef BSP_GPIO_C
#define EXTERN
#else
#define EXTERN extern
#endif
typedef enum
{
GPIO_INIT = 0,
GPIO_WAKEUP = 1,
GPIO_SLEEP = 2,
} GPIO_MODE;
/* Definition / conts */
#define PIO_PIN_HIGH 1
#define PIO_PIN_LOW 0
/* Variables */
EXTERN UINT8 numberOfToggles;
/* Functions */
void BSP_GPIO_set_interupt(UINT8 port, UINT8 pin, BOOL falling);
void BSP_GPIO_set(GPIO_MODE mode)
#endif
我现在有三个 .c 文件,BSP_gpio.c 我在其中实现了两种硬件通用的所有内容:
/*============================================================================*/
/* 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
}
和 BSP_gpio_HW1(/2).c 文件,我在其中实现了被预处理器指令包围的 void BSP_GPIO_set(GPIO_MODE mode)
函数,因此它只实现了一次:
#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
所以这回答了我的第一个 remark/question 我对我的代码不是很满意,但我仍然有重复定义问题,我不明白,因为 BSP_GPIO_C
只定义在BSP_gpio.c 文件而不是 BSP_gpio_HW1(/2).c 文件。
有什么想法吗?
再次感谢您的帮助!
我正在 EFM32 Cortex M3 处理器上开发嵌入式 C 代码,几个月后代码开始变得疯狂......我的意思是我们改变了硬件所以我们得到了不同的版本,我们改变了一些组件,移动了一些 IOs,在启动时有不同的论文状态...
所以我想稍微清理一下:
我有一些非常大的文件是这样组织的:
/*============================================================================*/
/* File : BSP_gpio.h */
/* Processor : EFM32GG980F1024 */
/*----------------------------------------------------------------------------*/
/* Description : gpio driver */
/*----------------------------------------------------------------------------*/
#ifndef BSP_GPIO_H
#define BSP_GPIO_H
#ifdef EXTERN
#undef EXTERN
#endif
#ifdef BSP_GPIO_C
#define EXTERN
#else
#define EXTERN extern
#endif
typedef enum
{
GPIO_INIT = 0,
GPIO_WAKEUP = 1,
GPIO_SLEEP = 2,
} GPIO_MODE;
/* Definition / conts */
#define PIO_PIN_HIGH 1
#define PIO_PIN_LOW 0
#ifdef HW_v1
... hundreds of lines...
#elif defined HW_v2
... hundreds of lines...
#endif
#endif
我尝试将不同版本的文件分开并尝试这样的操作:
#ifdef HW_v1
#include "BSP_gpio_HW1.h"
#elif defined HW_v2
#include "BSP_gpio_HW2.h"
#endif
每个 "subfile" 具有相同类型的 header(直到枚举)。目的是在每个其他“.c”文件中包含 "BSP_gpio.h",它会自动包含与所用硬件相对应的文件。
第一个问题是编译取决于我包含子文件的位置。例如,我有一个函数“void BSP_GPIO_set(GPIO_MODE mode)”,它使用枚举 "GPIO_MODE" 并且在两个硬件版本中不同(因为 IOs 的状态在两个硬件上是不一样的)。如果我在声明之前包含子文件,它不知道类型 "GPIO_MODE" 并产生编译错误,即使我在子文件中 #include "BSP_gpio.h" 也是如此。 所以我只是把它放在文件的末尾并且它有效,即使我真的不喜欢它......
当我有一个声明为 extern 的变量时出现第二个问题,我想在子文件和其他 C 文件中使用它。可以说我把这一行放在“#ifdef HW_v1”之前:
EXTERN int numberOfToggles;
"EXTERN" 这个词在我的 "BSP_gpio.c" 文件中什么都没有,因为我在它的开头定义了 BSP_GPIO_C,并且是关键字 extern 在我包含 "BSP_gpio.h" 的所有其他文件中。当我构建我的项目时,它可以编译,但我有一个链接器错误:"Duplicate definitions for numberOfToggles in BSP_gpio.o and BSP_gpio_HW2.o",我找不到解决方案。
如果有人对此有合适的解决方案,我准备更改我的项目的架构!
The first problem is that the compilation depends on where I include the subfile. For example, I have a function "void BSP_GPIO_set(GPIO_MODE mode)" which uses the enum "GPIO_MODE" and is different in the two hardware versions [...]. If I include the subfile before the declaration, it doesn't know the type "GPIO_MODE" and makes a compilation error, even if I #include "BSP_gpio.h" in the subfiles. So I just put this at the end of the file and it works, even if I don't really like it...
我不确定 "The first problem is [...]" 与 "it works" 的搭配如何。我猜你的抱怨是关于编码风格从你的重构中掉下来的。
无论如何,是的,C 对声明出现的顺序很敏感,因此放置 #include
指令的位置很重要。就个人而言,我更喜欢采用这样的方法,即每个 header 文件 H
应该 #include
所有其他 header 提供 H
需要和不需要的宏和声明本身提供。有了防止多重包含的标准防护措施,可以缓解许多(但不是全部)围绕 header 顺序和位置的问题。
可能需要从 header 中分解出更多或更精细的部分,才能将 #include
指令仅放在每个部分的顶部。
The second problem appears when I have a variable declared as extern which I want to use in both subfiles and in others c files. [...] When I build my project, it compiles but I have a linker error : "Duplicate definitions for numberOfToggles in BSP_gpio.o and BSP_gpio_HW2.o" and I can't find a solution for that.
一个全局变量可以在任意数量的编译单元中有任意数量的兼容声明,但它必须恰好有一个 定义。 header(s) 中的 extern
声明完全没问题,只要它们没有全局变量的初始值设定项即可。每个全局变量都应该在一个 .c
源文件中有一个 non-extern 声明作为定义。在定义中使用初始值设定项的奖励积分。
这个问题似乎 100% 与缺乏版本控制有关,而不是真正与编程有关。
不必为编译器开关而烦恼,只需更改代码,使其适用于最新版本的硬件。在进行过程中,为每个硬件修订更改在版本控制系统中创建一个 "tag"。如果您需要使用旧硬件,只需返回到您当时使用的版本即可。
或者创建一个当前最新文件的分支,从旧标签中取出 "hardware routing" 文件并将其转储到最新文件中。
在我看来,您似乎没有明确区分接口和实现。是否有充分的理由在头文件中包含硬件依赖项?使用 GPIO
驱动程序而不是功能特定的驱动程序是否有充分的理由?
在我看来,硬件依赖项应该隐藏在实现中(即 .c
文件),而不是通过头文件 public。例如,用于 LED 的 Signalisation
驱动程序。在那里,头文件可能看起来像这样:
#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 文件,其中包含我的定义和所有原型:
/*============================================================================*/
/* File : BSP_gpio.h */
/* Processor : EFM32GG980F1024 */
/*----------------------------------------------------------------------------*/
/* Description : gpio driver */
/*----------------------------------------------------------------------------*/
#ifndef BSP_GPIO_H
#define BSP_GPIO_H
#ifdef EXTERN
#undef EXTERN
#endif
#ifdef BSP_GPIO_C
#define EXTERN
#else
#define EXTERN extern
#endif
typedef enum
{
GPIO_INIT = 0,
GPIO_WAKEUP = 1,
GPIO_SLEEP = 2,
} GPIO_MODE;
/* Definition / conts */
#define PIO_PIN_HIGH 1
#define PIO_PIN_LOW 0
/* Variables */
EXTERN UINT8 numberOfToggles;
/* Functions */
void BSP_GPIO_set_interupt(UINT8 port, UINT8 pin, BOOL falling);
void BSP_GPIO_set(GPIO_MODE mode)
#endif
我现在有三个 .c 文件,BSP_gpio.c 我在其中实现了两种硬件通用的所有内容:
/*============================================================================*/
/* 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
}
和 BSP_gpio_HW1(/2).c 文件,我在其中实现了被预处理器指令包围的 void BSP_GPIO_set(GPIO_MODE mode)
函数,因此它只实现了一次:
#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
所以这回答了我的第一个 remark/question 我对我的代码不是很满意,但我仍然有重复定义问题,我不明白,因为 BSP_GPIO_C
只定义在BSP_gpio.c 文件而不是 BSP_gpio_HW1(/2).c 文件。
有什么想法吗? 再次感谢您的帮助!